Skip to content

How to sample wave height for a given pixel (add buoyancy)? #6

@jonandrewdavis

Description

@jonandrewdavis

First off, thank you for providing this project. It's incredible. Looks great. Technical marvel.

I am working on adding buoyancy to a sample project following this video.

The provided code for the floating cube requires a function get_height which samples the noise map of a simple water shader at a given point (or points).

func _physics_process(delta):
	submerged = false
	for p in probes:
		var depth = water.get_height(p.global_position) - p.global_position.y 
		if depth > 0:
			submerged = true
			apply_force(Vector3.UP * float_force * gravity * depth, p.global_position - global_position)

The shader in this project is much more complex, but it should still be possible to accomplish this. I'm curious what method I can use to access height info.

My basic approach was going to be similar to the code in the video:

  • Get the shader ShaderMaterial
  • Get the sampler2DArray displacements and map_scales using get_shader_parameter( )
  • Implement a new get_height function in water.gd that would mirror the shader's vertex func by:
    • Iterating over the displacements, creating images or textures texture.get_image() or something
      • Not sure how to do this conversion in gdscript texture(displacements, vec3(UV*scales.xy, float(i))).xyz * scales.z;
    • Add up the displacement displacement += for each
    • return the pixel's height like: global_position.y + noise.get_pixelv(pixel_pos).r * height_scale;

Here's the sample:

func get_height(world_position: Vector3) -> float:
	var uv_x = wrapf(world_position.x / noise_scale + time * wave_speed, 0, 1)
	var uv_y = wrapf(world_position.z / noise_scale + time * wave_speed, 0, 1)

	var pixel_pos = Vector2(uv_x * noise.get_width(), uv_y * noise.get_height())
	return global_position.y + noise.get_pixelv(pixel_pos).r * height_scale;

For my code, I couldn't get very far. I first tried collecting the information I needed via get_shader_parameter() and found many were null.

var water_shader:ShaderMaterial  

func _ready() -> void:
	RenderingServer.global_shader_parameter_set(&'water_color', water_color.srgb_to_linear())
	RenderingServer.global_shader_parameter_set(&'foam_color', foam_color.srgb_to_linear())
	
        // Added new code to test:
	water_shader = material_override
	print('water shader', water_shader)
	
	print('get_shader_parameter, roughness: ', water_shader.get_shader_parameter('roughness'))
	print('get_shader_parameter: normal_strength: ', water_shader.get_shader_parameter('normal_strength'))
	print('get_shader_parameter: map_scales: ', water_shader.get_shader_parameter('map_scales'))
	print('-----------')
	print('RenderingServer.get_shader_parameter_list: ', '[&"displacements", &"foam_color", &"normals", &"num_cascades", &"water_color"]')
	print('-----------')
	print('get_shader_parameter: water_color: ', water_shader.get_shader_parameter('water_color'))
	print('get_shader_parameter: displacements: ', water_shader.get_shader_parameter('displacements'))
	print('get_shader_parameter: &"displacements": ', water_shader.get_shader_parameter(&"displacements"))

Which yields the output:

water shader<ShaderMaterial#-9223372007175879383>
get_shader_parameter, roughness: 0.65
get_shader_parameter: normal_strength: 1
get_shader_parameter: map_scales: [(0.011364, 0.011364, 1, 1), (0.017544, 0.017544, 0.75, 1), (0.0625, 0.0625, 0, 0.25)]
-----------
RenderingServer.get_shader_parameter_list: [&"displacements", &"foam_color", &"normals", &"num_cascades", &"water_color"]
-----------
get_shader_parameter: water_color: <null>
get_shader_parameter: displacements: <null>
get_shader_parameter: &"displacements": <null>

The displacements are null, as are all of the ones in the get_shader_parameter_list. I think that may be because global, so they could be tough to access. Ones I can get do not have global.

uniform float roughness : hint_range(0.0, 1.0) = 0.4;
uniform float normal_strength : hint_range(0.0, 1.0) = 1.0; // Global normal strength

group_uniforms cascade_data;
uniform vec4 map_scales[MAX_CASCADES];               // Scales for displacement/normal maps. Packed: [uv scale, displacement scale, normal scale]
global uniform uint num_cascades;
global uniform sampler2DArray displacements;         // Each layer represents one wave cascade.
global uniform sampler2DArray normals : hint_normal; // Each layer represents one wave cascade.

roughness is available but water_color is not. I can also get map_scales which seems useful for what I want to do eventually.

So far, I am stuck trying to figure out how to get displacements. Even if I could access it, it's probably likely that I'd have trouble unpacking it.

Next, I tried to access the displacement_map created in water.gd, hoping it might be passed in by reference (unlikely, but possible) and be updated with the water shader and I could query for a pixel's height on those images by sampling it.

var displacement_maps := Texture2DArrayRD.new()
var normal_maps := Texture2DArrayRD.new()

Can count them, but I tried to see if I could get an image according to the Texture2DArrayRD docs:

displacement_maps.get_layer_data(0)

I got:

E 0:00:01:0221   water.gd:91 @ _ready(): Texture requires the `RenderingDevice.TEXTURE_USAGE_CAN_COPY_FROM_BIT` to be set to 

Which of course led me into res://assets/render_context.gd, which is likely not something I'm able to change.

Several open questions:

  • Is this approach fundamentally flawed?
  • Is there a way to expose a more simplistic deformed noise map that's "roughly" in sync with the cascades?
    • Maybe also provide a new noise map that can be updated with wave height and queried easily by buoyant objects

So, if anyone has suggestions on how best to add basic bouncy please let me know. Happy to take this discussion elsewhere if there's a better place for it. Thanks again for the project.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions