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;
}
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!
Thanks @NekotoArts , if you do something, please share screenshot in here!
I tried to do the exact same thing as you and failed. So grateful that you shared this!!!
Thanks @paddy-exe, if you do something, please share screenshot in here!
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 ?
@DeveloperOats thanks very much!,
i believe you cant upload images in here, but you can use any free image hosting and share the link instead like imgur
If you are wondering how to make this shader work in Godot 4 (alpha), you need to make the following changes:
@Crabsatos thanks very much! i definitely will use this advice!
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.
I found it! “rendering/quality/intended_usage/framebuffer_allocation.mobile”. change it to 3D. it worked perfect now!
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
@douwest this looks amazing!, could you share your changes probably with a github gist link for ppl interested!
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.
@fluffy you need to add some noise textures, you can check the example on github, the link is on the description
very cool shader
I merged the Godot 4 port and the waves from another shader.
All credits inside the gist.
https://gist.github.com/IsukyPohlo/5190a1fe741c58b6c06b61fac9815624
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