7

I'm programming a simple graphics engine in C++ that should be capable of representing the Earth. The Earth is modelled as an icosphere, and I planned to map its texture using the normals of each vertex. To do so, I'm essentially trying to get vertical and horizontal coordinates (φ and θ, respectively) (i.e. latitude and longitude) from the vertices' normals.

The following is the (edited) function that implements my approach (called when new vertices are generated). It should return texture coordinates from (0,0) to (1,1) when the parameter n is a normal vector:

glm::vec2 Icosphere::getTexCoord(glm::vec3 n)
{
    float theta = (atan2f(n.x, n.z) / PI) / 2.f + 0.5f;
    float phi = (asinf(-n.y) / (PI / 2.f)) / 2.f + 0.5f;
    return glm::vec2(theta, phi);
}

However, this is yielding unexpected results. With this test image, my icosphere looks like this:

Example 1

Example 2

Small white and red triangles are located at (x,y,z) = (1,0,0). In the first image, I generated the icosphere with less triangular faces (320), whilst in the latter the icosphere is generated with ~82k triangles.

I'm suspecting that what causes those texture "glitches" is the fact that coordinates (0,*) and (1,*) are not considered adjacent. Is there a proper way of doing this?

NB: These are my texture parameters:

/* Configure texture parameters: */
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glGenerateMipmap(GL_TEXTURE_2D);
Carles Araguz
  • 1,157
  • 1
  • 17
  • 37
  • My guess is that there is some kind of accumulated floating-point error when you generate your set of vertex normals. If I could look at that code, I might be able to find the problem. The `getTexCoord` function seems correct to me. – Christopher Oicles Jan 31 '17 at 23:03
  • You could try taking the vector sum of all your normals to see how close it is to zero. This might give you a clue about the problem's cause. – Christopher Oicles Jan 31 '17 at 23:20
  • I will try that to see if that's something to do with my normals, but I have my doubts, since I use the same normals to render lighting effects and they look just perfect. – Carles Araguz Jan 31 '17 at 23:52
  • I think the problem is caused by the fact that some triangles (the ones in the texture edge have vertices in both sides `(0,y)` and `(1,y)`. For these cases, when vertices are in different edges, the texture is compressed instead of considering texture edges adjacent. Could this be it? – Carles Araguz Feb 01 '17 at 00:13
  • Your guess is better than mine. That would explain why the sphere with larger triangles has more distortion than the one with smaller ones. Before now I was mistakenly thinking the first picture had more, and smaller triangles, so your theory sounds better to me now. – Christopher Oicles Feb 01 '17 at 02:27
  • I'm now certian you're right, because the bad triangles have textures with 10 stripes, so these triangles are mapped with a span of the whole texture, backwards because one of their vertices wraps to the opposite side. – Christopher Oicles Feb 01 '17 at 03:02

1 Answers1

3

I finally managed to overcome this ugly texture mapping. The problem is indeed caused by the characteristic non-uniform orientation of triangles in an icosphere. In an icosphere (as well as in an icosahedron) it is impossible to find a plane in the origin (0,0,0) that does not intersect any triangle. If we think of the Earth, this means that all its meridians intersect some triangles. It also means that when mapping a texture, the triangles located in the texture vertical edges (i.e. with tex. coord. (1,y) and (0,y)) will have some of their vertices in either of the sides. When this happens, the texture is interpolated from x≈1 to x≈0, producing the effect showed in my images.

In order to solve this, I set the texture wrapping to GL_REPEAT and simply forced some vertices to be away from the texture coordinate space (i.e. x > 1). This allowed the texture edges to be adjacent and mapped the texture perfectly:

/* Configure texture parameters: */
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); // Not GL_CLAMP_TO_EDGE.
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); // Not GL_CLAMP_TO_EDGE.
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glGenerateMipmap(GL_TEXTURE_2D);

Results:

After modifying texture coordinates of triangles in the edges (with GL_CLAMP_TO_EDGE):

Result1

Changing texture parameters to GL_REPEAT:

enter image description here

Carles Araguz
  • 1,157
  • 1
  • 17
  • 37
  • 4
    How did you detect which vertices to be away from texture coordinate space? I am unsure how to detect this. – DCON Nov 22 '17 at 19:40
  • I have the same issue. My only idea is to duplicate certain vertices of triangles that are on the "seam" and give the duplicated vertex different (> 1) texture coordinates. Is this what you did or is there a nifty way to solve it without duplication? – Wutz Jan 02 '23 at 00:59
  • 1
    It's been a long while since I implemented this engine, but going for duplication of vertices in the seam doesn't seem like a bad idea to me now (memory-wise, the amount of redundant points is probably irrelevant considering the total # of points in your sphere). In fact, I can't even remember if my triangles shared their vertices with the adjacent ones...! If they do in your case you can simply duplicate the seam texture data associated to each _triangle_, instead of setting texture data to individual _vertices_. – Carles Araguz Jan 02 '23 at 11:49
  • Thanks for the response @CarlesAraguz! I am wondering what you mean by setting texture data per triangle instead of per vertex. I only know of one way to texturize: set a texture coordinate for each vertex and let the fragment shader interpolate. Is there some other way I don't know about? – Wutz Jan 08 '23 at 14:49