Graphics Trick: Normals from Height

I’ve seen code floating around that computes a normal map from a height map by estimating tangent vectors and doing a cross product. That includes a bit of unnecessary extra math, so here’s a better way to compute the same thing.

First, let’s write the height field as a function of x & y (ok, it’s a texture, so call them u and v if it makes you feel better). So a height field is basically a texture that defines

z = h(x,y)

The tangent-based code already estimated the partial derivatives of h(x,y) with respect to x and y. Let’s call those hx and hy. There are several ways to do this: forward differences

hx = h(x+1) – h(x)

central differences:

hx = (h(x+1) – h(x-1)) / 2

Sobel filter (no, I’m not going to write that one out), or other derivative of a filter.

The trick comes in rewriting this thing as an implicit function of x,y,z that’s 0 on the surface, negative under the surface and positive outside the surface. That’s actually pretty easy:

f(x,y,z) = z – h(x,y)

So the neat thing is that the normal to an implicit surface like this is just the gradient of the implicit function:

N = (fx, fy, fz) = (-hx, -hy, 1)

That’s it. Much friendlier than cross( (1,0,hx), (0,1,hy) ). You could actually get exactly the same result by expanding out the cross product, but I think it’s much cooler (and more applicable in other situations) to know about the normal to an implicit function trick.

Marc

2 Responses to Graphics Trick: Normals from Height

  1. Should that not be something like N =(-hx, -hy, 1-sqrt(hx*hx+hy*hy)) to account for the normal’s unit length?

  2. Actually, it should be

        nlen=sqrt(1+hx*hx+hy*hy)
        (-hx,-hy,1)/nlen

    I didn’t mention it in the post, but I was doing this in the context of LEAN mapping for Civilization V, which needs un-normalized normals projected onto the z=1 plane. None the less, you want to scale all of the components equally to keep the normal pointing in the same direction.

    By the way, this copy of the blog is still lingering around, but the main copy is now on gaim.umbc.edu.

    Marc

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Connecting to %s