I am working with a plane model which I am offsetting the vertices on the up axis to look like waves by using the function:

Sin(vertex.x * frequency) * amplitude + Cos(vertex.y * frequency) * amplitude

Previously, I did this with a single variable and was able to confirm my normal directions were correct by getting the derivative, taking the tangent direction from that, and getting the perpendicular direction to the tangent in order to get my normal direction. I then just flipped them so they were facing the right way.

Now that I have added in the second, y, variable I have tried to take a similar approach. Where I take the two partial derivatives, get the tangent direction for each, get the normal for each from that, and then add the normals together to get my final normal direction. However, it seems that the results I am getting are different from those of the Unity calculation, which is correct.

Here is an image comparing my results to the correct Unity calculation: Comparison

Does anyone know where about I am going wrong in my calculation? I think it may be related to where and when I am normalizing, but I have tested out a bunch of different options and can't seem to get it to look correct and am now questioning if perhaps my method for combining the two normals is wrong. Thanks for any input. Below is a snippet of the code I am using to do this.

offsetAmount = Mathf.Sin(vertices[i].x * _Frequency) * _Amplitude + Mathf.Cos(vertices[i].y * _Frequency) * _Amplitude;

private void DifferentiateNormals()
    // Loop through every normal and calculate it manually
    for (int i = 0; i < vertices.Length; i++)
        // The derivative is also an equation for the slope of a tangent line
        float offsetAmountDerivative = Mathf.Cos(_Frequency * vertices[i].x) * (_Frequency * _Amplitude);
        float offsetAmountDerivative2 = Mathf.Sin(_Frequency * vertices[i].y) * -(_Frequency * _Amplitude);

        // Rise / Run => Rise is the derivative function, run is 1
        Vector2 tangent = new Vector2(1, offsetAmountDerivative);
        Vector2 tangent2 = new Vector2(1, offsetAmountDerivative2);

        // The perpendicular of tangent direction (x,y) is (y,-x)
        Vector3 normal = new Vector3(tangent.y, 0, -tangent.x);
        Vector3 normal2 = new Vector3(tangent2.y, 0, -tangent2.x);
        // Flip the normals to be the correct direction (up)
        normal = -normal;
        normal2 = -normal2;

        Vector3 finalNormal = normal + normal2;
        //Vector3 finalNormal = new Vector3(_Amplitude * _Frequency * Mathf.Cos(_Frequency * vertices[i].x), -(_Amplitude * _Frequency) * Mathf.Sin(_Frequency * vertices[i].y), -1);
        //finalNormal = -finalNormal;

        if (direction == ModelUpDirection.Y_Up)
            // Works for Unity Plane
            normals[i] = new Vector3(finalNormal.x, finalNormal.z, 0.0f);
        else if (direction == ModelUpDirection.Z_Up)
            // Works for 100x100 Plane
            normals[i] = new Vector3(finalNormal.x, 0.0f, finalNormal.z);
    mesh.normals = normals;

1 Answer 1


This trick

Vector3 normal = new Vector3(tangent.y, 0, -tangent.x);

works great in 2D, when we only have 2 perpendiculars to choose from. It's not the right technique to use in 3D though, when we have a whole circle of possible perpendiculars!

So, you compute your tangent along the x direction:

Vector3 xTangent = new Vector3(1, 0, offsetAmountDerivative);

...and your y direction. Note that since we're differentiating with respect to y, the rate of change of x is 0 and the rate of change of y is one:

Vector3 yTangent = new Vector3(0, 1, offsetAmountDerivative2);

Now we have two vectors in space, one pointing mostly along the x direction and the other mostly along the y. Between them, they describe a flat tangent plane representing the incline of our surface at this point. We want the normal of that plane: a vector that's perpendicular to both of them at once (not just an average of some perpendicular of each).

We can get this with a cross product:

Vector3 normal = Vector3.Cross(xTangent, yTangent).normalized;

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .