Normal Mapped Terrains
by Matt Vitelli · in Torque Game Engine Advanced · 12/08/2006 (3:15 pm) · 58 replies
This is from some work I've done with normal mapping and terrains. Enjoy!




#42
Has anyone tried doing terrains as a .dts utilizing the recent polysoup stuff? I'd imagine it would be awful performance-wise, but pretty cool to have that much control over the visual fidelity.
05/22/2008 (5:58 pm)
... you know what, scratch that. If it's still impossible to adjust the scale for textures placed on legacy terrain it would look terrible.Has anyone tried doing terrains as a .dts utilizing the recent polysoup stuff? I'd imagine it would be awful performance-wise, but pretty cool to have that much control over the visual fidelity.
#43
05/22/2008 (7:44 pm)
Well, doing things like per-terrain material effects were really easy back in TGEA 1.0-1.3. You had direct access to blended opacity info, which was quite spiffy. With this new clipmapping system though, terrains are setup so differently that I'm having trouble making sense of it. Part of the issue is that the base materials are loaded up differently from the other materials. Might be worth looking into to see if that can be changed. Think of all the neat things that could be done if terrain materials were linked to the matInstance.
#44
You said earlier that getting normal mapping working on Atlas would be fairly easy and I recall Ves having done it in his "atlas bumpmapping" resource. I tried some parts of his resource in TGEA 1.7 Atlas2 and added the following definition to the vertData in atlas.h:
float3 normal : NORMAL; // Vertex normal
While it compiles all right all the normals seem to have exactly the same values: x = 0.0, y = 0.0, z = 1.0.
Have I misunderstood something or is there a good explanation for that?
05/28/2008 (12:49 pm)
@Matt:You said earlier that getting normal mapping working on Atlas would be fairly easy and I recall Ves having done it in his "atlas bumpmapping" resource. I tried some parts of his resource in TGEA 1.7 Atlas2 and added the following definition to the vertData in atlas.h:
float3 normal : NORMAL; // Vertex normal
While it compiles all right all the normals seem to have exactly the same values: x = 0.0, y = 0.0, z = 1.0.
Have I misunderstood something or is there a good explanation for that?
#45
06/05/2008 (8:06 am)
The terrain system has radically changed. You have to make some small changes to the detail shaders and pass a light vector to it. I've implemented this in the new TGEA using the existing detail texture system. I'll post my code later.
#46
06/05/2008 (8:28 am)
Looking forward to that Matt.
#47
Great Job
06/05/2008 (9:25 am)
That amazing can't wait to test this out also can you post some screenshoots if you have free time.Great Job
#48
06/07/2008 (9:29 am)
I very much am interested in integrating this for my project :).
#49
@AtlasImportOldChu.cc around line 180:
adm.mPos = curChunk.pos;
adm.mPosMorphOffset = curChunk.morph;
adm.mTex = curChunk.texCoord;
// Set some defaults for morph and normal data.
adm.mTexMorphOffset = new Point2F[curChunk.vertCount];
adm.mNormal = new Point3F[curChunk.vertCount];
for(S32 i=0; i
{
adm.mTexMorphOffset[i].set(0,0);
adm.mNormal[i].set(0, 0, 1);
}
The code above is used to import the terrain vertexes from the old-format atlas chunks which in turn are generated from a RAW-file by using atlasOldGenerateChunkFileFromRaw16 -- this is the only way I currently know of turning a heighfield into a atlas terrain.
Could someone from the GG elaborate a bit why all the normals are being set to [0,0,1]? Are they supposed to be in some sort of vertex-specific coordinate space (yeah, I do actually suck at 3D math and shaders so this could be the case for all I know) or is the functionality to generate them simply a work-in-progress? The atlasOldHeightField.cc does have a "generateNormals()" method defined but is never used anywhere.
06/20/2008 (10:41 am)
I returned to subject of terrain normals being always the same (see above) and browsed through the code a little. It seems like the terrain normals are in fact set to [0,0,1] for all terrain vertexes when using the AtlasImportOldChu:@AtlasImportOldChu.cc around line 180:
adm.mPos = curChunk.pos;
adm.mPosMorphOffset = curChunk.morph;
adm.mTex = curChunk.texCoord;
// Set some defaults for morph and normal data.
adm.mTexMorphOffset = new Point2F[curChunk.vertCount];
adm.mNormal = new Point3F[curChunk.vertCount];
for(S32 i=0; i
adm.mTexMorphOffset[i].set(0,0);
adm.mNormal[i].set(0, 0, 1);
}
The code above is used to import the terrain vertexes from the old-format atlas chunks which in turn are generated from a RAW-file by using atlasOldGenerateChunkFileFromRaw16 -- this is the only way I currently know of turning a heighfield into a atlas terrain.
Could someone from the GG elaborate a bit why all the normals are being set to [0,0,1]? Are they supposed to be in some sort of vertex-specific coordinate space (yeah, I do actually suck at 3D math and shaders so this could be the case for all I know) or is the functionality to generate them simply a work-in-progress? The atlasOldHeightField.cc does have a "generateNormals()" method defined but is never used anywhere.
#51
08/06/2008 (7:59 pm)
That is amazing! Please confirm this will be in a update :-D
#52
08/06/2008 (8:30 pm)
Hmm that's a feature that might make me consider moving back to Legacy terrain. Pretty darn cool.
#53
08/07/2008 (12:47 am)
Sickhead's doing some really cool stuff. I had something similar working in 1.3, but this looks very spiffy. I'm curious about how they're handling some of Legacy's problems, such as the screwed up normals (needed for the eye vector and light vector calculations) and gathering opacity info. Very neat stuff
#54
I can't talk about the project, but i can talk a little about the tech.
Its a completely rewritten TerrainBlock geometry engine. I builds a couple of big static VBs broken into a few cells just for culling. There is no lod... it just renders it... so its fast... over 100fps. I may look at some discreet lod later if i get a little time, but so far it doesn't look needed.
I still use the clipmap for the base diffuse pass w/ lightmap. This gives us visible detail in the distance as well as breaking up the pattern on the detail maps.
We now have per-layer detail maps with optional normal/parallax maps and side projection (think cliffs). We render only the closest cells to the camera one or more times depending on the shader model and how many layers affect that particular cell.
@Matt - Not sure what you mean with the screwed up normals exactly.
I pass only the xy components of the normal and binormal in the vertex to the shader to save space. In the shader i derive the z value ( z = 1.0 - x + y ) and use a cross product with the normal for the tangent.
The opacity data i write from system memory into a texture at startup (or when the opacity changes). If you have less than 4 layers i only have a single opacity... if you have 8 i build two. I've been thinking about stuffing it into a single integer 64bit texture, but i don't know if its worth the trouble. Anyway... i then sample the correct opacity channel in the additive detail passes.
08/07/2008 (1:57 am)
Hey guys.I can't talk about the project, but i can talk a little about the tech.
Its a completely rewritten TerrainBlock geometry engine. I builds a couple of big static VBs broken into a few cells just for culling. There is no lod... it just renders it... so its fast... over 100fps. I may look at some discreet lod later if i get a little time, but so far it doesn't look needed.
I still use the clipmap for the base diffuse pass w/ lightmap. This gives us visible detail in the distance as well as breaking up the pattern on the detail maps.
We now have per-layer detail maps with optional normal/parallax maps and side projection (think cliffs). We render only the closest cells to the camera one or more times depending on the shader model and how many layers affect that particular cell.
@Matt - Not sure what you mean with the screwed up normals exactly.
I pass only the xy components of the normal and binormal in the vertex to the shader to save space. In the shader i derive the z value ( z = 1.0 - x + y ) and use a cross product with the normal for the tangent.
The opacity data i write from system memory into a texture at startup (or when the opacity changes). If you have less than 4 layers i only have a single opacity... if you have 8 i build two. I've been thinking about stuffing it into a single integer 64bit texture, but i don't know if its worth the trouble. Anyway... i then sample the correct opacity channel in the additive detail passes.
#55
In terms of damaged normals, there's an example:
08/07/2008 (8:33 am)
Tom, in terms of side projection, are you referring to a side-projection heightmap for geometry manipulation or side-projected textures for non-stretched UVs? This sounds very interesting.In terms of damaged normals, there's an example:
#56
I'd also be very interested in using side projected textures for non-strecthed UVs (unless it is something as restricting as using e.g. x/z-axises for defining texture coordinates instead of x/y -- because it has limited functional angle).
I also tried a quick mock-up of a cylindrical projection for vertical textures on blended atlas terrain using the vertex normal and a dot product but that was foiled by the fact that atlas has no vertex normals (see my previous post).
Oh and ...
@Tom: no offense, but was there some sort of abstraction on that formula to calculate the z-component, because [z = 1.0 + x -y] doesn't make any sense to me (infact I tried to minimize the texture fetches on a SM 2.0 version of the "8 textures on atlas" resource enhanced with 8 normal maps with their x/y components stored on 4 DXT5 compressed textures and regenerating the z-component requires a square root operation).
08/07/2008 (12:25 pm)
Hi.I'd also be very interested in using side projected textures for non-strecthed UVs (unless it is something as restricting as using e.g. x/z-axises for defining texture coordinates instead of x/y -- because it has limited functional angle).
I also tried a quick mock-up of a cylindrical projection for vertical textures on blended atlas terrain using the vertex normal and a dot product but that was foiled by the fact that atlas has no vertex normals (see my previous post).
Oh and ...
@Tom: no offense, but was there some sort of abstraction on that formula to calculate the z-component, because [z = 1.0 + x -y] doesn't make any sense to me (infact I tried to minimize the texture fetches on a SM 2.0 version of the "8 textures on atlas" resource enhanced with 8 normal maps with their x/y components stored on 4 DXT5 compressed textures and regenerating the z-component requires a square root operation).
#57
Putting in normal information into the clipmapper also was quite easy, but then doing the normalmap pass in the dynamic light shader wasn't, and took too long to be worth the hassle.
So we tried to port 1.0.3's TerrainBlock to 1.7 but that was time-consuming with all the changes to Render Targets and the blender.
Take a look in terrData.h and look at the commented code for passing normals from the emit functions.
08/07/2008 (12:35 pm)
Getting normals/binormals/tangent information was trivial enough, and I'm not very well versed with TerrainBlock. We got normal mapping and cubemapping working with terrains, but that was in 1.0.3 and we later moved to 1.7 which was a very different beast with the now modified TerrainBlock class.Putting in normal information into the clipmapper also was quite easy, but then doing the normalmap pass in the dynamic light shader wasn't, and took too long to be worth the hassle.
So we tried to port 1.0.3's TerrainBlock to 1.7 but that was time-consuming with all the changes to Render Targets and the blender.
Take a look in terrData.h and look at the commented code for passing normals from the emit functions.
#58
@Henri - Your right... i should have written z = sqrt( 1.0 - (x*x + y*y) ).
The side projection is for non-stretch uvs. And by side i mean projection on both the x and y axis planes.
I experimented with several techniques to get side projection with a single texture sample... this now seems impossible to do without CPU work.
My most promising was a spherical projection.
The big issue is you get places where the projection direction is perpendicular to the triangle normal... resulting in a smudge across that triangle. But... it does work via a single texture sample.
In the end i had to just burn two texture samples for side projected detail layers and blend between them based on the dot.
08/07/2008 (1:32 pm)
@Matt - Hum... i haven't seen that, but i also haven't rendered the normals to the scene. I'll give that a try and report back, but i'm fairly certain its working.@Henri - Your right... i should have written z = sqrt( 1.0 - (x*x + y*y) ).
The side projection is for non-stretch uvs. And by side i mean projection on both the x and y axis planes.
I experimented with several techniques to get side projection with a single texture sample... this now seems impossible to do without CPU work.
My most promising was a spherical projection.
float3 xyz = IN.position.xyz - float3( 256, 256, 70 ); // coord relative to terrainblock 3D center. tcoord = float2( length( xyz.xy ), IN.position.z ) / terrScale;
The big issue is you get places where the projection direction is perpendicular to the triangle normal... resulting in a smudge across that triangle. But... it does work via a single texture sample.
In the end i had to just burn two texture samples for side projected detail layers and blend between them based on the dot.
Torque Owner Jordan
Of course normal maps may be a bit overkill, depending on the application, but a simple grayscale bumpmap would be excellent, along with specularity options. You'd probably get artifacts trying to blend different RGB normal maps on the legacy vertices.