Jump to content

  • Log In with Google      Sign In   
  • Create Account


2D normal map generation


Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.

  • You cannot reply to this topic
13 replies to this topic

#1 Basic   Members   -  Reputation: 128

Like
0Likes
Like

Posted 05 June 2005 - 04:40 AM

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)

Sponsor:

#2 OrangyTang   Members   -  Reputation: 1294

Like
0Likes
Like

Posted 05 June 2005 - 05:07 AM

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.

#3 Basic   Members   -  Reputation: 128

Like
0Likes
Like

Posted 05 June 2005 - 05:17 AM

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?

:)

#4 OrangyTang   Members   -  Reputation: 1294

Like
0Likes
Like

Posted 05 June 2005 - 05:38 AM

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].

#5 Basic   Members   -  Reputation: 128

Like
0Likes
Like

Posted 05 June 2005 - 05:46 AM

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?

#6 Basic   Members   -  Reputation: 128

Like
0Likes
Like

Posted 05 June 2005 - 08:11 AM

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.



#7 OrangyTang   Members   -  Reputation: 1294

Like
0Likes
Like

Posted 05 June 2005 - 08:16 AM

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.

#8 Basic   Members   -  Reputation: 128

Like
0Likes
Like

Posted 05 June 2005 - 08:23 AM

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.

#9 Basic   Members   -  Reputation: 128

Like
0Likes
Like

Posted 05 June 2005 - 08:34 AM

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

#10 OrangyTang   Members   -  Reputation: 1294

Like
0Likes
Like

Posted 05 June 2005 - 08:35 AM

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.

#11 Basic   Members   -  Reputation: 128

Like
0Likes
Like

Posted 05 June 2005 - 08:46 AM

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"]

:)

#12 Basic   Members   -  Reputation: 128

Like
0Likes
Like

Posted 05 June 2005 - 10:58 AM

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.

#13 _DarkWIng_   Members   -  Reputation: 602

Like
0Likes
Like

Posted 05 June 2005 - 08:58 PM

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 );
}



You should never let your fears become the boundaries of your dreams.

#14 Basic   Members   -  Reputation: 128

Like
0Likes
Like

Posted 06 June 2005 - 08:39 AM

Thank you very much, this is exactly what I'm looking for to help me.

:)




Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.



PARTNERS