2
votes

I've created a terrain in OpenGL using a heightmap image. The result is an dynamically allocated array that includes all the vertices of the terrain (x,y,z) with a stride of 3 like this:(firstvertexX,firstvertexY,firstvertexZ,secondvertexX,secondvertexY,thirdvertexZ...).

I also calculated the normals (in a similar array) and an extremely bad approximation of the supposed UVs of the terrain (using the ...normals) in order to render it.The rendering has no problem at all,except that as i said the UV approximation is really bad and the texture that is rendered has a "polygon" feel in it (using GL_TRIANGLE_STRIPS).

I want to clarify that i want the terrain to be created in OpenGl and not in any other programm like Blender. So i have to somehow specify a UV coordinate for each vertex in the array mentioned above that containes the vertices. I would also like to say that im not looking for a perfect UV solution(even if somehow there is one), but a way that does a rough approximation in order for the result to be decent.

So my questions are:

  1. Do the UV coordinates need to range from 0 to 1, with no consideration of the terrain's width and length or not?
  2. Does the UV coordinate array need to be the same length as the vertices array?
  3. Is there any simple algorithm or way that i could specify the UVs of the terrain?

The texture that is rendered:

Renderedimage

The actual image from the web:

Actual image

The code that specifies the creation of the arrays :

for (i = 0; i < terrainGridLength - 1 ; i++) { //z
     for (j = 0; j < terrainGridWidth; j++) { //x

      TerrainVertices[k] =  startW + j + xOffset;
      TerrainVertices[k + 1] =  20 * terrainHeights[(i + 1)*terrainGridWidth + (j)]  + yOffset;
      TerrainVertices[k + 2] =  startL - (i + 1) + zOffset;
      k = k + 3;


      TerrainVertices[k] =  startW +  j +xOffset;
      TerrainVertices[k + 1] = 20 * terrainHeights[(i)*terrainGridWidth + j] +yOffset;
      TerrainVertices[k + 2] =  startL - i  + zOffset;
      k = k + 3;


      float x = RandomFloat(0.16, 0.96);
      TerrainUVs[d] = x* (terrainNormals[ 3*((i+1 )*terrainGridWidth + j)]);
      TerrainUVs[d + 1] = terrainNormals[3* ((i+1 )*terrainGridWidth + j) + 2 ];

      x = RandomFloat(0.21, 0.46);
      TerrainUVs[d+2] = x*(terrainNormals[ 3*((i+1 )*terrainGridWidth + j) +1]);
      d = d + 3;


      x = RandomFloat(0.3, 0.49);
      TerrainUVs[d] = x*(terrainNormals[3* ((i  )*terrainGridWidth + j) ]);
      TerrainUVs[d + 1] = terrainNormals[ 3*((i  )*terrainGridWidth + j)+2 ];

      x = RandomFloat(0.4, 0.85);
      TerrainUVs[d + 2] = x*(terrainNormals[3* ((i )*terrainGridWidth + j) +1]);

      d = d + 3;
    }
}
1
1) Not enough code. 2) i+1 may be out of array size. 3) GL_TRIANGLE_STRIPS defines triangles by 0,1,2 - 1,2,3 - 2,3,4... not your way 4) How is related the texture to the normals? There are some different usages... - Ripi2
Using normals was just a silly way to implement UVs with no real meaning, that is why i am asking for something else if its possible. However you may be right with the strips, can you maybe add some more details about this? - Coolshady

1 Answers

4
votes

I will try to address all of your questions as best I can.

1) Do the UV coordinates need to range from 0 to 1, with no consideration of the terrain's width and length or not?

UV coordinates are generally in 0 to 1 space, but if you pass in a UV that is not 0 to 1 the way that UV is handled will depend on how you have set up OpenGL to handle that case.

You can tell the texture sampler to treat the coordinates as wrapping with the following code:

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);

Alternatively you can clamp the UV values with the following code:

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

See more on texture samplers here: https://www.khronos.org/opengl/wiki/Sampler_Object

Does the UV coordinate array need to be the same length as the vertices array?

Generally speaking, yes. When texturing some geometry you will usually want at least one UV for each vertex thus you should have the same number of UVS as you do vertices. BUT you may have more if you have more than one texture applied to the geometry or you may have less if you are texturing your geometry via some other means, like with a fragment shader you write which does not require UVS. You can also have less if your UVS are indexed, which is when vertices that share the same UV each refer to the same UV in an array through an index. But if you want to keep things simple just go for 1 to 1 and don't worry about the duplicates.

Is there any simple algorithm or way that i could specify the UVs of the terrain?

One easy way to do this is just to texture each triangle as part of a quad. You terrain should just be a grid of quads which means that you could just iterate through you list of vertices assigning each quad in your mesh with texture coordinates like these:

enter image description here

Even better, if the grid of your terrain is size 1 on x and z you can just plug in the world space coordinates of your mesh on x and z and set your texture sampler to wrap, assuming your mesh isn't rotated. It will still work if your mesh is rotated, but the texture won't be rotated with it.