Search code examples
directx-11hlsl

How would I calculate the normals of a wave for lighting?


I'm doing vertex manipulation of a plane with DirectX11 and for my lighting (multiple point lights with specularity) I need to recalculate the normals of each vertex.

My question is essentially what equation would I use to do get the normals (x & y) of this wave equation (in HLSL):

input.position.y = height * sin(input.position.x + time) * sin(input.position.y + time);

Solution

  • You need to calculate the tangent to the surface along both x- and y- axis. You can do this at each vertex in the vertex shader. The normal to the vertex is the cross product of the tangent vectors at that vertex.

    In order to get the tangents, you need to calculate the derivative of you equation. Your equation is of the form:

    f(x,y) = a*sin(b*x+t)*sin(c*y+t)
    

    Where a is the amplitude (or height) of the wave, b and c are the wave length or period and t is the wave's phase. The partial derivatives of such function are:

    df/dx = a*b*cos(b*x+t)*sin(c*y+t) // tangent equation along x-axis
    df/dy = a*c*sin(b*x+t)*cos(c*y+t) // tangent equation along y-axis
    

    In your case a=height, b=c=1. The tangent vectors are:

    tx = [x, y, df/dx] // tangent vector along x-axis at point (x,y)
    ty = [x, y, df/dy] // tangent vector along y-axis at point (x,y)
    

    Finally, to calculate the normal you take the cross product of tangent vectors:

    n = cross(tx,ty)
    

    Note that the order of cross product matter. If you switch the order then the result will be -n. Evaluating the cross product we get:

    n = [y*((df/dy)-(df/dx)), x*((df/dx)-(df/dy)), 0]
    

    Substituting df/dx and df/dy in above vector:

    k = (a*c*sin(b*x+t)*cos(c*y+t) - a*b*cos(b*x+t)*sin(c*y+t))
    n = [y*k, x*(-k), 0]
    

    So, for your equation we get:

    k = height*(sin(x+t)*cos(y+t) - cos(x+t)*sin(y+t))
    n = [y*k, -x*k, 0]
    

    At each vertex, when you put in the x and y coordinates, you'll get the normal vector. I might have made some mistakes in my derivation since my calculus is rusty. But it should be something along these lines.