Sign in to follow this  
Basic

2D normal map generation

Recommended Posts

Hello, I was recently using Nvidia's normal map filter with Gimp 2(it's also availiable for Photoshop if you've got it), and I wanted to create my own normal map filter. By doing this, I'd be able to generate normal maps on the fly in my games, and help cut down on media a bit. Does anyone know where I could learn about generating a normal map from a regular rgb image? As far as I've researched, it's a matter of going from Initilial image --> Greyscale image --> normal map Generating the greyscale is no problem, I've made my own alogrithm for that, but I have no idea where to begin with generating a normal map. I'd really appreciate any help, thanks. 8)

Share this post


Link to post
Share on other sites
Once you've got a greyscale image, just treat it like a height map and generate the normals for it. Once you've got the normals then these can be encoded into an RGB colour without too much work.

Share this post


Link to post
Share on other sites
So it really doesn't matter if I get that fancy "blue shade" for my normal map to really work?

Right now I'm trying something like this

red = (pixelx + 255)/2
green = (pixely + 255)/2
blue = 255

I understand that the blue channel must always be there, is this correct?

[EDIT]
Never mind what I said above. :)
I'll post again shortly. I think I almost have it down.


[EDIT AGAIN]

So if I understand correctly, to generate a normal map, it's just the average color of the pixel from the left,right,top,and bottom, and the red and green channels are where the height is taken from?

:)

Share this post


Link to post
Share on other sites
That blue shade comes from almost everyone using the blue channel for the local up (usually Z) axis. XYZ->RGB maps rather intuitivly. So for a mostly flat surface you end up with most normals pointing outwards, so they get almost full blue and greater or lesser amounts of red and green depending on the actual tilt.

You basically need to get the delta (change) in height between your current greyscale point and all the adjacent ones. From these you get your tangent vectors, then you do a cross product to get your final normal. And don't forget to rescale your normals' XYZ components from [-1, +1] to [0, 255].

Share this post


Link to post
Share on other sites
Okay, thanks for the help. I have just successfully got an image generated. :)

I'm interested in knowing how to define the edges more, or add more depth to my normal map though. Any ideas on how this is done?

Share this post


Link to post
Share on other sites
After some fiddling around a little bit I have got the normal map working perfectly. I can't alter it or do anything fancy, but it works. :) I know it works because I've tested with images I'd made in GIMP prior to this.

However, I'm having some problems creating a heightmap. I've generated a greyscale image, but that doesn't seem to be good enough. Anyone know where I could find an alogrithm for generating proper heightmaps?

Is my process wrong?

Should this

a.) Initial image --> Greyscale image --> normal map

by changed to this

b.) Initial image --> Greyscale image --> Heightmap --> normal map

or maybe this

c.) Initial image --> Heightmap --> normal map

Thanks for any help provided.

Share this post


Link to post
Share on other sites
You can't just take a regular texture and generate a normal map from it, as the information just isn't there. Heightmaps are usually drawn by the artist at the same time as they create the texture.

Note also that RGB->greyscale usually involves different weightings for the different colours than just a straight avarage.

Share this post


Link to post
Share on other sites
Yeah, right now I'm just using an average of the points. So are you saying I need to draw a new texture manually for the height map? Or are you saying that after the greyscale is generated, I need to play with the weights before generating the normal map?

Thanks for all your help.

Share this post


Link to post
Share on other sites
Ack, I'm so close. I think I've got the heightmap down, and I think I know where the problem is.

I think the problem is with the scaling.

Could you elaborate or perhaps show me what you mean by this?

"And don't forget to rescale your normals' XYZ components from [-1, +1] to [0, 255]."


Thank you for all your help so far OrangyTang. + Points

Share this post


Link to post
Share on other sites
For a proper look it'll have to be hand created. But if you're going with the automatic conversion you'll likely get better results with proper weights. There was an IOTD a while back where someone did Quake 2 with automatically generated normalmaps (like this) which looked pretty good. This tends to work with pre-bumpmapping textures because they're drawn with the more raised areas brighter.

For conversion, your image/texture will probably be a 32bit texture, which gives you 8bits per colour. So each colour is in the range 0 to 255. Normals are usually in the range of -1 to 1 for each X/Y/Z value, so you have to do a bit of scaling and shifting to get the correct float->byte conversion. Something like:

R = (byte)((X+1f) / 128f);

Actually, since the Z/B component is always +itive your bumpmapping could do something sneaky and assume it was orignally [0,1] and get more precion. But thats probably not going to give you much of a visual difference.

Share this post


Link to post
Share on other sites
Okay, I'll do a search for the topic. I'll mess around with the properties and scaling some more as well.

I'm so close though, here's a picture illustrating the problem.

[img src="http://www.curvedbasic.com/normalproblem.JPG"]

:)

Share this post


Link to post
Share on other sites
Okay, to generate my height map, I'm just trying to enhance/exaggerate each color after it's been made into a greyscale. So any color greater than 128 rgb will become whiter, while colors less than that will become darker. This will be perfect for objects that aren't complex, but I may end up having users to just draw over dark areas, or have some kind of replace color function.

I'm still open to all links on 2D normal map generation and height map alogrithms.

Thanks.

Share this post


Link to post
Share on other sites
Here is a thread about generationg normals from heightmap that explains the use of sobel operator. This is the same way nVidia plugin works.

And here is a sample code using it:

// converts heightmap to normalmap (bumpmap)
void CImageTools::ConvertHeightMapToNormalMap( CImage &heightMap, CImage &normalMap, const float strength ) {
// error checking
if ( heightMap.GetDepth() != 1 ) {
Log << "<!> Cant convert heightmap to normalmap : invalid input image depth\n";
return;
}
const unsigned int width = heightMap.GetWidth();
const unsigned int height = heightMap.GetHeight();
normalMap.Set( width, height, 3 );

const float normY = 256.0f / strength;
Math::CVector3 normal;

unsigned int pos = 0;
for ( unsigned int y=0; y<height; ++y ) {
for ( unsigned int x=0; x<width; ++x ) {
normal.Set( 0.0f, normY, 0.0f );
normal.x -= static_cast<float>( heightMap.data[ heightMap.GetDataPos( x-1, y ) ] );
normal.x += static_cast<float>( heightMap.data[ heightMap.GetDataPos( x+1, y ) ] );
normal.z -= static_cast<float>( heightMap.data[ heightMap.GetDataPos( x, y-1 ) ] );
normal.z += static_cast<float>( heightMap.data[ heightMap.GetDataPos( x, y+1 ) ] );
normal.NormalizeFast();
normalMap.data[ pos++ ] = Math::FloatToByte( normal.x * 127.0f + 128.0f );
normalMap.data[ pos++ ] = Math::FloatToByte( normal.z * 127.0f + 128.0f );
normalMap.data[ pos++ ] = Math::FloatToByte( normal.y * 127.0f + 128.0f );
}
}
normalMap.SetDataFormat( GL_RGB, GL_RGB );
}


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