# Improving water shader

i guess this is a rather simple question, i just can’t think of a decent solution:
i’m working on a new water shader. i have a reflection and a refraction texture. along with these i have another texture to which i write the distance between the water surface and the ground. i want to combine the reflection and refraction textures in the following way:

• in shallow water, the refraction should be opaque, in deep water the reflection should be predominant. this way i prevent hard edges where ground and water surface meet. i achieve this with:

gl_FragData[0].rgb = ( ReflectionWaterDepth ) + ( Refraction(1.0-WaterDepth) );

• i want to use a fresnel term, so that if you look down, you can see the ground, but if you look straight ahead, you see the reflection, like this:

vec3 Fresnel = vec3( dot(ViewVector, Normal) );
gl_FragData[0].rgb = ( Reflection*(1.0-Fresnel) ) + ( Refraction*Fresnel );

both work fine seperately, but how can i combine the two? do you have further ideas for optimization/effects? thanks

``````
vec3 Fresnel = vec3( dot(ViewVector, Normal) );
gl_FragData[0].rgb = ( Reflection*WaterDepth*(1.0-Fresnel) ) + ( Refraction*(1.0-WaterDepth)*Fresnel );
``````

would be the obvious solution, but this way the result is too dark and i get hard edges

I got nice results using the Beer-Lambert law. Check out this article, especially the “Beer’s law” section, for more details.

So you use Beer’s law along with the refracted color to calculate the transmitted color.

Next, you use the Fresnel term to blend between the transmitted and the reflected light, like you do. The way you compute the Fresnel term is not correct though. For more accurate results, use Schlick’s approximation.

You can get the reflectance at normal incidence (R_0 in Schlick’s equation) from the IORs by using R_0 = ((n1 - n2) / (n1 + n2))^2, where n1 is the IOR of material the light moves from, and n2 is the IOR of the material it moves into. I’m guessing in your case n1 would be air so 1, and n2 would be water, around 1.33.

So the final result would be something like

vec3 Absorbance = WaterColor * WaterDensity * -WaterDepth;
vec3 Transmittance = Refraction * exp(Absorbance);

float AirIOR = 1.0;
float WaterIOR = 1.33;
float R_0 = (AirIOR - WaterIOR) / (AirIOR + WaterIOR);
R_0 *= R_0;

cosTheta = dot(ViewVector, Normal);
R_theta = R_0 + (1.0 - R_0) * pow(1.0 - cosTheta, 5.0);

gl_FragData[0].rgb = R_theta * Reflection + (1.0 - R_theta) * Transmittance;

I think… I wrote this on the fly, but I think the theory is sound And obviously the above can be optimized, I wrote it for clarity.

Edit: As for WaterColor and WaterDensity, remember that the color is absorbance, so if you want it to look blue, you need to absorb red and green. I suggest you set it to something like (0.8, 0.85, 0.9) to begin with and then play with the WaterDensity variable to tune it. WaterDensity should be positive, other than that all I can say is that it depends on the scale of your object (ie the range of the WaterDepth variable).

wow thanks!

the result looks pretty cool, i just need to tweak the clip plane:

but as you can see, the edges are hard… is there a way to get rid of that? thanks!

The only way I can think of would be to read from a depth buffer and make it more transparent when the depth behind the water is small.

I think HL2 just does transparency based on the camera/water plane angle, so it sort of makes water on the shore more transparent…if you are standing above it.

The only way I can think of would be to read from a depth buffer and make it more transparent when the depth behind the water is small.

that’s what i’ve already tried, read my 1st post this does the trick:

vec3 Output = R_theta * Reflection + (1.0 - R_theta) * Transmittance;

gl_FragData[0].rgb = mix( Refraction, Output, WaterDepth );

next question is, what to do in the lighting stage? so far i’m just adding specular highlights phong-style for every light, what else could i do?

Hello,

Have you checked this out?

http://www.bonzaisoftware.com/water_tut.html

The GLSL conversion;