NME: reflections on modified in vertex shader plane are strange with PerturbNormal block

I am using Node Material Editor to displace y-coordinate of plane at vertex shader and want to change normal orientation with PerturbNormal block accordingly with analytically calculated normal.
I’ve made simple version which refers to the problem:

Here y-coordinate is displaced by H*sin(x/L) value. For perturbation normal block I pass remapped to [0,1] normal which is normalized (-H/L*cos(x/L), 0, 1).

On preview for plane I see that reflection of hemispherical light, when watched from above, is always either on topmost or bottommost point of sinusoid, although I would’ve expected the light to be directly below with some distortion, not on extrema.

Am I missing something?
I am going to apply some calculations for precalculated gradients of height textures later, but I need to understand what I’m doing wrong here already.

I’m apologizing if some of my questions may look dumb, though I really haven’t worked with shaders a lot :slightly_frowning_face: I feel that I somehow misunderstood how exactly PerturbNormal block operates.

cc @PatrickRyan

@tokareuv, I am trying to figure out exactly what you are trying to do here. I assume you are trying to modify the surface normal accurately with the displacement of the vertices, correct? In this case, there are a few things that are working against you in your current graph.

  1. The PerturbNormalBlock is intended to perturb mesh normals based on a tangent space or object space normal texture wired to the normalMapColor input. If you want to procedurally generate this input, you can, but you need to generate it as a texture that maps correctly to the UVs mapped in the mesh. This is likely not what you are looking to do.
  2. What you are doing with the modification you are trying to apply to the vertex normals won’t do what you are looking for in this instance because you have remapped the modification to 0-1. If you look at the X component of your modification using the debug node, you see the following:

    In Y, you pass 0.5 and in Z 1.0. So you are modifying all of the normals which start at (0,1,0) with a range of (0.0, 0.5, 1.0) to (1.0, 0.5, 1.0). So in the areas that are black in the image above is shifting the height of the normal by 0.5 and pushing it forward in Z by 1.0. This is making the vertex normal point in a very different direction.

What you should do instead is rather than trying to use a PerturbNormalBlock, just modify the affected normals before passing those to the WorldNormalBlock. So applying a -1 to 1 range value to the X component of the normal vector, scaled by the amplitude of the wave, you should get a more accurate surface normal for your light calculations.

1 Like

@PatrickRyan
Thank you for the reply!
Yes, the intention behind this shader is to correct normals of the surface in accordance with vertices displacement. It is one part of displacers, others consist some height textures (like Vorley and Simplex) with precalculated gradients in R/B channels, sampled with time and position coordinates (not UV), and that is a reason I’ve tried to avoid perturbing normals in vertex shader and to move to fragment shader to make small changes more visible and less distorted.
Though I see now that in sense of uv it will be incorrect.
Do you think it’s possible to achieve such a goal with given functionality?

@tokareuv, I reworked your node graph to do what you are asking for. The biggest problem you had in the original version was that if you want to use the perturb normal block, you have to pass color that can be interpreted as a tangent space or object space normal texture. In this case, I am using what would be expected for a tangent space normal texture

In the rework, I have simplified your graph and rearranged it to have the effect you were looking for. The light should reflect with the remapped surface normals.

The challenge is that real-time derivatives are an approximation and are quite noisy. Relying on them for highly reflective surfaces (very low roughness) will highlight that noise. And obviously, the more displacement you place on the plane, the more contrast is put into the normal texture and the more the noise is highlighted:

Versus low displacement:

1 Like

@PatrickRyan I tried to use this approach before. Initially I used this topic as reference point. But here’s the problem: as for some reason with derivatives perturbation is really small, the reflection that we can see with environment mode enabled is wrong (that’s why I made wave very high in this example).

It reflects as if the surface is almost flat (which is not surprising with small difference in normal map). Also I have concerns that, as we can see on normalMapColor input debug-panel, normals change depending on the angle we are watching it from (I suppose, that’s one of noisiness factors you mentioned before)

I tried to opt out of using derivatives for performance/quality balancing reasons: while sampling texture (Vorley, Simplex) kills some variety, I can use precalculated gradients here and avoid calculations per pixel with corresponding built-in blocks. But for this I need correct behaviour at least at a simpler case like sine function.