Search code examples
copenglmappingtexturing

Texturing a sphere in OpenGL with glTexGen


I want to get an earth texture on sphere. My sphere is an icosphere built with many triangles (100+) and I found it confusing to set the UV coordinates for whole sphere. I tried to use glTexGen and effects are quite close but I got my texture repeated 8 times (see image) . I cannot find a way to make it just wrap the whole object once. Here is my code where the sphere and textures are created.

    glEnable(GL_TEXTURE_2D);
glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
glBegin(GL_TRIANGLES);

for (int i = 0; i < new_sphere->NumOfTrians; i++)
{
    Triangle *draw_Trian = new_sphere->Trians+i;
    glVertex3f(draw_Trian->pnts[0].coords[0], draw_Trian->pnts[0].coords[1], draw_Trian->pnts[0].coords[2]);
    glVertex3f(draw_Trian->pnts[1].coords[0], draw_Trian->pnts[1].coords[1], draw_Trian->pnts[1].coords[2]);
    glVertex3f(draw_Trian->pnts[2].coords[0], draw_Trian->pnts[2].coords[1], draw_Trian->pnts[2].coords[2]);

}
glDisable(GL_TEXTURE_2D);
free(new_sphere->Trians);
free(new_sphere);

glEnd();

enter image description here


Solution

  • You need to define how your texture is supposed to map to your triangles. This depends on the texture you're using. There are a multitude of ways to map the surface of a sphere with a texture (since no one mapping is free of singularities). It looks like you have a cylindrical projection texture there. So we will emit cylindrical UV coordinates.

    I've tried to give you some code here, but it's assuming that

    • Your mesh is a unit sphere (i.e., centered at 0 and has radius 1)
    • pnts.coords is an array of floats
    • You want to use the second coordinate (coord[1]) as the 'up' direction (or the height in a cylindrical mapping)

    Your code would look something like this. I've defined a new function for emitting cylindrical UVs, so you can put that wherever you like.

    /* Map [(-1, -1, -1), (1, 1, 1)] into [(0, 0), (1, 1)] cylindrically */
    inline void uvCylinder(float* coord) {
      float angle = 0.5f * atan2(coord[2], coord[0]) / 3.14159f + 0.5f;
      float height = 0.5f * coord[1] + 0.5f;
      glTexCoord2f(angle, height);
    }
    
    glEnable(GL_TEXTURE_2D);
    glBegin(GL_TRIANGLES);
    
    for (int i = 0; i < new_sphere->NumOfTrians; i++) {
      Triangle *t = new_sphere->Trians+i;
    
      uvCylinder(t->pnts[0].coords);
      glVertex3f(t->pnts[0].coords[0], t->pnts[0].coords[1], t->pnts[0].coords[2]);
      uvCylinder(t->pnts[1].coords);
      glVertex3f(t->pnts[1].coords[0], t->pnts[1].coords[1], t->pnts[1].coords[2]);
      uvCylinder(t->pnts[2].coords);
      glVertex3f(t->pnts[2].coords[0], t->pnts[2].coords[1], t->pnts[2].coords[2]);
    
    }
    
    glEnd();
    glDisable(GL_TEXTURE_2D);
    
    free(new_sphere->Trians);
    free(new_sphere);
    

    Note on Projections

    The reason it's confusing to build UV coordinates for the whole sphere is that there isn't one 'correct' way to do it. Mathematically-speaking, there's no such thing as a perfect 2D mapping of a sphere; hence why we have so many different types of projections. When you have a 2D image that's a texture for a spherical object, you need to know what type of projection that image was built for, so that you can emit the correct UV coordinates for that texture.