home

Water Shader

An early work of trying to render water from a flat plane. The plane is made transparent by mapping the scene buffer to the plane and to give the illusion of depth, the depth buffer is used to calculate the how far we can see into the water.

The depth buffer needs to converted into world space so that we can use it for our water.

fragment.glsl
float sceneDepth = readDepth(u_depthTexture, screenSpace);
float distToWater = geo.viewPos.z;
vec3 wDirDivide = geo.worldViewDir / distToWater;
vec3 wDirMultiply = wDirDivide * sceneDepth;
vec3 wDirAdd = wDirMultiply + cameraPosition;
vec3 wDirSubtract = geo.worldPos - wDirAdd;
float worldWaterDepth = length(wDirSubtract.y);

The appearance of the water can then be calculated using this depth value worldWaterDepth.

fragment.glsl
vec3 waterColor = vec3(184.95/360.0, 0.5243, 0.5627); waterColor = hsl2rgb(waterColor);
vec3 absorbColor = vec3(1.0) - waterColor;
float absorbVal = 1.0 - exp(-u_absorbStrength * worldWaterDepth);
vec3 sceneColor = sceneTexture.rgb;
vec3 subtractiveColor = absorbColor * absorbVal;
vec3 underWaterColor = sceneColor - subtractiveColor;
finalColor = mix(waterColor, underWaterColor, 0.5) * (1.0 - absorbVal);

Lighting is added by approximating normals, which is generally good enough.