# OpenGL Good specular for water surface

This topic is 1763 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

## Recommended Posts

Hi there,

I am trying to have a convincing ocean shader, but i am struggling a bit with the specular effect.
Here is what i have now :

Bigs waves

Small waves

I ported this tutorial from OpenGL to DirectX 11 to simulate ocean with FFT (currently runing on CPU, so very poor performance)

I'd like this to look a bit more like AC IV ocean's

I followed this tutorial which uses a normal map to render more realistic water

And things got a lot worse :

Here is my pixel shader :

float4 PS(VertexOut pin) : SV_Target
{
float zFar = 300.0f;
pin.NormalV = normalize(pin.NormalV);
float3 viewRay = normalize(pin.PosV);
float3 refractRay = refract(viewRay, pin.NormalV, 0.75f);
float2 InvTextureSize = float2(1.0f / gBackBufferWidth, 1.0f / gBackBufferHeight);
float2 texCoord = float2(pin.PosH.x, pin.PosH.y) * InvTextureSize;

float3 diff = viewRay - refractRay;
float3 newTarget = viewRay + refractRay;

texCoord = texCoord + (diff.xz * 0.1f);

float3 n1 = gNormalMap.Sample(PointSampler, (texCoord / 0.1f) + (float2(1.0f, 0.0f) * (gTimer * 0.25f)));
float3 n2 = gNormalMap.Sample(PointSampler, (texCoord / 0.2f) + (float2(1.0f, 0.0f) * (gTimer * 0.25f)));

// Expand the range of the normal from (0,1) to (-1,+1).
n1 = (n1 * 2.0f) - 1.0f;
n2 = (n2 * 2.0f) - 1.0f;

float3 n0 = normalize(n1 + n2);

float zw = gDepthMap.Sample(PointSampler, texCoord).x;

// Reconstruct linear depth.
float depth2 = ProjectionB / (zw - ProjectionA);

float3 groundPosV = normalize(newTarget) * depth2;
float distance = DistanceBetweenPoints(groundPosV, pin.PosV);
float3 sourceColor = gGroundMap.Sample(PointSampler, texCoord + (n0*0.03f));
float3 sandColor;

if(sourceColor.x == 0.0f)
{
sandColor = float3(0.0f, 0.0f, 0.0f);
}
else
{
sandColor = GetWaterColorAt(sourceColor, distance);
}

float4 emissive_color = float4(sandColor, 1.0);
float4 ambient_color = float4(0.31, 0.54, 0.75, 1.0);
float4 diffuse_color = float4(0.5, 0.65, 0.75, 1.0);
float4 specular_color = float4(1.0, 1.0, 1.0,  1.0);

float emissive_contribution = 0.80f;
float ambient_contribution  = 0.30f;
float diffuse_contribution  = 0.30f;
float specular_contribution = 0.30f;

float3 lightVec = normalize(gSunLightDir);
float3 toEye = normalize(gEyePosW - pin.PosW);

float3 normal = normalize(pin.NormalW + n0); <--------- This is where i add the normal map value to the normal i already have.

float d = dot(normal, lightVec);

float4 finalColor = emissive_color * emissive_contribution +
ambient_color  * ambient_contribution  +
diffuse_color  * diffuse_contribution  * max(d, 0);

// Calculate the reflection vector using the normal and the direction of the light.
float3 reflection = -reflect(lightVec, normal);

// Calculate the specular light based on the reflection and the camera position.
float specular = dot(normalize(reflection), normalize(pin.ViewDirection));

float specularShininess = 20.0f;

if(specular > 0.0f)
{
// Increase the specular light by the shininess value.
specular = pow(specular, specularShininess);

// Add the specular to the final color.
finalColor = saturate(finalColor + specular);
}

finalColor.a = 1.0f;
return finalColor;
}


I took a look at NVidia ocean demo and realized they used an ocean mesh with a lot more vertices

I thought it came from by ocean mesh resolution, so i increased my grid resolution, but it still dit not do the trick :

