GL_TEXTURE_3D and mipmapping
I've implemented the alt-grad mapping for terrain described in the chapters on "RealWorldz" at the end of the Orange Book. Alt-Grad mapping is super simple, where you have a 3d surface texture for your terrain, and the z-value for the texture coordinate lookup is calculated by looking into a greyscale texture, indexing x by the surface gradient ( 1 - normal.z ) and the y value is the altitude.
It works great -- in principle -- except that mipmapping of my 3d texture causes the effect to be lost completely, since the depth collapses from ( in my case 4 layers to 1 ) as the mipmapping increases. When mipmapping is disabled for the texture, it looks correct, but looks like hell because of the aliasing artifacts that happen when you disable mipmapping.
Here's a couple screenshots to illustrate:
First, with mipmapping disabled -- you can see various different surface textures as one would expect:
![[Image: Screenshots-2006-08-12-14.png]](http://zakariya.net/shamyl/Screenshots/Screenshots-2006-08-12-14.png)
Second, with mipmapping enabled. All the surface textures collapse into one, with mottled tone.
![[Image: Screenshots-2006-08-12-15.png]](http://zakariya.net/shamyl/Screenshots/Screenshots-2006-08-12-15.png)
As you'd expect, when you approach closely, the mipmapping decreases and it looks OK, but only when you're close enough, which basically ruins the whole effect.
My question is whether there's a way to tell gl to *not* mipmap the depth of the 3d texture. E.g., mipmap S & T, but not R.
Is there such a way?
If there isn't, I can drop 3d textures for four 3d textures and have GLSL calculate per-texture weightings, but I like the cleanliness of the 3d texture approach, it's really slick and makes it easy to support as many or as few depths as you want ( so long as it's a power of two, obviously ).
Thanks,
P.S. I also implemented mushrooming ( from the same chapter in the orange book ), where vanilla heightmapped terrain can have overhangs and other protruberances. I also implemented ambient occlusion in my lightmap baking... I love it!
It works great -- in principle -- except that mipmapping of my 3d texture causes the effect to be lost completely, since the depth collapses from ( in my case 4 layers to 1 ) as the mipmapping increases. When mipmapping is disabled for the texture, it looks correct, but looks like hell because of the aliasing artifacts that happen when you disable mipmapping.
Here's a couple screenshots to illustrate:
First, with mipmapping disabled -- you can see various different surface textures as one would expect:
![[Image: Screenshots-2006-08-12-14.png]](http://zakariya.net/shamyl/Screenshots/Screenshots-2006-08-12-14.png)
Second, with mipmapping enabled. All the surface textures collapse into one, with mottled tone.
![[Image: Screenshots-2006-08-12-15.png]](http://zakariya.net/shamyl/Screenshots/Screenshots-2006-08-12-15.png)
As you'd expect, when you approach closely, the mipmapping decreases and it looks OK, but only when you're close enough, which basically ruins the whole effect.
My question is whether there's a way to tell gl to *not* mipmap the depth of the 3d texture. E.g., mipmap S & T, but not R.
Is there such a way?
If there isn't, I can drop 3d textures for four 3d textures and have GLSL calculate per-texture weightings, but I like the cleanliness of the 3d texture approach, it's really slick and makes it easy to support as many or as few depths as you want ( so long as it's a power of two, obviously ).
Thanks,
P.S. I also implemented mushrooming ( from the same chapter in the orange book ), where vanilla heightmapped terrain can have overhangs and other protruberances. I also implemented ambient occlusion in my lightmap baking... I love it!
Don't you specify the filter function per axis?
edit: Never mind. I guess that's the repeat function.
edit: Never mind. I guess that's the repeat function.
Scott Lembcke - Howling Moon Software
Author of Chipmunk Physics - A fast and simple rigid body physics library in C.
Skorche Wrote:Don't you specify the filter function per axis?
edit: Never mind. I guess that's the repeat function.
I'm actually hoping that I can disable mipmapping for just the R axis. I've googled a bit, but can't figure out a way. I don't know, either, if manually generating my own mipmaps will let me prevent the collapse of the R axis.
No, TEXTURE_3D must reduce mipmap dimensions in S,T,R.
What you want is an extension like MESAX_texture_stack. But no current hardware supports this.
What you want is an extension like MESAX_texture_stack. But no current hardware supports this.
Aaarg! That's exactly what I want.
Looks like I'll be using GLSL and separate textures. Thanks for the tip, arekkusu.
Looks like I'll be using GLSL and separate textures. Thanks for the tip, arekkusu.
That looks amazing! What is this mushrooming?
Sir, e^iπ + 1 = 0, hence God exists; reply!
unknown Wrote:That looks amazing! What is this mushrooming?
I would guess it's the ability of the terrain to go more than vertical. (like a mushroom)
Scott Lembcke - Howling Moon Software
Author of Chipmunk Physics - A fast and simple rigid body physics library in C.
uh yeah.. me too, I was kind of hoping for a more detailed description.
Sir, e^iπ + 1 = 0, hence God exists; reply!
My implementation of mushrooming is far from perfect, you have to be careful not to mushroom too much or the terrain can go a little degenerate. I'll post the code, it ought to work on any heightmapped terrain.
Basically, it just pushes the geometry out along its normal based on a function of height. Simple enough...
Basically, it just pushes the geometry out along its normal based on a function of height. Simple enough...
OK, mushrooming code. It's pretty well commented, so it ought to make sense. One thing, though, is that the second method -- Terrain::mushroomRelax -- works but definitely could be better. All it does right now is try to make certain that windings don't break too badly.
As I mentioned earlier, my mushroomRelax method isn't any good, and probably causes more problems than it solves.
Code:
struct TerrainHeightComparator
{
vec3 *vertices;
TerrainHeightComparator( vec3 *Vertices ) : vertices(Vertices){}
inline bool operator()( int indexA, int indexB )
{
return vertices[ indexB ].z > vertices[ indexA ].z;
}
};
void Terrain::mushroom( void )
{
if ( _mushrooming < EPSILON ) return;
const int passes = 20;
float mushroomingChunk = _mushrooming / float( passes );
float worldExtent = _size * 0.5f;
float worldExtentThickness = 1000.0f;
float relaxationAmount = 1.0f / float( passes );
/*
Process
Note: mushrooming is applied as a function of height.
For each vertex, extend its position along its normal by the
vertex's z value / maxHeight times the mushrooming factor
for that altitude.
Notes:
1) Instead of indexing linearly, we must created an index
array sorted by height, so we can apply distortion *up* the
terrain.
2) We're doing this in multiple passes, performinga "relaxation"
of the geometry after each mushrooming iteration to try to
prevent collapsing triangles.
3) We also linearly decrease the mushrooming factor as we approach
the ends of the geometry, since the mushrooming algorithm creates
very noticable weirdness at the edges ( since there's nothing to
connect to ).
*/
/*
Create a copy of the original vertices, we need this
for the correction of degenerate triangles
*/
std::vector< vec3 > originalVertices;
originalVertices.resize( _numVertices );
for ( int i = 0; i < _numVertices; i++ ) originalVertices[i] = _vertices[i];
/*
Create storage for indices sorted by height
*/
std::vector< int > indicesByHeight;
indicesByHeight.resize( _numVertices );
float mushroomingZ = _maxHeight * _mushroomingAltitude;
for ( int i = 0; i < passes; i++ )
{
/*
Copy over indices and sort them by height
*/
for ( int i = 0; i < _numVertices; i++ ) indicesByHeight[i] = i;
std::sort( indicesByHeight.begin(), indicesByHeight.end(), TerrainHeightComparator( _vertices ));
/*
Now, apply mushrooming per-vertex
*/
std::vector< int >::iterator it( indicesByHeight.begin() ), end( indicesByHeight.end() );
for ( ; it != end; ++it )
{
vec3 vertex = _vertices[ *it ],
normal = _normals[ *it ];
/*
Calculate x & y scaling factors which are scaled out by edge threshold
*/
float factor = 0;
if ( vertex.z > mushroomingZ )
{
factor = 1.0f - ( vertex.z - mushroomingZ ) / ( _maxHeight - mushroomingZ );
}
else
{
factor = 1.0f - ( mushroomingZ - vertex.z ) / mushroomingZ;
}
factor = clamp( powf( factor, _mushroomingHeightBias ), 0.0f, 1.0f );
float factorX = factor * mushroomingChunk,
factorY = factor * mushroomingChunk;
if ( vertex.x < -worldExtent + worldExtentThickness )
{
factorX *= 1.0f - clamp( ( vertex.x - (worldExtent + worldExtentThickness)) / worldExtentThickness, 0.0f, 1.0f );
}
else if ( vertex.x > worldExtent - worldExtentThickness )
{
factorX *= 1.0f - clamp( ( vertex.x - (worldExtent - worldExtentThickness)) / worldExtentThickness, 0.0f, 1.0f );
}
if ( vertex.y < -worldExtent + worldExtentThickness )
{
factorY *= 1.0f - clamp( ( vertex.y - (worldExtent + worldExtentThickness)) / worldExtentThickness, 0.0f, 1.0f );
}
else if ( vertex.y > worldExtent - worldExtentThickness )
{
factorY *= 1.0f - clamp( ( vertex.y - (worldExtent - worldExtentThickness)) / worldExtentThickness, 0.0f, 1.0f );
}
/*
Apply the mushrooming factor, and then perform a clamping sanity-check
*/
vertex += normal * vec3( factorX, factorY, _mushroomingCapping );
vertex.x = clamp( vertex.x, -worldExtent, worldExtent );
vertex.y = clamp( vertex.y, -worldExtent, worldExtent );
vertex.z = std::max( vertex.z, 0.0f );
/*
Finally, assign the vertex
*/
_vertices[ *it ] = vertex;
}
/*
After each mushrooming pass, update the normals, since the
mushrooming will have resulted in new geometry.
*/
calculateNormals();
}
/*
Perform relaxation
*/
for ( int i = 0; i < passes; i++ )
{
mushroomRelax( relaxationAmount, originalVertices );
}
/*
And one more normal calculation pass, since the
relaxation will have changed geometry.
*/
calculateNormals();
}
void Terrain::mushroomRelax( float correctiveAmount, const std::vector< vec3 > &originalVertices )
{
/*
Relaxation is performed per-triangle.
A triangle is considered to be OK if the normal of the new triangle
is still pointing roughly the same direction as the original, e.g.,
the dot-product is > 0, and the magnitudes of the edges are greater than
or equal to the original, where a triangle is as such, with AC the hypoteneuse.
A
| \
| \
B-- C
If the normal is still in the roughly same direction, just verify
that the lengths are >= to the originals, else perform correction
on the erroneous segment.
If the normal is reversed, perform correction on each segment.
Correction is defined as moving the offending vertices by
correctiveAmount to where they were before mushrooming was applied,
or in the case of shortened segments, in the direction necessary
to fix the collapse.
*/
for ( int i = 0; i < _numIndices; i += 3 )
{
int indexA = _indices[ i + 0 ],
indexB = _indices[ i + 1 ],
indexC = _indices[ i + 2 ];
vec3 originalA( originalVertices[ indexA ] ),
originalB( originalVertices[ indexB ] ),
originalC( originalVertices[ indexC ] ),
mushroomA( _vertices[ indexA ] ),
mushroomB( _vertices[ indexB ] ),
mushroomC( _vertices[ indexC ] );
vec3 originalNormal( vec3::normal( originalA, originalB, originalC )),
mushroomNormal( vec3::normal( mushroomA, mushroomB, mushroomC ));
/*
Verify normal
*/
if ( dot( originalNormal, mushroomNormal ) < 0.0f )
{
/*
Winding broke, move vertices halfway to original locations.
*/
_vertices[ indexA ] = lrp( correctiveAmount, mushroomA, originalA );
_vertices[ indexB ] = lrp( correctiveAmount, mushroomB, originalB );
_vertices[ indexC ] = lrp( correctiveAmount, mushroomC, originalC );
}
else
{
/*
Still facing same direction, so check distances
*/
float originalAB( originalA.distance( originalB )),
originalBC( originalB.distance( originalC )),
originalCA( originalC.distance( originalA )),
mushroomAB( mushroomA.distance( mushroomB )),
mushroomBC( mushroomB.distance( mushroomC )),
mushroomCA( mushroomC.distance( mushroomA ));
if ( originalAB > mushroomAB )
{
vec3 dir = mushroomA - mushroomB;
dir.normalize();
vec3 newA = mushroomB + dir * originalAB;
dir = mushroomB - mushroomA;
dir.normalize();
vec3 newB = mushroomA + dir * originalAB;
_vertices[ indexA ] = lrp( correctiveAmount, mushroomA, newA );
_vertices[ indexB ] = lrp( correctiveAmount, mushroomB, newB );
/*
Adjust the distances to accommodate the change we just made
*/
mushroomBC = mushroomB.distance( mushroomC );
mushroomCA = mushroomC.distance( mushroomA );
}
if ( originalBC > mushroomBC )
{
vec3 dir = mushroomB - mushroomC;
dir.normalize();
vec3 newB = mushroomC + dir * originalBC;
dir = mushroomC - mushroomB;
dir.normalize();
vec3 newC = mushroomB + dir * originalBC;
_vertices[ indexB ] = lrp( correctiveAmount, mushroomB, newB );
_vertices[ indexC ] = lrp( correctiveAmount, mushroomC, newC );
/*
Adjust the distances to accommodate the change we just made
*/
mushroomCA = mushroomC.distance( mushroomA );
}
if ( originalCA > mushroomCA )
{
vec3 dir = mushroomC - mushroomA;
dir.normalize();
vec3 newC = mushroomA + dir * originalCA;
dir = mushroomA - mushroomC;
dir.normalize();
vec3 newA = mushroomC + dir * originalCA;
_vertices[ indexC ] = lrp( correctiveAmount, mushroomC, newC );
_vertices[ indexA ] = lrp( correctiveAmount, mushroomA, newA );
}
}
}
}As I mentioned earlier, my mushroomRelax method isn't any good, and probably causes more problems than it solves.
Wow, that is an impressive technique. Thanks for posting the code its interesting too see how thats done
Sir, e^iπ + 1 = 0, hence God exists; reply!
TommorowPlusX, every time I see screens of your work, I'm amazed.
Really, I've no idea how to achieve such an impressive level of... everything!
Really, I've no idea how to achieve such an impressive level of... everything!
I appreciate the kind words, but, well let's see if I ever manage to produce something.
Somebody on the mac-opengl mailing list suggested that I manually generate my mipmap data and submit it myself. I just gave this a shot ( with some hacky code ) and it works!
This is good, because yesterday I wrote a GLSL implementation, which works, but brings the performance down ( when rendering just terrain and skydome ) from ~70fps to 30. The fixed function implementation is a *lot* faster.
This is good, because yesterday I wrote a GLSL implementation, which works, but brings the performance down ( when rendering just terrain and skydome ) from ~70fps to 30. The fixed function implementation is a *lot* faster.
TomorrowPlusX Wrote:Somebody on the mac-opengl mailing list suggested that I manually generate my mipmap data and submit it myself. I just gave this a shot ( with some hacky code ) and it works!
This is good, because yesterday I wrote a GLSL implementation, which works, but brings the performance down ( when rendering just terrain and skydome ) from ~70fps to 30. The fixed function implementation is a *lot* faster.
Do you mean you wrote some code that runs through pixel data arrays and generates mipmaps as it goes through it?
After hearing you mention 3D textures (didn't know about them) I went and read about them on the GameDev wiki. I can see their use for mixing two different textures, like in the mountain example. (Smooth transition from grass to rock...) And I can see it's a great feature, but I'm not sure how you would pass data to it...
Do you just pack the pixel data into one array and pass it like that?
Thanks!
Possibly Related Threads...
| Thread: | Author | Replies: | Views: | Last Post | |
| Mipmapping | Nick | 4 | 3,014 |
Aug 30, 2004 01:40 AM Last Post: sealfin |
|

