Sign in to follow this  
george7378

Depth in water shader

Recommended Posts

george7378    1441

Hi everyone,

I have a planar water shader which I've been using for quite a while. It follows the standard format of interpolating between refraction/reflection maps to create the overall colour and using normal maps to perturb the texture coordinates and create specular reflection. It produces decent results but I'd like to add some depth-based effects such as weighting towards a 'murky' colour for pixels in the refraction map which have a larger volume of water between them and the camera. Something a bit like this:

https://i.ytimg.com/vi/UkskiSza4p0/maxresdefault.jpg

I tried the following approach to determine the water depth for each pixel of the refraction map:

- As I'm rendering terrain under the water, the alpha channel is not going to be used (i.e. I won't have translucent terrain). So, in the terrain pixel shader, I used the alpha channel to return the screen-space depth, i.e:

// ...pixel shader code to calculate the terrain colour here...
return float4(finalTerrainColour.rgb, psInput.ScreenPos.z/psInput.ScreenPos.w);

- Then in the water shader, when I sample my refraction map containing the terrain rendered below the water, I find the difference between the alpha channel (where the terrain depth is stored) and the depth of the current pixel on the water plane, i.e:

// ...pixel shader code to sample refraction map colour...
refractionMapColour = lerp(refractionMapColour, murkyWaterColour, saturate(depthDropoffScale*(refractionMapColour.a - psInput.ScreenPos.z/psInput.ScreenPos.w)));

This didn't seem to work and produced some strange results, for example when the camera was just above the water plane, it would show the 'murky' colour only, even though the terrain was quite a way below the water (and hence refractionMapColour.a should have been much larger than the depth of the pixel on the water plane).

So, can anyone spot an obvious problem with the way I tried above, and are there any better ways of going about such depth-based effects?

Thanks for the help :)

Edited by george7378

Share this post


Link to post
Share on other sites
missionctrl    206

The thing that stands out to me is your use of the z-coordinate.  NDC coordinates range from -1 to +1, so you are probably getting negative values creeping into your lerp function.  You'll also notice that there is a range of 2 (-1 to +1), so you might also be getting some values greater than 1.

Share this post


Link to post
Share on other sites
george7378    1441

The thing that stands out to me is your use of the z-coordinate. NDC coordinates range from -1 to +1, so you are probably getting negative values creeping into your lerp function. You'll also notice that there is a range of 2 (-1 to +1), so you might also be getting some values greater than 1.

Thanks for the reply. I think for XNA the projection gives a z-coordinate from 0 to 1, with 0 being the near plane (?):

https://msdn.microsoft.com/en-us/library/bb195672.aspx

Share this post


Link to post
Share on other sites
dmatter    4844

when the camera was just above the water plane, it would show the 'murky' colour only, even though the terrain was quite a way below the water (and hence refractionMapColour.a should have been much larger than the depth of the pixel on the water plane).

 

That could be the correct/expected behaviour for your shader...

If as you say: refractionMapColour.a is much larger than the depth of the water plane.

So your shader does (largeValue - smallValue). Which could still yield a large value.

You then multiply that large value by depthDropoffScale - If this value is >1 then you'll be making the resultant value even larger!

Next you clamp it at max 1.0 using saturate(). So let's say the result was clamped at 1.0

That means your lerp call is effectively this: lerp(refractionMapColour, murkyWaterColour, 1.0)

Which will contribute 100% weight to murkyWaterColour and the result will be exactly equal to that.

Edited by dmatter

Share this post


Link to post
Share on other sites
george7378    1441

I think the issue is that the depth doesn't have enough resolution when stored in the alpha channel. The effect seems to be working when the camera is right next to the water plane, but since my far clip plane is pretty distant, it causes problems when the camera is not close to the water. Not sure of the best way to continue with this - perhaps I should calculate the 'fog factor' in the terrain shader based on actual world positions, but then I'd have to add a ray-plane intersection to calculate the distance to the water.

EDIT: I was already doing clipping when drawing the refraction map so I just decided to use the distance below the clip plane and pass that through in the alpha channel (after dividing by a scale factor to get it in a reasonable range). Produces a decent result:

ALYmL53.png

Share this post


Link to post
Share on other sites
Styves    1792

You're using Z/W. You should be using either linear depth, or depthDropoffScale should be proportionate (getting smaller with distance) to account for the non-linear distribution of your depth values.

 

I.e. use W/FarDist.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this