How to recalculate normals?

3.8k Views Asked by At

I'm trying to write a simple shader that moves all verts with a sin wave.

v.vertex.y += sin(_Time.y * _Speed + v.vertex.x * _Amount * v.vertex.z) * _Distance;

The problem is that after moving them, the normals are wrong, thus there are no real time shadows. I've searched a lot and found that I need to recalculate the normals using fake neighbours. None of the implementations were for Unity Shaderlab, so I can't just copy and paste them, and my knowledge of shader code is pretty basic so I can't translate what I found to what I need.

Can anyone help me with what and how to recalculate normals after moving a vertex?

3

There are 3 best solutions below

2
On

The math behind normal calculation is cross product.

Take vertex A, B and C from a triangle. Then create vectors AB and AC. ABxAC will give you the normal of the triangle. ACxAB will give the opposite normal (ABxAC = - ACxAB).

en.wikipedia.org/wiki/Cross_product

If you were to move the mesh from Mesh class, there is a RecalculateNormals. But I guess that doesn't apply here as you work on the shader.

2
On

Having not done this myself, I did a little googly and found this shader, used to invert the normals. It does this by tweaking the vertex information:

    void vert(inout appdata_full v) {
        v.normal.xyz = v.normal * -1;
    }

You should be able to tweak your shader to recompute the normal direction based off the other alterations and update it in a similar manner.

0
On

The normal vector of a surface is related to its derivative. In the case of a sinus wave its derivative is just cosine wave cos(). Both can be computed efficiently in a single operation called sincos().

Perfect introduction exercise :) plot both and observe the relationship.

The previous answer by Everts works when you have access to neighbouring vertices, which you usually don't in shaders (but that would be fine on the CPU).