Friday, May 20, 2005

Normal Map seams in 3ds Max

There is a bug that creates lighting seams at UV borders when using normal maps and .fx shaders in 3ds Max 7. I created a sample file to show the results of the bug. You can download it here.

This file uses HLSLnormal_map_specular.fx for the DirectX 9 shader. This shader and the required textures are also included with this zip file. More information about the shader is available here.

Three test cases are shown in the sample file. The first test case (the cylindar on the left) uses a blank normal map - meaning that all of the pixels in the normal map are (127,127,255) - so all of the normals should be pointing straight off the surface. The cylindar has a cylindrical projection UV map applied. In the final release version of Max 7 (7.0 SP1), a seam is clearly visible running from the top to the bottom of the cylindar along the edge where one side of the UV map meets the other. This seam should not occur since the faces on either side of the seam share the same smoothing group and the pixels in the normal map are all exactly the same color.

In the second test case, I'm using a more complex normal map with the same texture coordinates. The same sceam exists. This seam is not caused by the normal map because if I apply the same normal map to a flat plane and tile it, there is no seam.

In the last test case, bot the cylindar and the sphere are face mapped, so every quad on the cylindar and sphere have UV coordinates from zero to one. The seams (facets) are clearly visible on both the cylindar and the sphere - but they shouldn't be there because all those faces share smoothing groups.

I know that this is a bug because if I open the sample file in the Beta version of Max 7 (version 57), there is no seam. Here's what it looks like in Max 7 Beta:

No Seams in Max 7 Beta

And here's what it looks like in Max 7 Final version with the seams:

Normal Map Seams in Max 7 Final

I think that this bug occurs because the smoothing groups are crossing UV borders. This means that each vertex in max has to be split into two verts to send to the graphics card. To handle this case correctly both verts would have to share the same normal but for some reason, the verts on both sides of the seam are not unified.

If you add a "NormalBump" map to a standard max material and do a software render, there is no seam, so this bug only applies to the real-time viewport display.

This same bug can also be observed when using the two normal map .FX files that are shipped with 3ds Max, DiffseBump.fx and RTTNormalMap.fx, although these shaders yield incorrect lighting - so I didn't use them for this test case.

If you have more infomation about this bug, post it here.


Blogger Ben Cloward said...

I submitted this bug to Discreet and this is the reply that I got:

"it’s actually not a bug , it’s the way your Mapping is done , all the quads should not overlap each other , for exemple , in the game industry , the mappers will use symmetry to save some space on the bitmap to get a better quality , but with normal mapping it can’t be done , all quads should have their own UV coordinates ,instead of using faces as the mapping type , use box or the unwrapUVW.

Thank you for you submission."

He didn't understand the bug. The way the mapping is applied isn't the issue. The issue is that there is a seam at the edge of UV borders - even if all the polygons share a single smoothing group and the normal map has no seams.

May 20, 2005 7:31 PM  
Anonymous Anonymous said...

Well done for noticing, pursuing and documenting this so clearly. Its definitely worth knowing about.

May 22, 2005 11:27 AM  
Blogger Ben Cloward said...

Thanks! Now if we can just get Discreet to fix it . . .

May 22, 2005 5:56 PM  
Anonymous Anonymous said...


As a programmer, i think this problem is because of the way they are generating tangent space information on the model.
A common mistake is to generate tangent information after breaking vertices that have more than one uv information.
So by doing each resulting "border" vertex (at the same position) dont have the same normal. The normal should be the average of both side faces, but in that case, only one face is used.
So the computation of lighting is not the same on each side of the border.
THe point is to generate tangent space information by using all the needed faces connected to the vertex (but take care about the smoothing group :-) )

Hope i've could help for this problem understanding

May 28, 2005 1:55 AM  
Blogger Ben Cloward said...


Very well put! That is exactly what the problem is. You've summerized it perfectly. It's nice to have your perspective here. As a programmer you obviously have a much deeper understanding of the topic so I appreciate your input.

Very nice game engine, by the way! I took a look at your kjAPI and I was very impressed! Does it read FX format shaders?

May 28, 2005 8:54 AM  
Anonymous Anonymous said...

kjAPI supports Cg shaders.
that means that vertex and pixel shaders are separated files.
We use Cg because it is cross-API (DirectX & OpenGL).
We plan to use Fx in a near future. (could be done as cross-API way if CgFX would be less buggy... ;-) )

May 29, 2005 9:05 PM  
Blogger Ben Cloward said...

I started learning shaders by writing them in Cg but Nvidia seems to have stopped supporting it. There aren't any new tools for it and they'll really been pushing their FX composer app which is designed for HLSL, so I switched over and now all my shaders are in HLSL.

I really like Cg, however, and I'm hoping that they'll release a bunch of new Cg tools now that all PS3 graphics will be using Cg.

I also like using the FX format (for both HLSL and Cg) because it's a nice clean package for all of the elements that contribute to an effect. I mainly use it because I use 3ds Max as my platform to render my shaders and that's what Max supports. I don't have the know-how (like you do) to write all of the supporting code required to for a graphics engine - so I just use Max for that. Good luck getting CgFX to work in your engine!

May 30, 2005 10:53 PM  
Anonymous Anonymous said...

Thanks for the textures, you have some of the best collection of snow textures I've found!

June 01, 2005 9:55 AM  
Anonymous Anonymous said...

Great site, our team are soon going next generation, so your normal mapping tuts are a god send!

June 18, 2005 5:55 AM  
Blogger Ben Cloward said...


Is the new version of Cg that Nvidia just posted any better for you?


Glad you like the tutorial! Is there anything else that you'd like to know about next-gen developement that I could add?

June 20, 2005 9:26 PM  
Anonymous Anonymous said...


Well, i've just installed it. It seems that they've made lots of effort on CgFX that's good.
Since we are focusing on the level editor for the moment, i'll go back to shaders later :)
But we have plan to add .fx(so also CgFX) support. :)

July 27, 2005 4:14 AM  
Anonymous Anonymous said...

Hi I used your texture.

August 14, 2005 7:39 AM  
Blogger Ben Cloward said...

I think it's important that I add an update here. The bug that I wrote about in my original blog post has been completely fix in 3DS Max 8. Way to go, Autodesk! They've also fixed some other normal mapping issues. Especially cool is that you can now mirror your UV coordinates and they'll be lit correctly. Nice!

November 03, 2005 4:10 PM  
Anonymous Anonymous said...

Here's a simple solution:

November 08, 2008 4:37 AM  

Post a Comment

<< Home