2
\$\begingroup\$

I'm trying to create and texture a terrain mesh in DirectX11. I've managed to create the mesh itself, but I don't know how I should do the texturing. Let me start by explaining what I'm doing so far:

I have a vertex structure that looks like this:

struct Vertex
{
    XMFLOAT3 position;
    XMFLOAT4 color;
};

Then I create all the vertices for the terrain mesh:

for (int z = 0, index = 0; z < terrainHeight; ++z)
{
    for (int x = 0; x < terrainWidth; ++x, ++index)
    {
        vertices[index].position = XMFLOAT3(x, 0.0f, z);
        vertices[index].color = XMFLOAT3(1.0f, 1.0f, 1.0f, 1.0f);
    }
}

Then I create all the triangles for the terrain mesh with indices:

for (int z = 0, index = 0; z < terrainHeight - 1; ++z)
{
    for (int x = 0; x < terrainWidth - 1; ++x)
    {
        indices[index++] = (z    ) * terrainWidth + (x    );
        indices[index++] = (z + 1) * terrainWidth + (x    );
        indices[index++] = (z + 1) * terrainWidth + (x + 1);

        indices[index++] = (z    ) * terrainWidth + (x    );
        indices[index++] = (z + 1) * terrainWidth + (x + 1);
        indices[index++] = (z    ) * terrainWidth + (x + 1);
    }
}

With 256x256 vertices I get this result (rendering in wireframe):

A terrain grid created from 256x256 vertices

I'd now like to change the color to a texture, so I change the Vertex structure to this:

struct Vertex
{
    XMFLOAT3 position;
    XMFLOAT2 texture; // Changed the color to a texture coordinate.
};

And this is where I get stuck. Since each vertex now have a texture coordinate (U, V) I'd like to set it up like this:

The texture coordinates between square A and B reqires 2 different coordinates.

But each vertex can only hold one texture coordinate. As you can see, the 2 vertices between square A and B needs 2 different texture coordinates each in order to map it correctly. If I'd loop through each pair of triangles, then when I reach square B I'll overwrite the texture coordinates that was correct for square A.

So therefore I have some questions:

  1. Am I doing this in the "right" way? How does modern games do it? Do they texture each square or the entire mesh?

  2. If I'd like to texture each square, how can I solve this problem?

\$\endgroup\$
1
  • \$\begingroup\$ I solved this issue in Unity Mesh programming by creating a unique copy of each vertex for each face. Hope this helped. \$\endgroup\$ Commented Jul 19, 2017 at 6:56

3 Answers 3

2
\$\begingroup\$

The best way I have found to do terrain texturing is using color maps to splat textures, with this method the only vertex info you need is a float3 for the vertex positon as there is no need to pass in texture cords.

First up you draw around your terrain using each color channel for a different texture.

Color map: Red = beach Green = under water Blue = grass

enter image description here

Then in your pixel shader you use the world space vertex position like this to build your texture cords and sampel your 3 terrain textures to build a nice blended terrain.

Texture2D ColorMap;
Texture2D DiffuseMap0; //Terrain Texture
Texture2D DiffuseMap1; //Terrain Texture
Texture2D DiffuseMap2; //Terrain Texture


float4 Texcords;

//Color map cords, 4096 *8 = terrain size, you will have to play with this. You sample your Colormap with this

Texcords.xy = input.WorldPos.xz / (4096 *8); 
Texcords.xy += 0.5;

//Terrain texture cords, TextureScale = 1024 you will have to play with this value. You sample your terrain textures with this.

Texcords.zw = dout.PosW.xz / TextureScale;


//ColorMap
float4 Layer1 = ColorMap.Sample(pointsamp, Texcords.xy); //pointsamp = point sampling. 

float total = Layer1.r + Layer1.g + Layer1.b;   
Layer1.rgb /= total;

//Terrain Textures
float4 TerrainColor;
TerrainColor+= DiffuseMap0.Sample(samANISOTROPICWrap, Texcords.zw) * Layer1.r;//ANISOTROPICWrap = ANISOTROPIC Wrap sampling 
TerrainColor+= DiffuseMap1.Sample(samANISOTROPICWrap, Texcords.zw) * Layer1.g;//ANISOTROPICWrap = ANISOTROPIC Wrap sampling 
TerrainColor+= DiffuseMap2.Sample(samANISOTROPICWrap, Texcords.zw) * Layer1.b;//ANISOTROPICWrap = ANISOTROPIC Wrap sampling 

What you end up with is under water texture where the green is, sand where the red is and grass where blue is.

You can also get some really cool blending by using a custom color value rather than green=(0,255,0,255) you can go things like green=(0,200,0,127) and that way you would let a bit more of the blue or red come into play when the textures get sampled on the gpu.

I use two color maps, one for low ground and one for mountains, in my color map the black spots will be filled in with another color map but just for the slops.

You can also look into tri planar mapping so you can avoid color maps.

\$\endgroup\$
0
\$\begingroup\$

If you want each quad to be a full uv range of [0..1[, then you do not need a texture coordinate stream in your vertex at all. Instead, in your shader

float2 tileDimensions; // number of quads in each direction
float2 terrainDimensions; // in object space
float2 texcoordFromPosition( float3 position ) {
  return frac( position.xy * tileDimensions / terrainDimensions; 
}

By design, the input assembly works with full tuples. The only way to have on one point span over 2 triangles a texcoord at 0 AND 1 on each side is to split the vertex into 2 different ones.

You should note that the frac may introduce issues with the derivatives and mip mapping, you should just use the same function as above without the frac and set the sampler state with wrapping address mode.

\$\endgroup\$
4
  • \$\begingroup\$ Thank you very much for your answer. Although, I'm not sure that I understand what you mean. Would you mind explaining it on an easier level? \$\endgroup\$ Commented Jan 17, 2017 at 22:56
  • \$\begingroup\$ @fighting_falcon93 For a terrain mesh, you can generate the texture coordinates from the position ( you could even have only the height and generate the 2D grid location from the vertex ID ). If you want to repeat the texture over the surface, you can set the sampler state to wrap, and just let the hardware loop it for you. \$\endgroup\$
    – galop1n
    Commented Jan 17, 2017 at 23:02
  • \$\begingroup\$ Aha, now I understand. Does that technique also work if I want to build a terrain mesh with different kinds of textures in different areas, for instance, sand, grass and rock? \$\endgroup\$ Commented Jan 17, 2017 at 23:20
  • \$\begingroup\$ @fighting_falcon93 You can do many things with a shader. You may for example paint the material opacity in the vertex ( it is continuous, no need to cut vertices ) and blend in the pixel shader reading the various texture. Solution are countless here anyway, but the basics, terrain are simple enough to parametrize the uvs. \$\endgroup\$
    – galop1n
    Commented Jan 18, 2017 at 0:05
0
\$\begingroup\$

With this color variable you allotted you can just change the color of the vertex.But when it comes to texture you have to create an image from the texture coordinates you have and then map this image to the corresponding terrain.Then you will get to see the textured terrain.I have done this in using OpengL so I cant give you code.

\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .