Just like everyone else I was having trouble getting the normals right.
I managed to get it in the end. It’s still a work in progress, but I thought it might help out a few people at this point already.
I implemented it using HLSL in a custom node in the material.
The code below is adapted from several pieces I found that are all based on the GPU Gems article.
The two things I was doing wrong in the normals calculation was:
- I was using the dot-product of the Direction and the WorldPosition, instead of the dot product of the Direction and the offset I just calculated.
- I didn’t add the WorldPosition to the offset in the dot product.
float r = wi * dot(Direction,WorldPosition+offset) + phi * Time;
I missed that last part originally, but it is in the original article. Notice that in equation 9 it adds the offset to x and y to get the final World position of the vertex.
The “subtract from one” part in the normal calculation is done by initialising the normal variable at the start of the script as float3(0,0,1).
float3 directions[3];
directions[0] = normalize(Direction1);
directions[1] = normalize(Direction2);
directions[2] = normalize(Direction3);
float3 offset = float3(0, 0, 0);
normal = float3(0, 0, 1);
for (int i = 0; i < NumberOfWaves; i++)
{
float wi = 2 * 3.1415926 / WaveLength;
float WA = wi * Amplitude;
float phi = Speed * wi;
float Qi = Steepness / Amplitude * NumberOfWaves;
float rad = wi * dot(directions[i], WorldPosition) + phi * Time;
float sine = sin(rad);
float cosine = cos(rad);
offset.x += Qi * Amplitude * directions[i].x * cosine;
offset.y += Qi * Amplitude * directions[i].y * cosine;
offset.z += sine * Amplitude;
float r = wi * dot(directions[i],WorldPosition+offset) + phi * Time;
normal.x -= directions[i].x * WA * cos(r);
normal.y -= directions[i].y * WA * cos(r);
normal.z -= Qi * WA * sin(r);
Amplitude /= Ratio;
Speed /= Ratio;
WaveLength /= Ratio;
wi *= Ratio;
}
return offset;
Result:

