Toon Water Shader

This is a port from the original unity shader by Erik Roystan Ross explained in this article, and hosted on github with Unlicense License

There are still some issues:

  • Godot 3 doesn’t has NORMAL texture from the Camera on Spatial shaders, so the normal calculations might be not correct

I encourage all people to contribute to this and make it better , code and project is hosted on github

GODOT 4 Port: github link
Github Discussions: 
link

Shader code
/**
* Ported from the original unity shader by Erik Roystan Ross
* https://roystan.net/articles/toon-water.html
* https://github.com/IronWarrior/ToonWaterShader
* Camera Depth taken from Bastiaan Olij's video on: https://www.youtube.com/watch?v=Jq3he9Lbj7M
*/

shader_type spatial;

const float SMOOTHSTEP_AA = 0.01;

uniform sampler2D surfaceNoise;
uniform sampler2D distortNoise;

uniform float beer_factor = 0.8;

uniform float foam_distance = 0.01;
uniform float foam_max_distance = 0.4;
uniform float foam_min_distance = 0.04;
uniform vec4 foam_color: hint_color  = vec4(1.0);

uniform vec2 surface_noise_tiling = vec2(1.0, 4.0);
uniform vec3 surface_noise_scroll = vec3(0.03, 0.03, 0.0);
uniform float surface_noise_cutoff: hint_range(0, 1) = 0.777;
uniform float surface_distortion_amount: hint_range(0, 1) = 0.27;

uniform vec4 _DepthGradientShallow: hint_color = vec4(0.325, 0.807, 0.971, 0.725);
uniform vec4 _DepthGradientDeep: hint_color = vec4(0.086, 0.407, 1, 0.749);
uniform float _DepthMaxDistance: hint_range(0, 1) = 1.0;
uniform float _DepthFactor = 1.0;

varying vec2 noiseUV;
varying vec2 distortUV;
varying vec3 viewNormal;

vec4 alphaBlend(vec4 top, vec4 bottom)
{
	vec3 color = (top.rgb * top.a) + (bottom.rgb * (1.0 - top.a));
	float alpha = top.a + bottom.a * (1.0 - top.a);
	
	return vec4(color, alpha);
}

void vertex() {
	viewNormal = (MODELVIEW_MATRIX * vec4(NORMAL, 0.0)).xyz;
	noiseUV = UV * surface_noise_tiling;
	distortUV = UV;
}

void fragment(){
	// https://www.youtube.com/watch?v=Jq3he9Lbj7M
	float depth = texture(DEPTH_TEXTURE, SCREEN_UV).r;
	depth = depth * 2.0 - 1.0;
	depth = PROJECTION_MATRIX[3][2] / (depth + PROJECTION_MATRIX[2][2]);
	depth = depth + VERTEX.z;
	depth = exp(-depth * beer_factor);
	depth = 1.0 - depth;
	
	// Still unsure how to get properly the NORMAL from the camera
	// this was my best attempt
	vec3 existingNormal = vec3(dFdx(depth), dFdy(depth), 0);
	
	float normalDot = clamp(dot(existingNormal.xyz, viewNormal), 0.0, 1.0);
	float foamDistance = mix(foam_max_distance, foam_min_distance, normalDot);
	
	float foamDepth = clamp(depth / foamDistance, 0.0, 1.0);
	float surfaceNoiseCutoff = foamDepth * surface_noise_cutoff;
	
	vec4 distortNoiseSample = texture(distortNoise, distortUV);
	vec2 distortAmount = (distortNoiseSample.xy * 2.0 -1.0) * surface_distortion_amount;
	
	vec2 noise_uv = vec2(
		(noiseUV.x + TIME * surface_noise_scroll.x) + distortAmount.x , 
		(noiseUV.y + TIME * surface_noise_scroll.y + distortAmount.y)
	);
	float surfaceNoiseSample = texture(surfaceNoise, noise_uv).r;
	float surfaceNoiseAmount = smoothstep(surfaceNoiseCutoff - SMOOTHSTEP_AA, surfaceNoiseCutoff + SMOOTHSTEP_AA, surfaceNoiseSample);
	
	float waterDepth = clamp(depth / _DepthMaxDistance, 0.0, 1.0) * _DepthFactor;
	vec4 waterColor = mix(_DepthGradientShallow, _DepthGradientDeep, waterDepth);

	vec4 surfaceNoiseColor = foam_color;
    surfaceNoiseColor.a *= surfaceNoiseAmount;
	vec4 color = alphaBlend(surfaceNoiseColor, waterColor);
	
	ALBEDO = color.rgb;
	ALPHA = color.a;
}
Tags
toon, water
The shader code and all code snippets in this post are under MIT license and can be used freely. Images and videos, and assets depicted in those, do not fall under this license. For more info, see our License terms.

Related shaders

Toon Style 3D Water Shader – No textures needed

fBm Toon Water

Flexible Toon Shader (Godot 4)

Subscribe
Notify of
guest

17 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
NekotoArts
3 years ago

I actually can’t believe it! I saw this on GitHub a few days ago and was really curious if it could be recreated in Godot, AND THEN I SEE THIS TODAY! Absolutely beautiful!

paddy-exe
3 years ago

I tried to do the exact same thing as you and failed. So grateful that you shared this!!!

DeveloperOats
2 years ago

Awesome Shader. Just found it and added to my game. It looks sooo nice 🙂
Thanks for the work and the code.

How do I share a screenshot ?

Crabsatos
Crabsatos
2 years ago

If you are wondering how to make this shader work in Godot 4 (alpha), you need to make the following changes:

  • replace “hint_color” with “source_color”
  • remove this line: depth = depth * 2.0 – 1.0;
  • (Vulkan depth texture has range 0 to 1 already)
Madongdong
Madongdong
2 years ago

It’s awesome! Thank you! But i can’t make it work on my iOS device. It’s might has a project setting for mobile support depthTexture, but i can’t find it.

Madongdong
Madongdong
2 years ago
Reply to  Madongdong

I found it! “rendering/quality/intended_usage/framebuffer_allocation.mobile”. change it to 3D. it worked perfect now!

douwest
douwest
2 years ago

This is great! I adapted it to Godot 4 and added some small tweaks I needed for a kind of A Short Hike style:
– Added a surface noise color, separate from the foam color, that gets blended with the waterDepth starting from where the foam ends.
– Added a parameter to control the depth at which the foam starts at full opacity.

I can share the code if you like, although the changes were very simple :D.

Example:
https://freeimage.host/i/HdjQrOl

fluffy
1 year ago

how do i use it. i put it on a meshinstance with a plane and a shader material with the script attached. but it doesn’t do anything and just stays white, I also tried a qued mesh.

Anes_pro
Anes_pro
1 year ago

very cool shader

IsukyPohlo
6 months ago

I merged the Godot 4 port and the waves from another shader.
All credits inside the gist.

https://gist.github.com/IsukyPohlo/5190a1fe741c58b6c06b61fac9815624

IsukyPohlo
6 months ago

If you want to use this shader with an orthographic you need to change some code that calculates the depth and displays the foam.

Here the fix I did
https://gist.github.com/IsukyPohlo/67da5d30bfe27f1206989995f3a85c54