How to fix foam issue in 3D water shader
Asked Answered
C

11

0

Does anyone know where to fix this foam issue in this shader? I just want foam to be around the edges only.

I've tried playing with all numeral parameters but nothing fixes it. I think the foam visual is based on camera distance but I don't know which part of code should I update to fix it.

shader:

shader_type spatial;
render_mode unshaded;

uniform vec4 deep_color : source_color;
uniform vec4 shallow_color : source_color = vec4(1);
uniform float refraction_speed = 0.25;
uniform float refraction_strength = 1.0;
uniform float foam_amount = 1.0;
uniform float foam_cutoff = 1.0;
uniform vec4 foam_color : source_color = vec4(1);
uniform float displacement_strength = 0.25;
uniform float depth_distance = 1.0;
uniform vec2 movement_direction = vec2(1,0);
uniform sampler2D refraction_noise : hint_normal;
uniform sampler2D foam_noise : hint_default_black;
uniform sampler2D displacement_noise : hint_default_black;

void vertex() {
	float displacement = textureLod(
			displacement_noise, 
			UV + (TIME * movement_direction) * refraction_speed, 
			0.0).r * 2.0 - 1.0;
	
	VERTEX.y += displacement * displacement_strength;
}

void fragment() {
	vec2 uv = SCREEN_UV + refraction_strength 
			* (texture(refraction_noise, UV + (TIME * movement_direction) * refraction_speed).rg 
			* 2.0 - 1.0);
	
	float real_depth = texture(DEPTH_TEXTURE, SCREEN_UV).r * 2.0 - 1.0;
	real_depth = PROJECTION_MATRIX[3][2] / (real_depth + PROJECTION_MATRIX[2][2]) + VERTEX.z;
	
	// Get the raw linear depth from the depth texture into a  [-1, 1] range
	float depth = texture(DEPTH_TEXTURE, uv).r * 2.0 - 1.0;
	// Recreate linear depth of the intersecting geometry using projection matrix, and subtract the vertex of the sphere
	depth = PROJECTION_MATRIX[3][2] / (depth + PROJECTION_MATRIX[2][2]) + VERTEX.z;
	
	depth = max(depth, real_depth);
	
	float intersection = clamp(depth / foam_amount, 0, 1) * foam_cutoff;
	
	vec4 out_color = mix(shallow_color, deep_color, clamp((depth / depth_distance), 0, 1));
	vec4 scene_color = texture(SCREEN_TEXTURE, uv);
	out_color = mix(scene_color, out_color, out_color.a);
	
	vec3 foam = step(intersection, 
			texture(foam_noise, UV + (TIME * movement_direction) * refraction_speed).rgb) 
			* foam_color.rgb;
	
	ALBEDO = out_color.rgb + foam;
}

This shader was originally from GDQuest in Godot 3.x.

I just changed syntax to make it work for Godot 4.

Thanks

Cesaria answered 4/11, 2022 at 7:33 Comment(0)
C
0

Here is the code to get a linear depth from the depth texture.

// samples the depth from depth buffer in linear space
float getDepth(sampler2D tex, vec2 uv) {
	float depth_sample = textureLod(tex, uv, 0.0).r;
	float ndc = depth_sample * 2.0 - 1.0;
	float linear_depth = (2.0 * z_near * z_far) / (z_far + z_near - ndc * (z_far - z_near));
	linear_depth /= z_far;
	return linear_depth;
}

z_near and z_far are from the camera parameters (you can pass as uniform or hard-code them). tex is the DEPTH_TEXTURE and uv is SCREEN_UV.

Corwin answered 8/12, 2022 at 5:19 Comment(0)
R
0

I realise this is a necro but it's actually helpful for me you did this because when I was fiddling around with water shaders I was having this very same problem and it was because of how the setup was taking camera distance into account among other things this was back when I was using Unity and I was wondering how I was going to deal with it in Godot.

Rochdale answered 8/12, 2022 at 15:35 Comment(0)
C
0

Teenybopper

Thank you but can you show how to use it with the shader I posted? I just can't work it out without a working example as I don't have much exprience with shader.

I think this is a shader function so I've tried placing it above void vertex(), Replace z_near and z_far with 1.0 and 2.0, tex with DEPTH_TEXTURE..etc. and It just gave me all errors.

Cesaria answered 10/12, 2022 at 9:19 Comment(0)
C
0

You can remove these lines:

float real_depth = texture(DEPTH_TEXTURE, SCREEN_UV).r * 2.0 - 1.0;
real_depth = PROJECTION_MATRIX[3][2] / (real_depth + PROJECTION_MATRIX[2][2]) + VERTEX.z;
	
// Get the raw linear depth from the depth texture into a  [-1, 1] range
float depth = texture(DEPTH_TEXTURE, uv).r * 2.0 - 1.0;
// Recreate linear depth of the intersecting geometry using projection matrix, and subtract the vertex of the sphere
depth = PROJECTION_MATRIX[3][2] / (depth + PROJECTION_MATRIX[2][2]) + VERTEX.z;

