Creating waves on a water surface (HLSL)

Started by
28 comments, last by kobingo 17 years, 11 months ago
Here's how I'm doing it in one of my water shaders:

float3 combinedNormal;        combinedNormal = tex2D(animatedNormalMapSampler, IN.normalMapCoords1.xy);        //convert the normal from 0 to 1 into -1 to 1        combinedNormal = normalize(2 * combinedNormal - 1);                //IN.reflectCoords.z is the distance from the viewer to the pixel        float distScaledDistortion = IN.reflectCoords.z;                //setup a distortion fadeout with distance        distScaledDistortion = clamp(3 / (distScaledDistortion + 2), .3, 1);        //scale the normal by this distortion and a user-supplied constant - reflectDistortion        float3 scaledNormal = normalize(combinedNormal * float3(reflectDistortion * distScaledDistortion, 1, reflectDistortion * distScaledDistortion));            //project the coordinates and offset them by the normal        //note that up in this normal map is y not z        IN.reflectCoords.xy = IN.reflectCoords.xy / IN.reflectCoords.w + scaledNormal.xz;        float4 reflectiveColor = tex2D(reflectionSampler, IN.reflectCoords);


Hope that helps.
Advertisement
It's really not that complicated.

Your quad should have some texture coordinates for the du-dv map.

1. Reflection

in the shader, offest the texture coordinates of the projected reflection by *2.0-1.0 of the du_dv map.

that will put wave on the reflection on the water.

2. Refraction

do an offscreent render to toexture from the camera pov.

pass the refraction texture to the matrix -

use a fresnel term to calculate a blend from the camera pov with the the du_dv map to mix the reflection and refraction texutes.

et voila.

Image Hosted by ImageShack.us



also, don't use the above z read out of te refraction texture. you will be calculating the offset with air having the same coefficient as water.

create your own depth texture for the that, by using the pixels depth below the water plane level.

jamesw, I tried this code you showed me, and now my pixelshader looks like this:
    float2 projcoord = (IN.project.xy / IN.project.z);    float4 bump = tex2D(NormalSampler, projcoord);    float4 reflect = tex2D(TextureSampler, projcoord);	    float3 combinedNormal;    combinedNormal = tex2D(NormalSampler, IN.project.xy);    //convert the normal from 0 to 1 into -1 to 1    combinedNormal = normalize(2 * combinedNormal - 1);        //IN.reflectCoords.z is the distance from the viewer to the pixel    float distScaledDistortion = IN.reflectCoords.z;        //setup a distortion fadeout with distance    distScaledDistortion = clamp(3 / (distScaledDistortion + 2), .3, 1);    //scale the normal by this distortion and a user-supplied constant - reflectDistortion    float3 scaledNormal = normalize(combinedNormal * float3(reflectDistortion * distScaledDistortion, 1, reflectDistortion * distScaledDistortion));    //project the coordinates and offset them by the normal    //note that up in this normal map is y not z    IN.project.xy = IN.project.xy / IN.project.w + scaledNormal.xz;    float4 reflectiveColor = tex2D(TextureSampler, IN.project);        return reflectiveColor;

But the problem is, not only does it distort the reflections, it also changes the uv coordinates of the reflections so much that it doesn't look right anymore... is there something wrong with my shader code?
First are you sure you're projecting the reflective texture coordinates right? In other words, does it look like a mirror when you're not distorting the coords?

Make sure you set the scale constants appropriately. I'm using

float reflectDistortion = .03;

but it really depends on your normal map.

It looks like you are using projective coordinates to lookup into the normal map, this isn't what you want. The normal map should be applied just like a normal texture.
Yes, my reflection is perfect when I don't distort it.

I changed the line from:
combinedNormal = tex2D(NormalSampler, IN.project.xy);

to:
combinedNormal = tex2D(NormalSampler, IN.texCoordDiffuse.xy);

but it was still the same problem...

Weird, but the whole reflected surfaces moves "up" when I do this. If I change the line:
IN.project.xy = IN.project.xy / IN.project.w <plus> scaledNormal.xz;

to:
IN.project.xy = IN.project.xy / IN.project.w - scaledNormal.xz;

(subtract instead of add) then the whole surface moves "down".

Also my float reflectDistortion is .03

Some error with scaledNormal?
Could you post a screenshot?

Also make sure that your normal map is using x and z as the horizontal components, otherwise you will need to change the shader.
Sure, here is a screenshot: Water 1
(you can see that the reflection offset is wrong)

Here is screenshot of only reflection (no distortion): Water 2
(the reflection offset is correct)

What do you mean by I must use the x and z horizontal components, and if I dont, what do I need to change in my shader?
Quote:What do you mean by I must use the x and z horizontal components, and if I dont, what do I need to change in my shader?


What I mean is the normal maps that I use with that shader have the normals stored as y axis going up and the x and z horizontal. So a vector straight up would be stored as (127, 255, 127). Most normal maps are stored with z up and x and y horizontal, so up would be (127, 127, 255). Basically if your normal map is predominately blue then you need to change how you treat the normal fetched from the normal map. So instead of

    float3 scaledNormal = normalize(combinedNormal * float3(reflectDistortion * distScaledDistortion, 1, reflectDistortion * distScaledDistortion));    IN.project.xy = IN.project.xy / IN.project.w + scaledNormal.xz;


you would put


    float3 scaledNormal = normalize(combinedNormal * float3(reflectDistortion * distScaledDistortion, reflectDistortion * distScaledDistortion, 1));    IN.project.xy = IN.project.xy / IN.project.w + scaledNormal.xy;
Ok, I'll try that as soon as I get home. I couldn't perhaps "borrow" one of your normal maps? :-)

This topic is closed to new replies.

Advertisement