Converting Bump Maps to Height Maps

Cryect

Regular
Alright on another forum saw some interest in higher polygon models than the ones that come with Doom3. People were discussing remodeling the models but that seem kinda crazy to myself really. If we have the nice normal maps generated from models with millions of polys then we should be able to take those normal maps and generate a height map.

Of course the scale and bias would have to be handled subjectively for each normal map but the idea seemed a simple integration issue really. So did a simple integrator that starts in the upper leftcorner with a value of 0 and then working its way from that upper corner to the lower right corner. Unfortunately this results in height maps that smear their height diagonally in that direction as well.

Here's a picture of a sample normal map and resulting height map. And also matlab prototype code (actually written for Octave since at my grandfathers and I didn't bring Matlab cd's with me).

So curious if anyone has any ideas how to fix it up so it doesn't smear? (it makes sense each pixel is based on the data diagonally from it and I've confirmed its not the normal map causing the smearing as well)

Or is this just an issue due to lack of sampling data leading to error propagation.

normal1.jpg


Height.gif



Code:
function HeightMap=IntegrateNormalMap(XNorm,YNorm)

YRes=size(XNorm,1);
XRes=size(XNorm,2);

HeightMap=zeros(YRes,XRes);

for X=2:XRes
	HeightMap(1,X)=HeightMap(1,X-1)+XNorm(1,X-1);
end

for Y=2:YRes
	HeightMap(Y,1)=HeightMap(Y-1,1)+YNorm(Y-1,1);
end

for Y=2:YRes
	for X=2:XRes
		HeightMap(Y,X)=(HeightMap(Y-1,X)+HeightMap(Y,X-1)+XNorm(Y,X-1)+YNorm(Y-1,X))/2;
	end
end

Average=sum(sum(HeightMap))/(XRes*YRes);
HeightMap=HeightMap-Average;
 
It's probably cause of the lack of sleep but tt looks like i'm having some troubles using your function here.. Cryect the YNorm and XNorm should be 2 arrays read from the red and green channel right?
Code:
Im=imread('c:\matlab\work\normal.bmp');
Im=double(Im);
R=Im(:,:,1)/256;
G=Im(:,:,2)/256;
B=Im(:,:,3)/256;
HeightMap=IntegrateNormalMap(R,G);
imwrite(uint8(HeightMap*256),'HeightMap.bmp','bmp');

It looks like there is something wrong but can't figure our what..
i also changed in your function:
Code:
Average=sum(sum(HeightMap))/(XRes*YRes);
HeightMap=HeightMap-Average;

to
Code:
HeightMap=HeightMap-min(min(HeightMap));
HeightMap=HeightMap/max(max(HeightMap));
So the HeightMap should go from 0 to 1.

But the result is kinda not what i expected.. :?

Edit: Added the images
NormalHeightMap.jpg
 
That code look's OK to me, but I'm wondering about how you're normalising the values in the bump map initially. Remember that a bump map normally encodes a surface normal in the RGB value, so XYZ of normal = RGB of pixel.

So you'll need to take that normal, scale it so that the Z component is 1 (because in generating it it will have been scaled so that the magnitude is 1), and then the X component should be dz/dx and the Y should be dz/dy.

At least I think that would be how it would work.

Certianly any edges in one direction seem to be having a greater effect than in the other. (This could also be down to lack of resolution in the original bump map, I would expect errors)
 
NocturnDragon said:
Cryect the YNorm and XNorm should be 2 arrays read from the red and green channel right?

Yeah, heh I had a little lack of sleep at the time I was writing that code as well.



I just took the straight x component of the normal for integration along X coordinates and Y component for along Y coordinates (if you think about the magnitude along those axis coordinates to how much the height should raise going that way or at least thats what I was thinking last night at 11)

I think I'm going to change the integration to use 2nd Order Runga Kutta by adding instead (XNorm(X-1)+XNorm(X))/2 which should give higher resolution for the normal and result in the propagation errors in theory adding up a order less. Will try that out, see how it works, and report back a little later (just got off a plane).
 
The smearing may perhaps have something to do with that zeros doesn't have an exact representation? Zero should be represented with 127.5, so that falls between 127 and 128, so you'll either have either 0.0078 or -0.0078 instead. So flat surfaces in the bumpmap will be represented with a slight slope in x and y. Maybe you'll have to do a special case for (127,127,255) and (128,128,255) and force it to mean exactly (0,0,1)?
 
I don't think it's very easy to go from normal maps to height maps. Even when starting from a height map, calculating a normal is an approximation. Reintegrating will propogate these approximation errors like crazy.

The directional blur just shows you how the integration is being done. Maybe if you had an iterative integration algorithm that looks at all neighbors and adjusted the height it would work a bit better. I might give it a try tonight.
 
Back
Top