Sign in to follow this  
cheese

Does anyone use cubic normal maps?

Recommended Posts

I am trying to implement lighting in my planet renderer, using dot3 normal mapping. I am trying to store the normal map as a cube texture. The terrain is generated procedurally using Perlin Noise, and so the normal map will also be generated procedurally. I am trying to generate the normal map as follows: 1) Generate a cubic heightmap, by sampling the noise function at every pixel of the cube map. 2) Generate the normalmap from the heightmap (the resultant normal map will be in tangent space) 3) Convert the normalmap to object space by rotation I have succesfully implemented stage 1 - to test the heightmap, I applied the texture to the planet and all faces of the cube matched up seamlessly. However, I am having great problems with stage 2 - converting the heightmap to a normalmap. I have managed to generate each face of the normal cube map, and they look correct, however they do not match up seamlessly when applied to the planet. Here's a screenshot of what I mean: The problem is definitely not the heightmap. I think that the problem may be that I need to reverse the way in which the normals are encoded, for certain faces of the cube. For example, at the moment, I used do the following for EVERY face: color.red = ((normal.x + 1) / 2) * 255; color.green = ((normal.y + 1) / 2) * 255.0f; color.blue = ((normal.z + 1) / 2) * 255.0f; And maybe I should be doing something like the following: color.red = ((-normal.x + 1) / 2) * 255; color.green = ((-normal.y + 1) / 2) * 255.0f; color.blue = ((-normal.z + 1) / 2) * 255.0f; Does anyone else store normals in cube maps, or generate their own cubic normal maps procedurally. Any help that you may be able to give me would be eternally appreciated - its driving me nuts. Thanks in advance.

Share this post


Link to post
Share on other sites
I'm doing the same thing for planets.

I don't know the solution to your problem at the moment, but maybe I can give you some hints.

1) Did you guarantee that the height map is smooth at the seems? What I mean is: When you go from one cube face to the next, does the heightmap have a kink there?
If you have used a 2D noise generator, then your heightmap is by sure not smooth at the seems. This would explain the artifacts. Use a 3D noise generator. As input coordinates use the 3D coordinates of the tesselated cube faces.

2) How do the seems look when you disable the decal texture and set all heights to zero? If all heights are zero, the planet should look like a perfect sphere. If this is not the case, you've got "space problems", that means that there is an error in the tangent space rotation business. In this case, you could generate the normals directly in object space like this:


for each cube face f
// Height computation
for each vertex v of f
compute height h with 1+small factor*perlin3d(v)
normalize v to length h // This projects v on the sphere to a height h, where height 1 means "sea level"

// Normal computation
Set all vertex normals to zero

for each triangle (v1,v2,v3) of face f
Compute face normal from the 3 vertices v1,v2,v3
Add face normal to vertex normals n1, n2, n3

Normalize all vertex normals
Store vertex normals in a normal map



Share this post


Link to post
Share on other sites
I think the problem is caused by the fact that the normal map is effectively the 1st derivative of the height map. The normal map is effectively the curvature of the surface, i.e. the first derivative of the height map. While the height maps may be seamless this does not imply that the normal map will be, the effective curvature at the seams between the height maps may not be continuous.

\______


The two lines above meet at a point, but their curvature does not match. If I remember the terminology correctly, they have C0 continuity, but not C1 or above. My tip would be to read up on spline/bezier curves and surfaces, particularly focusing on articles that discuss joining curves and surfaces to form seamless wholes. You should come across the concepts I mentioned here and they will probably be explained much better.

(edit: ascii art getting chopped :( Did there used to be a preview button? Or am I imagining things....)

Share this post


Link to post
Share on other sites
Thankyou very much for your replies.

Lutz, in reply to your hints..

1) Yes, I checked the cube heightmap both by applying it to the planet, and by tesselating it in a paint application. I use 3D normalized coordinates to input to the noise function

2) I tried what you said, and it does indeed look like a perfect sphere. I am pretty sure that using the pseudocode you posted would solve my problems, however it is infeasible. My LOD scheme is based on chunked level-of-detail using triangular patches, so I didn't know how to calculate the normals of vertices on the edge of patches, because I don't store pointers between neighbouring triangles, that belong to different patches. How do you go about calculating the normals of vertices on the edge of patches?

Another quick question Lutz - what size is your cubic normalmap? Also, once you have calculated the vertex normals by averaging the normals of the triangles, do you interpolate between these to fill in the normal map?

paul, I think you've identified the problem - it fits exactly with the symptoms I am getting. Unfortunately, I know very little about bezier/spline surfaces, though I will certainly follow your recommendation if I can find any relevant articles.

Good news, I think I've found a reasonable solution to the problem, although it is a bit of a hack. I first generate a spherical heightmap, and then generate a spherical normalmap in object space from this heightmap. I then create a cubic normalmap by sampling the spherical normalmap, which gives the normals in object space, with no visible cube seams. The problem with this is that the normal appears to be a lot more pixelly, and I'm aiming to overcome this at the moment.

Thanks again for your input.

Share this post


Link to post
Share on other sites
Quote:
Original post by cheese
1) Yes, I checked the cube heightmap both by applying it to the planet, and by tesselating it in a paint application. I use 3D normalized coordinates to input to the noise function


OK, so it's probably not the normal map generation.

Quote:

2) I tried what you said, and it does indeed look like a perfect sphere. I am pretty sure that using the pseudocode you posted would solve my problems, however it is infeasible. My LOD scheme is based on chunked level-of-detail using triangular patches, so I didn't know how to calculate the normals of vertices on the edge of patches, because I don't store pointers between neighbouring triangles, that belong to different patches. How do you go about calculating the normals of vertices on the edge of patches?

Another quick question Lutz - what size is your cubic normalmap? Also, once you have calculated the vertex normals by averaging the normals of the triangles, do you interpolate between these to fill in the normal map?


You're right. I didn't notice that problem when I wrote the pseudo-code. My normalmap is of size 1024. I actually don't compute the normals in all vertices, but I compute normals for each quad, like this:

o o o o o
x x x x
o o o o o
x x x x
o o o o o
x x x x
o o o o o
x x x x
o o o o o

The 'o's are the vertex positions (resp. the positions where I compute the terrain height) and the 'x's are the positions where I compute the normals. The normals are computed from the 3 'o's top-left, bottom-right and bottom-left of each 'x'. That's not perfectly correct and the normal map does not cover the whole cube face (1/2 pixel error at the borders), but choosing the right texture clamp mode and texture coordinates you don't see any difference. And it's faster than computing vertex normals.

When you are close enough to the planet so that the 1/2 pixel error would become visible, I switch to the LOD mode anyway. I use clipmaps instead of chunked LOD. The clipmaps have their own normal maps which are seemlessly blended between different LOD levels and replace the global normal map.

Share this post


Link to post
Share on other sites
Thanks again for your reply Lutz - with your help, I think I've finally found a solution.

It works as follows:

For the current vertex, I generate four surrounding vertices on the planet surface in object space. I do this using spherical coordinates, and the closeness of the vertices depends on the resolution of the cube map.

I use these vertices to generate four small triangles, each of which share the current vertex. I then do what you do - average the surface normals of these triangles, to get the normal of the current vertex in object space.

Its a pretty long-winded method, and there's loads of room for optimisation in my implementation, but the lighting looks great, even for cube maps of low resolution.

And, it'll work with any subdivision method or LoD scheme.

Thanks again for your continuing help Lutz - it's really helped.


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