Normal from 2D heightmap

Started by
6 comments, last by yahastu 16 years, 4 months ago
I've created a height map with some perlin noise, where the pixel values correspond to the Y coordinate of my vertices. Now, I want to calculate normals based on this height map. I guess I can use GLSLs dFdx & dFdy to get the height variation (in X & Z directions) but how do I create the normal from that? I tried vec3 N = normalize(vec3(dFdx(sampel.y), 1.0, dFdy(sampel.y))); but then all normal.y's got very close to 1.0, and that's not correct.
Advertisement
I have no direct experience with GLSL, but presumably those gradient operators don't scale the return-values. So I suspect you'd have to multiply the x- and z-coordinates by the respective texture dimensions prior to normalisation.
Ring3 Circus - Diary of a programmer, journal of a hacker.
Thanks, that did it.
Mathematically the normal is defined like this:

Let S(x,y):R^2 -> R^3 be your surface, and f(x,y):R^2 - > R be your heightmap.

S(x,y) = <x, y, f(x,y)>

dSdx(x,y) = <1, 0, dfdx(x,y)>
dSdy(x,y) = <0, 1, dfdy(x,y)>

The normal of S at (x,y) is defined by dSdx(x,y) cross dSdy(x,y). The cross product can be computed by taking the rows of a 3x3 matrix to be [<i,j,k>, dSdx, dSdy], and then taking the determinant. This yields:

N(x,y) = normalize < dfdx(x,y), -dfdy(x,y), 1>

So, your result just switches around the axis
Quote:Original post by yahastu
This yields:

N(x,y) = normalize < dfdx(x,y), -dfdy(x,y), 1>

I'm sure it's just a typo, but you've lost a sign. Though you're quite right about the normals being upside-down. In akerlund's case, the correct formula for the upward normal is

vec3 N = normalize(vec3(-dFdx(sample.y), 1.0, -dFdy(sample.y)));
Ring3 Circus - Diary of a programmer, journal of a hacker.
Yep, the final equation I wrote was supposed to be:

N(x,y) = normalize < -dfdx(x,y), -dfdy(x,y), 1>

(the first term because it is 0*ddy - 1*ddx = -ddx)
Well, my map is S(x,y) = <x, f(x,y), y >, so the normal should then be

N = normalize < -dfdx(x,y), 1, -dfdy(x,y)>

right?

This still gives way too high Y values. Scaling the df's with the texture size (512) helps but it's not 100% correct. Scaling with 256 gives a better result. But how can that be?
You need to make sure that you scale both XY (image plane) and Z (image depth) appropriately. For example, if f(x,y) ranges from 0-255, and your image is 512x512 so X/Y range from 0-512, and you want to map this into world space such that it is inside a 20x20x20 cube, then you would have to scale f(x,y)/255*20, X/512*20, Y/512*20 ...you get the idea

This topic is closed to new replies.

Advertisement