normal map -> height map

Started by
6 comments, last by DV8D 20 years, 3 months ago
Anyone have any thoughts on howto convert a rgb normal map "back to" a lets say a grayscale heightmap? Think I''ve seen such a thing once but googling gives me nothing.
Advertisement
that'll be a bit tricky but lemme take a stab.

basically the normal map will allow you to retrieve the "slopes" (forget the 3D term, is it gradient?) of the polygons.

my initial impression is that you should seperate each normal into 2 vectors one for the XZ plane and one for the YZ plane. then by comparing slopes between vertices, and knowing the stride (X & Y widths of each quad component) you should be able to work out a relative height field pretty easily along each of the Y and X axes. there's a complication buried there in that each normal you have is really an average of the slopes of the surrounding triangles. for my algorithm here to work you'll have to ponder a way to de-average the normals...i think (there may be a way around doing that, i haven't given it enough thought). once you have the relative height field, just normalize it to whatever suits your fancy (my leaning is to have the lowest point be zero on the Z axis).

i don't want to get to bogged down in brainstorming this since it'll probably take a while to explicitly work out the math.... but that's your basic algorithm

i'm happy to elaborate or clarify on this algorithm, so just ask away.

-me

P.S. this is a really good interview question...i'm going to store it away for torturing candidates

[edited by - Palidine on January 7, 2004 1:20:59 PM]
Off the top of my head...

The normals in your normal map are made up of partial derivitives of the height map. So to get the height map back, integrate.

I''d build the height map out of floats first. Start in the upper left corner with a value of 0.0. To get the height of the pixel to the right add the X component of the normal. To get the value below add the Y component.

When you''re done, find the min and max values of all the floats and rescale to fit the range of your grayscale image.
good to see that i came up with a similar alrithm as someone else .

it occurred to me that normal maps are not necessarily compounded, (i.e. sometimes you have one normal for each vertex of each triangle, rather than one normal per vertex but each surrounding triangle uses the same normal). it''ll mean the difference between a really simple algorithm and having to worry about the complications of de-averaging the normals, to retrieve the "slope" of each triangle. make sense?

so you need to figure out if your normal map has averaged adjacent triangle slopes to create each normal
since the original poster referred to converting the normal map "back to" a grayscale image, I''m guessing they''re taking about normal maps that are created independent of any particular geometry.

The typical way to create a normal map from a height map is to compute the gradient (a.k.a. partial derivatives): dh/dx and dh/dy for each pixel and then the normal is just
<-dh/dx, -dh/dy, Cz>

Where Cz is some arbitrary constant which affects the perceived "steepness" of the bumps on the surface. And of course that vector has to be normalized.

So if this is the type of normal map we''re talking about then I don''t think they have to worry about whether the geometry has averaged normals or not and the technique you and I both described ought to work fine.

On the other hand, if the normal map was generated for a particular piece of geometry, say by projecting a higher resolution mesh onto a lower resolution mesh, then one needs to be concerned with the tangent space in which the normals of the normal map reside.
>>So to get the height map back, integrate.

Yeah. The only problem is that the average height is lost when we did differentiation (computed the normal map), so we need to use some explicit average height.

>>I'd build the height map out of floats first. Start
>>in the upper left corner with a value of 0.0.

This is a nice solution.

What we're really trying to do is to:

1.) Set upper left corner height to 0.0
2.) Enfore the height of each heightmap position (not including upper left corner) so that when differentiated, we'll get the normal map.

>>To get the height of the pixel to the right add the X >>component of the normal. To get the value below add the Y >>component.

This is a sort of relaxation method to enforce condition 2.) I'm not sure how the rounding/differentiation errors cumulate when going nearer to lower right corner of the heightmap. If the differentiation is done properly, then it might be possible to exactly reconstruct the heightmap (not counting rounding error).

A way to make relaxation better would be to sweep through the array in different directions (or possibly even randomly) re-setting condition 2.) on every position.

Another way would be to construct a system of linear equation out of conditions 1.) and 2.) and solve it using an iterative method.

If you have the time and motivation, I'd suggest trying all of them and seeing which one is the most accurate.

- Mikko Kauppila

[edited by - uutee on January 7, 2004 7:32:32 PM]
Thanks for the input guys. I was leaning towards something like Palidine suggested. I''ll try the first AP''s method and evalute the quality of the result (seems all to easy ).
Before attempting any conversion (which is fairly tricky to do right, I''ll post later a detailed procedure for it), you must know whether your normal map is in model space or in tangent space. It makes a huge difference since you''re basically evaluating different basis for each case and underlying vectors align to different plane (very important if the normal map was generated automatically from mesh data, which is the only case I assume you''d want to attempt such a conversion anyway) so you''d get very funky results.

Mind you tell us why you need such a conversion?

This topic is closed to new replies.

Advertisement