Texture distortion on heightmapped terrain

Started by
15 comments, last by swiftcoder 16 years, 5 months ago
When you texture a heightmapped terrain by tiling a texture across it, areas where there is a steep slope between adjacent heightmap samples tend to stretch the texture if the heightmap texture coordinates are evenly spaced. Until recently, I lived with it, but lately I've been wondering about ways to eliminate it. I attempted to re-calculate texture coordinates so that the actual length of the edge of a quad determines the spacing of texture coordinates along that edge, rather than the spacing of the samples in the x/z plane. So a quad with a steeper slope 'covers' more texture space than a quad with a shallower slope. On ridges and steep features parallel to the axes, this approach works well, as shown in these images. The first image is non-corrected terrain on a straight ridge, showing the stretching. The second image is corrected. Distorted Corrected However, doing it this way poses problems, since the 'correction' is actually just additional distortion which gets propagated across the remainder of the heightmap, so that on flat areas you can still see how the texture was stretched to accomodate the curve of the feature, as in the following images. The first image here is a mound, with un-corrected texturing, and the second is the same mound corrected. You can see in the second how the 'correction' continues across the map in a very ugly fashion. Distorted Corrected I've tried any number of means for calculating the correction, with no change in the result. Does anybody know of any other means for correcting the distortion that doesn't result in this ugliness, or is it just something you have to deal with with regularly spaced grids? Or am I doing something stupid? The terrain map function that actually calculates the texture coordinates after the grid mesh is constructed is:

void CTerrainMap::calcTextureCoords()
{
    float totals[m_meshmapwidth];
    unsigned int x,z;
    for(x=0; x<m_meshmapwidth; ++x) totals[x]=0.0f;

    for(z=0; z<m_meshmapwidth; ++z)
    {
        float s=0.0f;
        for(x=0; x<m_meshmapwidth; ++x)
        {
            CVec3f prev, cur, delta;
            // Do s
            if(x>0)
            {
                prev=m_vertex.get((int)(x-1), (int)z);
                cur=m_vertex.get((int)(x), (int)z);
                delta=cur-prev;
                float len=delta.length() / m_samplespacing;
                s+=len;
            }
            // Do t
            if(z>0)
            {
                prev=m_vertex.get((int)(x), (int)(z-1));
                cur=m_vertex.get((int)(x), (int)z);
                delta=cur-prev;
                float len=delta.length() / m_samplespacing;
                totals[x]+=len;
            }
            CVec2f tc;
            tc[0]=s*m_texturespacing;
            tc[1]=totals[x]*m_texturespacing;
            m_texcoord.set(x,z,tc);
        }
    }
}


Thanks.
Advertisement
What about just treating the terrain as a regular mesh, which has to deal with irregularities in how the texture coordinates change and abandoning the projected texture altogether? That is, have areas with consistent texture coordinate (relative to position) derivatives be continuous areas in a texture, and duplicate vertices at discontinuous areas in the texture?
One way i can think of, in the case of tiled terrain textures, is to use tri-planar mapping as did nVidia in their Cascades demo (pdf+movies)
Basically you calculate the texcoords relative to all 3 planes (xz, xy, yz) and you select the correct one, by blending them together, based on the normal.

EDIT : You haven't said anything about the minimum requirements, but tri-planar mapping requires 3 times the texture fetches you usually do in the fp, so this may be a little slow. But again it depends on what shaders and how many different textures you are using for the terrain.

HellRaiZer
HellRaiZer
JTippetts,

If you manage to find an answer to that question, you can sell it to Autodesk and become a RICH man.

The tri-linear method described in that nvidia paper is pretty cool, and will work great if you are using procedural textures or simple repetitious textures like dirt or grass. However, if you are wanting to map actual images then they wont match up at the seems.

Some of the most promising solutions are "LSCSM mapping" as used in Blender and "Pelt Mapping" as used in 3ds Max.

However, since you are likely projecting some kind of a dirt texture, so you have no seems but you just want to avoid distortion, the tri-linear method should do perfectly!
I've never really found a good general solution to this problem. Most of the time, any major texture distortion is fixed manually by the artists, who will modify the texture space on really steep surfaces to eliminate artifacts. Seams that appear on the boundaries will typically be covered up by blending over them.

Is there a particular reason why you're trying to eliminate the stretching?
1. You can reparamatrize the mapping, for the whole terrain - just run a simulation that will shrink the stretched mapping on steep edges - but it will change the whole mapping of the terrain (as you method...).
In practice however, this will not work, since it creates a dependency on the texturing by the geometry, which is *evil*. No tweaking and can change already textured areas.

2. Triplanar mapping. Make it two-planar, and use it only for steep surfaces - and that works pretty well.
I.e. it can be very easilly fitted into existing splatmapped terrain, just by adding another kind of layer - say, vertical, that has 'cylindrical' texture coordinates and is applied only to vertical terrain.

The (1,3) is the image with that technique:
http://haemimontgames.com/gotre/screenshots/

3. The same as (2), but do not use twoplanar mapping, just single VERY tiled texture. VERY tiled. It can work the same as (2), if you don't have truely vertical geometry, which is exactly the case of heightmapped terrains anyway... :)
Quote:Original post by Zipster
Is there a particular reason why you're trying to eliminate the stretching?


Mostly OCD, really. Every time I see a stretched cliff, my inner psychotic 'fixer' screams at me to do something about it. [grin] All of my terrains are randomly generated, so special-case fixing of the terrain texturing doesn't seem like the best solution, since I really need a general-case one.

Thanks for the posts, everyone. I've been playing quite a bit with procedurally generated terrain textures, so I might play around with the tri-planar mapping as was suggested. I can see how it could be pretty handy.
The triplanar projection certainly will work, but is it needed? In my experience if you are using procedural textures (perlin-noise-based for instance), there is such a high resolution for the final texture output that the stretching is hardly noticeable if you avoid really sheer cliffs..
You could move towards a 3D texture mapping / storage.

There's a bit of research lately taking advantage of the fact we have enough storage to access 3D textures.

for example, 'Solid Texture Synthesis'
http://www.johanneskopf.de/publications/solid/index.php

~Main
==Colt "MainRoach" McAnlisGraphics Engineer - http://mainroach.blogspot.com
Quote:Original post by Matt Aufderheide
The triplanar projection certainly will work, but is it needed? In my experience if you are using procedural textures (perlin-noise-based for instance), there is such a high resolution for the final texture output that the stretching is hardly noticeable if you avoid really sheer cliffs..


At the moment, I'm using basic bitmap images. I have experimented with procedural terrain texture synthesis, but support for using such is not implemented in the engine.

I have thought about experimenting with non-square textures which are 2 or 4 times as large in one dimension as in the other. I could place a 'tall' texture on one layer and a 'wide' texture on another, then blend between the two layers based on the normal at a given vertex. This way, steep cliffs that face mostly one direction would sample from the texture that encodes more texels in the [0,1] range in that direction. This might fix a lot of the stretching. I'll have to try it and see how it goes. I doubt for a 'checkerboard' texture as in the examples above it would do anything, but for similar appearing dirt or stone textures, it would probably be fine.

This topic is closed to new replies.

Advertisement