Nvidia mentions something about using perlin noise to add more details, but doesn not say why and how.

So before i attempt to decifer NVidia's code, can anyone give me a high level explanation on why my ocean looks so bad ?
I know i still don't have reflection & caustic, but still, does this explain eveything ???

You can download my binaries here, shaders are compiled at runtime, so you can try to edit and see what's wrong :-/
You can tweak the settings.xml file to play with grid resolution and waves amplitude.

Thanks,
Yann

##### Share on other sites

You want a much higher shininess than 20 for water. Water is extremely reflective, and is usually a near perfect mirror at most viewing angles (though you don't want to do that in a shader because of aliasing), so make that a lot higher, maybe somewhere near 128-256. As it is now your water looks like plastic.

You mention wave amplitude - that usually doesn't matter, if you've ever watched a pond or a lake in calm weather, there are almost no waves, yet the water sparkles. Why? Because those tiny waves are actually sharp (high frequency), so only a small part of them is able to face the right direction to reflect lots of light towards you. What does that mean when translated to graphics? It means your normal map (and possibly wave heightmap) isn't high resolution enough, and is being blurred so much in the process that the wave normals lose all their high frequencies, resulting in a yucky "jello", almost flat appearance.

In shallow water, caustics play a huge part in lighting, as light that refracts into the water is reflected off the bottom and back towards the surface, and is then refracted towards you, which tends to produce a kind of "ambient lighting" term for water (consider your last two pictures - all those unlit areas on the water surface, should still get some light).

And finally, water is not air, it's a liquid. So you can't just render the plane (interface) between water and air and expect it to suffice. Light loses energy in a liquid, and is scattered by it, so the bottom of your ocean shouldn't be visible at all, except near the shore where it appears slightly blurred and tinted. Sufficiently deep water takes a color depending on its composition, usually ranging from light blue/green for shallow seawater to deep blue for deep seawater. Obviously it's unrealistic to simulate all of these in a game, but a cheap way to approximate it is to have a kind of "fog" in water depending on the distance between your terrain and the water plane (by reading the depth buffer) with exponential decay and a green/blue tint. That will almost certainly make your water look much better.

Fixing that and then adding proper reflection/refraction with a little caustic map should dramatically improve the quality of your water.

##### Share on other sites

Thanks a lot for your response Bacterius !

To 200

http://imgur.com/6G1Pv5S

To 2000

http://imgur.com/9mJuujo

To 7000

http://imgur.com/KXaNaBU

This is much better !

I wonder why my specular effect is located on such a small area though, i'll try to tweak my shader a bit to come up with a better algorithm.

Regarding the sharpness of my waves, that's why i increased my grid resolution, and added a height map, it did not help a lot though.

And regarding color absorption in water, i am actually handling that with this function which i wrote based on this article.

sandColor = GetWaterColorAt(sourceColor, distance);

//...

float3 GetWaterColorAt(float3 sourceColor, float depth)
{
float rIndex = 7.0f;
float gIndex = 10.0f;
float bIndex = 70.0f;

float sandRed = sourceColor.r;
float sandGreen = sourceColor.g;
float sandBlue = sourceColor.b;

float lostRed = (depth / rIndex) * (1.0f/3.0f);
float lostGreen = (depth / gIndex) * (1.0f/3.0f);
float lostBlue = (depth / bIndex) * (1.0f/3.0f);

sandRed = sandRed - (sandRed * lostRed);
sandGreen = sandGreen - (sandGreen * lostGreen);
sandBlue = sandBlue - (sandBlue * lostBlue);

float3 sandColor = float3(max(0, sandRed), max(0, sandGreen), max(0, sandBlue));
return sandColor;
}


I'll try to add caustic and reflection to see if i get better results.

Thanks again !

• ### What is your GameDev Story?

In 2019 we are celebrating 20 years of GameDev.net! Share your GameDev Story with us.

• 13
• 9
• 15
• 14
• 46
• ### Forum Statistics

• Total Topics
634059
• Total Posts
3015291
×