Sign in to follow this  
LE

Calculating normals from 2D heightmap

Recommended Posts

Hi I have a simple question. I can't figure out how to calculate the normals for a 2D heightmap. normal.x = 128 - 64 //Height value row2 col 0 and 2 normal.z = 96 - 128 //Height value row 1 col 1 row 3 col 1 Now how do I calculate the x normal and z normal from this input? I am asuming height of map 0-255, angles between -90 and + 90 degrees. Thanks

Share this post


Link to post
Share on other sites
Here's an easy way to do it, lets say at row i, column j. I am assuming columns increase along x (increasing j = increasing x), rows increase along y (increasing i = increasing y), and the height is z.

First, create a representative 3D vector between columns, a vector at row i. At row i, the vector should be computed using points at columns (j+1) and (j-1):

T1 = <X(i,j+1),Y(i,j+1),Z(i,j+1)> - <X(i,j-1),Y(i,j-1),Z(i,j-1)>

Next, do the same t hing between rows, e.g., a vector at column j. It uses rows (i+1) and (i-1):

T2 = <X(i+1,j),Y(i+1,j),Z(i+1,j)> - <X(i-1,j),Y(i-1,j),Z(i-1,j)>

Normalize T1 and T2. These are your tangent vectors! You can easily compute the normal from those:

N = CrossProduct(T1,T2)

Now, there are more sophisticated methods. This is basically a bilinear interpolation type approach. You could fit a more complex surface, such as a bicubic surface, which would give a polynomial function in two variables that you can take derivatives of to get the two tangent vectors. But, this is an easy approach for you to try.

Hope that helps!

Share this post


Link to post
Share on other sites
Actually, the tangent vectors T1 and T2 don't have to be normalized to use them in a cross product to get the surface normal. Without normalizing T1 and T2, the cross product result will also not be normalized. Since you'll likely want a normalized surface normal, you can merely normalize this result, instead of T1 and T2.

Share this post


Link to post
Share on other sites
I do it like that:
normal.x=(h[x-1,y]-h[x+1,y]);
normal.y=(h[x,y-1]-h[x,y+1]);
normal.z=vertical_scaling_factor*2;
normal.Normalize();
where h is heightfield, vertical scaling factor is the unit height in the units of height grid.

How it works: For heightfield h( the vector v = [-dh/dx , -dh/dx, 1] is normal to the surface. Which can be proved as follows:
The vector 1,0,dh/dx is tangent to the surface. It's dot product with v is -dh/dx*1 -dh/dx*0 + dh/dx*1 = 0 . The vector 0,1,dh/dy is different, non-parallel tangent, it also has dot product with v equal to zero. Therefore the v is orthogonal to both tangents, and is orthogonal to the surface.

Share this post


Link to post
Share on other sites
Great method, Dmytry. Just a typo correction:

vector v = [-dh/dx , -dh/dy, 1]

I worked out your method just to prove your solution to myself, and I noticed the slope is computed in the same method that grhodes_at_work mentioned (which I also use).

I am not sure if this is well known (since I did the simple math myself, and didn't research this), but I thought I should point out to the original poster (and perhaps others) that computing the slope as: slope = h2 - h0 / 2, given three equally spaced height values h0, h1, and h2, is actually the solution of the derivative (slope), at the middle value, of the quadratic interpolation of these three heights. So, it's not just a quick 'hack'; it's quite meaningful.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this