Normal calculation in shader and contour lines


I did some vertex displacement on ground mesh and tried to do normal calculation in fragment shader. Customization to the shader were applied via CustomMaterial.

My first attempt was to force the fragment shader to use the default normal computation. It gave flat shaded shadow effect.

Then I tried to use the same approach, but sample the height again in fragment shader instead of using the height data from vertex shader. It gave me result below with very clear contour lines.

After this, I tried to sampling the neighboring heights from the heightmap and computed normal as the following. Unfortunately it gave the same result.

      vec3 slope = vec3(hLeft - hRight, 0.01, hDown - hUp);
      normalW = normalize(slope);

There are some parameters I changed that help to reduce the contour lines. But I don’t know if it is the correct way? Is there standard way of fixing such problem?

For example, I achieved this result by changing paramY value. But it also makes the lighting effect weaker.

      vec3 slope = vec3(hLeft - hRight, paramY, hDown - hUp);
      normalW = normalize(slope);

Finally this version below seems to be the best so far. This is achieved by manipulating a scale factor and the different offset distance between current position and the neighboring positions for height sampling.

      vec3 slope = vec3(hLeft - hRight, 1.0, hDown - hUp);
      slope.x = slope.x * scale;
      slope.z = slope.z * scale;
      normalW = normalize(slope);

It also seem to be related to the heightmap used and the height range I choose for the heightmap. One of the heightmap seems don’t have this problem by default. But I was able to make the contour lines to show up for it as well by changing the parameters I mentioned above. :rofl:

1 Like

For your last formula, you should have 2 instead of 1 for y.

See the explanations here: vector - Calculate Normals from Heightmap - Stack Overflow

Just posting the actual math here in case that stack overflow link ever goes bad (I’ve had this experience before):

From vector calculus, the normal of a surface is given by the gradient operator:

enter image description here

A height map h(x, y) is a special form of the function f:

enter image description here

For a discretized height map, assuming that the grid size is 1, the first-order approximations to the two derivative terms above are given by:

enter image description here

At the bottom there ^ we can see were those coefficients come from.