Skip to main content
gamedev.net
🔒 Locked

Need some tips on improving water shader

Started by Telanor May 20, 2013 at 10:00 PM 9 replies 3.1k views
Original Post
Telanor
Telanor

I'm working on tweaking our water shader to get a nicer look out of it and there are a couple of issues I'm not sure how to solve. The main one is that the colorization, while nice during the day, makes the water look lit up at night and is very unnatural:

[attachment=15824:RuinValor 2013-05-19 20-57-37-99.png]

All I'm doing is lerping between the sampled refraction color and a water color parameter. How can I make it so it isn't "creating light"?

Another issue I'm having is the specular. Works during the day, creates black spots at night. Running through the shader debugger, I found it was returning QNANs, but I can't understand why, I have an if-statement that should return 0 specular for when the dot product returns a negative:


float specular = dot(normalize(reflectionVector), -eyeVector);
float3 specColor = 0;

if(specular > 0)
{
	specular = pow(specular, SpecPower) * 5;
	
	if(specular > 0) //This doesnt help either
		specColor = normalize(SpecColor.rgb) * specular;
}


One last issue I'm working on is making the water color strength vary based on the depth of the water. I haven't been able to find any documentation online about this so I've just sort of been making something up and playing around with it to see what works well. What I've done so far is read the terrain depth buffer sampled straight along the view ray, subtracted the depth from the camera to the surface, and then used that in an exponential fog equation. It works pretty well but seems a bit strange when shallow water becomes very foggy when viewed at a shallow angle. Any advice for this?

phil_t
phil_t


All I'm doing is lerping between the sampled refraction color and a water color parameter. How can I make it so it isn't "creating light"?

What are you using to light your terrain for day vs night? Seems like, at least as a start, you should just be able to multiply your water value by the incident light on a flat surface - same as you would do for flat terrain.


Another issue I'm having is the specular. Works during the day, creates black spots at night. Running through the shader debugger, I found it was returning QNANs, but I can't understand why, I have an if-statement that should return 0 specular for when the dot product returns a negative:
float specular = dot(normalize(reflectionVector), -eyeVector);
float3 specColor = 0;

if(specular > 0)
{
specular = pow(specular, SpecPower) * 5;

if(specular > 0) //This doesnt help either
specColor = normalize(SpecColor.rgb) * specular;
}

Shaders generally execute all code paths, so that may be the source of your problem. An easy solution would be to get rid of your if statements and just use saturate to clamp the specular value between 0 and 1:

float specular = saturate(dot(normalize(reflectionVector), -eyeVector));

Telanor
Telanor
Changing the if statement to a saturate made the water turn entirely black at night. I'll give the lighting thing a try and see how that works out.
unbird
unbird
As for the shallow angle: How do you lerp ? Do you use the Fresnel term ?. At shallow angles water reflects almost perfectly.
Telanor
Telanor
Ok, so a quick test of the lighting idea. I just multiplied the dot(normal, lightVector) with the final color. It works pretty well

[attachment=15825:RuinValor 2013-05-20 18-40-16-54.png]

It's a little too dark too soon though.

unbird: I am using fresnel. The reflections weren't working right before though, I've fixed them now and the shallow angle fog seems to be a lot less of a problem.
Telanor
Telanor
Ok, two more issues. The lighting equation is causing the backside of waves to turn black:

[attachment=15828:RuinValor 2013-05-20 20-44-29-97.png]

Also, this is something that's been around for a while. One side of the wave (I'm guessing it's the back) is getting a strong reflection while the other side has a strong refraction. It's creating a kind of spotted/streak effect near the viewer where the water will be mostly transparent with spots of strong blue (from the sky) mixed in. This seems pretty unnatural to me. Any suggestions?

[attachment=15829:RuinValor-2013-05-20-20-45-20-21.jpg]

And again with the water set to an orange color (in this case there are also some strong orange spots):

[attachment=15830:RuinValor 2013-05-20 20-58-48-77.png]
riuthamus
riuthamus

Any thoughts on this guys? This would be of great help if we could get this resolved. Thanks in advance!

InvalidPointer
InvalidPointer

Firstly, on the subject of bright water:

Backing up a bit, I think it's helpful to understand where that 'water color' comes from. Physically, you're doing a very simple (but workable) approximation to multiple scattering-- basically light is entering the water all over the surface, bouncing around a little bit *inside* the water volume, then leaving towards the viewer... among other directions. You can get all crazy and simulate this numerically, but it turns out just having a single, flat color does a pretty good job of capturing the core 'light enters somewhere, leaves elsewhere' effect.

Intuitively, you just need to control that simulated incoming amount of light. Since we're throwing correctness out the window already, I'd suggest doing the simple, controllable thing and just have a sort of 'day water color' and 'night water color' and then blend between those (preferably on the CPU!) before handing it off to the shader. In order to save artist work, it seems like you could analytically figure out what the difference between night light and day light is and then just apply that directly to the 'day' color.

Re: streaking--

That's a problem with your eye vector resulting from your water surface approximation; (I'd bet good money you're just using a single flat plane + normal map for the water surface) it's entirely physically accurate. The issue arises because water (and surfaces in general) don't really change orientation without some intuitive change in 'position' (and hence eye vector) to go along. Ideally you'd do away with the normal map entirely and just have a nicely tessellated/displaced water volume, but if that's off the table then you may be able to fake a displacement in the eye vector by adding a small vector scaled by a height value in the normal map alpha or some other such unused channel.

clb: At the end of 2012, the positions of jupiter, saturn, mercury, and deimos are aligned so as to cause a denormalized flush-to-zero bug when computing earth's gravitational force, slinging it to the sun.
Telanor
Telanor
Ah, so the streaking can't be fixed without actually making the plane 3D. I managed to figure out the specular issue. The backsides of waves being black is about the only issue left now.
Ashaman73
Ashaman73


The backsides of waves being black is about the only issue left now.

This is often an issue with screen normals pointing into the screen. The assumption, that all screen normals point outside the screen is just wrong, therefore you need to take special care of these normals.

One case would be, when using compression (xyz to 2 channel compression). E.g. the naive compression, when only saving the x and y coord of the normal and reconstructing z from them (z = -sqrt(x*x+y*y) ) will not work in this case. Although clamping could be a problem.

Therefore check your code for issues when your normals point into the screen.

Telanor
Telanor
We're not using screen normals though, they're regular world space normals. The problem is coming from the lighting equation making it black on the side that's facing away from the light. Actually.... in the processing of writing this I had an idea. There's no reason I need to do the lighting equation using the normal after the normal-mapping is applied. I switched it to just use the flat plane's normal and now the black is gone. Thanks for the suggestions guys.

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.