Then call it like this:

float real_depth = getDepth(DEPTH_TEXTURE, SCREEN_UV);
float depth = getDepth(DEPTH_TEXTURE, uv);

z near and far are camera properties, you can define them at the top:

float z_near = 0.05;
float z_far = 100.0;

Those are the defaults for a Godot camera. If you didn't alter the camera, then you can leave them, it shouldn't matter much since they will be normalized.

Corwin answered 10/12, 2022 at 10:25 Comment(0)
C
0

Corwin

After using the code the foam on edge disappears and I noticed deep color fading effect is also gone too:

Here is the code I have now:


const float z_near = 0.05;
const float z_far = 100.0;

float getDepth(sampler2D tex, vec2 uv){
		float depth_sample = textureLod(tex, uv, 0.0).r;
		float ndc = depth_sample * 2.0 - 1.0;
		float linear_depth = (2.0 * z_near * z_far) / (z_far + z_near - ndc * (z_far - z_near));
		linear_depth /= z_far;
		return linear_depth;
}

void vertex() {
	//....
}

void fragment() {
	vec2 uv = SCREEN_UV + refraction_strength 
			* (texture(refraction_noise, UV + (TIME * movement_direction) * refraction_speed).rg 
			* 2.0 - 1.0);
	
	float real_depth = getDepth(DEPTH_TEXTURE, SCREEN_UV);
	float depth = getDepth(DEPTH_TEXTURE, SCREEN_UV);
	
	depth = max(depth, real_depth);
	//...

here is the original water in 3x for reference:

I've also upload my test project if anyone want to have a look.
https://github.com/j3du/godot-4-water-shader-testing

Cesaria answered 10/12, 2022 at 12:30 Comment(0)
C
0

That may not have been the issue. You should study the original code and see if you missed a step or made a mistake.

Corwin answered 10/12, 2022 at 13:43 Comment(0)
C
0

Teenybopper
Thanks for trying to help. Water is something I want to get it right so I guess I need to start learning shader coding from ground up. Along with Godot and Blender, this is gonna take forever 😆

Cesaria answered 11/12, 2022 at 3:14 Comment(0)
R
0

Cesaria I had a lot of issues with this. I worked literally months on water shaders in unity and I had a pretty dope one I was using. It's been a rough ride transferring some of those concepts to godot. I did eventually get the depth to work, and the code I used to get depth looks like the following. I still have yet to get refraction to look as well as I'd like, but it's on the right path.

	float depth = texture(DEPTH_TEXTURE, SCREEN_UV).r;
	
	
	float z = -PROJECTION_MATRIX[3][2] / (depth + PROJECTION_MATRIX[2][2]);
	float delta = -(z - VERTEX.z); // z is negative.
	
	depth = exp(-delta * 2.0);

If you shade your scene with just depth, you should get a grayscale of what it looks like. You can tweak it from there by applying a strength factor to depth.

Replace answered 22/12, 2022 at 0:17 Comment(0)
A
0

Replace If you shade your scene with just depth, you should get a grayscale of what it looks like. You can tweak it from there by applying a strength factor to depth.

Yes I always pipe a value into EMISSION when debugging something in a shader.

Acclimate answered 22/12, 2022 at 0:24 Comment(0)
C
0

Catafalque

Thanks a lot! I will give it a try. (Sorry for late reply I'm on vacation now)

I've also just found this water tutorial demo looks very nice. There is a project in there so you can study the code as well.

And if you go to that channel's videos they have tutorials on shader for noobs as well. Pretty cool channel.

Cesaria answered 9/1, 2023 at 13:6 Comment(0)
A
0

You can use depth fade(as its called in unreal engine) to get a mask at intersecting edges, for foam effect.
heres how it looks like:

shader:

shader_type spatial;
render_mode depth_prepass_alpha,depth_draw_always;

varying vec3 AbsoluteWorldPosition;

uniform float depthFadeDiv;
void vertex()
{
	AbsoluteWorldPosition = (MODEL_MATRIX * vec4(VERTEX, 1.0)).xyz;
}
void fragment() 
{
	vec3 CameraPosition = INV_VIEW_MATRIX[3].xyz;
	vec3 CameraDirection = normalize( INV_VIEW_MATRIX[2].xyz*-1.0);
	//Scene Depth
	float depth = texture(DEPTH_TEXTURE, SCREEN_UV).x;
	vec3 ndc = vec3(SCREEN_UV * 2.0 - 1.0, depth);
	vec4 view = INV_PROJECTION_MATRIX * vec4(ndc, 1.0);
	view.xyz /= view.w;
	float linear_depth = -view.z;
	//Depth Fade
	float PixelDepth = dot(CameraDirection,(AbsoluteWorldPosition - CameraPosition));
	float depthFade = linear_depth-PixelDepth;
	depthFade = clamp(depthFade/depthFadeDiv,0.0,1.0);

}
Aeroscope answered 18/1, 2023 at 6:37 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.