Texture on a sphere

Hi, I have a problem applying a texture to a sphere.
In the setup phase I do this:

	earth = gluNewQuadric();

	gluQuadricTexture( earth, GL_TRUE);

	earthTexture = LoadBitmap("data/EarthTexture.bmp");

And when I draw the I use this code


	    glEnable ( GL_TEXTURE_2D );
	    glBindTexture ( GL_TEXTURE_2D, earthTexture);
	    gluSphere( earth, 0.9, 36, 72);
	  glDisable ( GL_TEXTURE_2D );

But something goes wrong and I obtain this http://yfrog.com/14screenshot20091119at105j

What am I doing wrong?

If I try to move the sphere the result is getting even worst.

If I try to draw only the texture on a square it works, but if I try to draw the texture on the square after I call the gluSphere also the image on the square is screwed :frowning:

The image mapped on the sphere appears like it is rotated 90. Can you rotate your image 90 degrees in some image editor and try again. Is LoadBitmap() a custom function or winOS library function?

Do you have some code affecting the texture Matrix ie do you call the following somewhere in your code?


glMatrixMode(GL_TEXTURE);

Thanks for your answer. I never use the matrix mode GL_TEXTURE explicitly, is there any GL command that makes that matrix change?

The code for the load bitmap was found on internet and it is this:


int LoadBitmap(char *filename)
{
    FILE * file;
    char temp;
    long i;

    BITMAPINFOHEADER infoheader;

    GLuint num_texture;

    if( (file = fopen(filename, "rb"))==NULL) return (-1); // Open the file for reading

    fseek(file, 18, SEEK_CUR);  /* start reading width & height */
    fread(&infoheader.biWidth, sizeof(int), 1, file);

    fread(&infoheader.biHeight, sizeof(int), 1, file);

    fread(&infoheader.biPlanes, sizeof(short int), 1, file);
    if (infoheader.biPlanes != 1) {
	    printf("Planes from %s is not 1: %u
", filename, infoheader.biPlanes);
	    return 0;
    }

    // read the bpp
    fread(&infoheader.biBitCount, sizeof(unsigned short int), 1, file);
    if (infoheader.biBitCount != 24) {
      printf("Bpp from %s is not 24: %d
", filename, infoheader.biBitCount);
      return 0;
    }

    fseek(file, 24, SEEK_CUR);

    // read the data
    if(infoheader.biWidth<0){
	infoheader.biWidth = -infoheader.biWidth;
    }
    if(infoheader.biHeight<0){
	infoheader.biHeight = -infoheader.biHeight;
    }
    infoheader.data = (char *) malloc(infoheader.biWidth * infoheader.biHeight * 3);
    if (infoheader.data == NULL) {
	    printf("Error allocating memory for color-corrected image data
");
	    return 0;
    }

    if ((i = fread(infoheader.data, infoheader.biWidth * infoheader.biHeight * 3, 1, file)) != 1) {
	    printf("Error reading image data from %s.
", filename);
	    return 0;
    }

    for (i=0; i<(infoheader.biWidth * infoheader.biHeight * 3); i+=3) { // reverse all of the colors. (bgr -> rgb)
	    temp = infoheader.data[i];
	    infoheader.data[i] = infoheader.data[i+2];
	    infoheader.data[i+2] = temp;
    }


    fclose(file); // Closes the file stream

    glGenTextures(1, &num_texture);
    glBindTexture(GL_TEXTURE_2D, num_texture); // Bind the ID texture specified by the 2nd parameter

    // The next commands sets the texture parameters
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); // If the u,v coordinates overflow the range 0,1 the image is repeated
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); // The magnification function ("linear" produces better results)
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST); //The minifying function

    glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);

    // Finally we define the 2d texture
    glTexImage2D(GL_TEXTURE_2D, 0, 3, infoheader.biWidth, infoheader.biHeight, 0, GL_RGB, GL_UNSIGNED_BYTE, infoheader.data);

    // And create 2d mipmaps for the minifying function
    gluBuild2DMipmaps(GL_TEXTURE_2D, 3, infoheader.biWidth, infoheader.biHeight, GL_RGB, GL_UNSIGNED_BYTE, infoheader.data);
    
    free(infoheader.data); // Free the memory we used to load the texture

    return (num_texture); // Returns the current texture OpenGL ID
}

I tried to rotate the texture, but it doesn’t help

Mhhm, the glMatrixMode(GL_TEXTURE) question was a remote possibility because you can affect texture coordinates with glTranslate/glRotate etc with it. So it is good to rule that out right away.

I am surprised rotating the image 90 degrees did nothing.

Some versions of openGL don’t accept non-power of two sizes ie what are the actual values of infoheader.biWidth and infoheader.biHeight? If they are not 64, 128,256 … 2^integer then try scaling your image file to the nearest power of 2 before loadBMP called.

Do you think this may be a problem? If I load the texture on a square it works, so the loader should be ok. Isn’t it?

What is really weird is that when I load just the square is drawn correctly, while if I load bot the square and the sphere also the texture on the square is messed up

I wrote a quick code (attached below) to see if I could replicate your problem. It works fine for me. I don’t know what your code is doing differently – maybe looking at the attachment will help you.

p.s. My windows.h defined BITMAPINFOHEADER and it did NOT have a member named .data so I had to replace everywhere “infoheader.data” with “infoheader_data” of type “unsigned char *”. Also note that this forced change from your code which used (char *) malloc to (unsigned char *) malloc.


#include <windows.h>
#include <stdio.h>
#include <GL/glut.h>
#include <math.h>

float anglex = 35;
float angley = -35;
float anglez = 0;
float locZ = 0.0;
float locY = 0.0;
float locX = 0.0;

GLUquadric *earth;
GLuint earthTexture;

int LoadBitmap(char *filename)
{
    FILE * file;
    char temp;
    long i;

    BITMAPINFOHEADER infoheader;
    unsigned char *infoheader_data;

    GLuint num_texture;

    if( (file = fopen(filename, "rb"))==NULL) return (-1); // Open the file for reading

    fseek(file, 18, SEEK_CUR);  /* start reading width & height */
    fread(&infoheader.biWidth, sizeof(int), 1, file);

    fread(&infoheader.biHeight, sizeof(int), 1, file);

    fread(&infoheader.biPlanes, sizeof(short int), 1, file);
    if (infoheader.biPlanes != 1) {
      printf("Planes from %s is not 1: %u
", filename, infoheader.biPlanes);
      return 0;
    }

    // read the bpp
    fread(&infoheader.biBitCount, sizeof(unsigned short int), 1, file);
    if (infoheader.biBitCount != 24) {
      printf("Bpp from %s is not 24: %d
", filename, infoheader.biBitCount);
      return 0;
    }

    fseek(file, 24, SEEK_CUR);

    // read the data
    if(infoheader.biWidth<0){
  infoheader.biWidth = -infoheader.biWidth;
    }
    if(infoheader.biHeight<0){
  infoheader.biHeight = -infoheader.biHeight;
    }
    infoheader_data = (unsigned char *) malloc(infoheader.biWidth * infoheader.biHeight * 3);
    if (infoheader_data == NULL) {
      printf("Error allocating memory for color-corrected image data
");
      return 0;
    }

    if ((i = fread(infoheader_data, infoheader.biWidth * infoheader.biHeight * 3, 1, file)) != 1) {
      printf("Error reading image data from %s.
", filename);
      return 0;
    }

    for (i=0; i<(infoheader.biWidth * infoheader.biHeight * 3); i+=3) { // reverse all of the colors. (bgr -> rgb)
      temp = infoheader_data[i];
      infoheader_data[i] = infoheader_data[i+2];
      infoheader_data[i+2] = temp;
    }


    fclose(file); // Closes the file stream

    glGenTextures(1, &num_texture);
    glBindTexture(GL_TEXTURE_2D, num_texture); // Bind the ID texture specified by the 2nd parameter

    // The next commands sets the texture parameters
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); // If the u,v coordinates overflow the range 0,1 the image is repeated
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); // The magnification function ("linear" produces better results)
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST); //The minifying function

    glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);

    // Finally we define the 2d texture
    glTexImage2D(GL_TEXTURE_2D, 0, 3, infoheader.biWidth, infoheader.biHeight, 0, GL_RGB, GL_UNSIGNED_BYTE, infoheader_data);

    // And create 2d mipmaps for the minifying function
    gluBuild2DMipmaps(GL_TEXTURE_2D, 3, infoheader.biWidth, infoheader.biHeight, GL_RGB, GL_UNSIGNED_BYTE, infoheader_data);
    
    free(infoheader_data); // Free the memory we used to load the texture

    return (num_texture); // Returns the current texture OpenGL ID
}

void keyboard(unsigned char key, int x, int y)
{
   switch (key) {
      case 27:
         exit(0);            
         break;
      case 'a':
         anglex+=5;
         break;
      case 'A':
         anglex-=5;
         break;
      case 's':
         angley-=5;
         break;
      case 'S':
         angley+=5;
         break;
      case 'd':
         anglez+=5;
         break;
      case 'D':
         anglez-=5;
         break;
      case 'u':
         locZ-=0.05;
         break;
      case 'U':
         locZ+=0.05;
         break;
      case 'k':
         locY+=0.05;
         break;
      case 'j':
         locY-=0.05;
         break;
      case 'l':
         locX+=0.05;
         break;
      case 'h':
         locX-=0.05;
         break;
      default:
         break;
   }
   glutPostRedisplay();
}

void display(void)
{
   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );

   glEnable ( GL_TEXTURE_2D );
   glBindTexture ( GL_TEXTURE_2D, earthTexture);

   glMatrixMode(GL_MODELVIEW);
   glLoadIdentity();
   //rotate/translate entire scene with a,A,s,S,D,D and h,l,j,k,u,U keys
   glRotatef(anglez,0.0,0.0,1.0);
   glRotatef(angley,0.0,1.0,0.0);
   glRotatef(anglex,1.0,0.0,0.0);

   //draw textured sphere
   glPushMatrix();
      glTranslatef(locX,locY,locZ);
      glScalef(0.5,0.5,0.5);
      gluSphere( earth, 0.9, 36, 72);
   glPopMatrix();

   //draw textured rectangle
   glPushMatrix();
      glTranslatef(0.0,0.0,1.0);
      glScalef(0.5,0.5,0.5);
      glBegin(GL_POLYGON);
        glTexCoord2f(0.0, 0.0);
        glVertex2f( -1.0,-1.0);
        glTexCoord2f(1.0, 0.0);
        glVertex2f(  1.0,-1.0);
        glTexCoord2f(1.0, 1.0);
        glVertex2f(  1.0, 1.0);
        glTexCoord2f(0.0, 1.0);
        glVertex2f( -1.0, 1.0);
        glTexCoord2f(0.0, 0.0);
        glVertex2f( -1.0,-1.0);
      glEnd();
   glPopMatrix();

   glDisable ( GL_TEXTURE_2D );

   glutSwapBuffers();
}

void init (void) 
{
  glClearColor(0.1f, 0.1f, 0.1f, 0.0f);

  glEnable(GL_DEPTH_TEST);

  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  glOrtho(-1.0, 1.0, -1.0, 1.0, -2.0, 2.0);
     
  earth = gluNewQuadric();
  gluQuadricTexture( earth, GL_TRUE);
  earthTexture = LoadBitmap("data/EarthTexture.bmp");
}

int main(int argc, char** argv)
{
   glutInit(&argc, argv);
   glutInitDisplayMode (GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
   glutCreateWindow ("Testing Texture");
   init ();
   glutDisplayFunc(display); 
   glutKeyboardFunc(keyboard);
   glutMainLoop();
   return 0; 
}

Thanks again for your reply, I really appreciate it.

It now maps the texture in the correct way, but if I move there is still an issue. The texture mapping sometimes goes wrong and what I can see is the mirrored texture on the other side rather than the actual texture I’d like to see.

If you want I can attach the image I am using.
I am also running my program on Linux.

Thanks again for your help

I saw the mirroring also – I fixed it with glVertex3f rather than 2f as follows


   //draw textured rectangle
   glPushMatrix();
      glTranslatef(0.0,0.0,1.0);
      glScalef(0.5,0.5,0.5);
      glBegin(GL_POLYGON);
        glTexCoord2f(0.0, 0.0);
        glVertex3f( -1.0,-1.0, 0.0);
        glTexCoord2f(1.0, 0.0);
        glVertex3f(  1.0,-1.0, 0.0);
        glTexCoord2f(1.0, 1.0);
        glVertex3f(  1.0, 1.0, 0.0);
        glTexCoord2f(0.0, 1.0);
        glVertex3f( -1.0, 1.0, 0.0);
        glTexCoord2f(0.0, 0.0);
        glVertex3f( -1.0,-1.0, 0.0);
      glEnd();
   glPopMatrix();

If that doesn’t fix it then attach your bitmap so I can try again.

Here’s my Linux version (should still work on WIN32 though) to remove dependency on windows.h


//note you need a bitmap file named data/EarthTexture.bmp to run this
#include <GL/glut.h>
#include <stdio.h>
#include <stdlib.h> //for malloc/free
#include <math.h>

float anglex = 35.0;
float angley =-35.0;
float anglez =  0.0;
float locZ = 0.0;
float locY = 0.0;
float locX = 0.0;

GLUquadric *earth;
GLuint earthTexture;

int LoadBitmap(char *filename)
{
    FILE * file;
    char temp;
    long i;

    // own version of BITMAPINFOHEADER from windows.h for Linux compile
    struct {
      int biWidth;
      int biHeight;
      short int biPlanes;
      unsigned short int biBitCount;
      unsigned char *data;
    } infoheader;

    GLuint num_texture;

    if( (file = fopen(filename, "rb"))==NULL) return (-1); // Open the file for reading

    fseek(file, 18, SEEK_CUR);  /* start reading width & height */
    fread(&infoheader.biWidth, sizeof(int), 1, file);

    fread(&infoheader.biHeight, sizeof(int), 1, file);

    fread(&infoheader.biPlanes, sizeof(short int), 1, file);
    if (infoheader.biPlanes != 1) {
      printf("Planes from %s is not 1: %u
", filename, infoheader.biPlanes);
      return 0;
    }

    // read the bpp
    fread(&infoheader.biBitCount, sizeof(unsigned short int), 1, file);
    if (infoheader.biBitCount != 24) {
      printf("Bpp from %s is not 24: %d
", filename, infoheader.biBitCount);
      return 0;
    }

    fseek(file, 24, SEEK_CUR);

    // read the data
    if(infoheader.biWidth<0){
      infoheader.biWidth = -infoheader.biWidth;
    }
    if(infoheader.biHeight<0){
      infoheader.biHeight = -infoheader.biHeight;
    }
    infoheader.data = (unsigned char *) malloc(infoheader.biWidth * infoheader.biHeight * 3);
    if (infoheader.data == NULL) {
      printf("Error allocating memory for color-corrected image data
");
      return 0;
    }

    if ((i = fread(infoheader.data, infoheader.biWidth * infoheader.biHeight * 3, 1, file)) != 1) {
      printf("Error reading image data from %s.
", filename);
      return 0;
    }

    for (i=0; i<(infoheader.biWidth * infoheader.biHeight * 3); i+=3) { // reverse all of the colors. (bgr -> rgb)
      temp = infoheader.data[i];
      infoheader.data[i] = infoheader.data[i+2];
      infoheader.data[i+2] = temp;
    }


    fclose(file); // Closes the file stream

    glGenTextures(1, &num_texture);
    glBindTexture(GL_TEXTURE_2D, num_texture); // Bind the ID texture specified by the 2nd parameter

    // The next commands sets the texture parameters
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); // If the u,v coordinates overflow the range 0,1 the image is repeated
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); // The magnification function ("linear" produces better results)
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST); //The minifying function

    glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);

    // Finally we define the 2d texture
    glTexImage2D(GL_TEXTURE_2D, 0, 3, infoheader.biWidth, infoheader.biHeight, 0, GL_RGB, GL_UNSIGNED_BYTE, infoheader.data);

    // And create 2d mipmaps for the minifying function
    gluBuild2DMipmaps(GL_TEXTURE_2D, 3, infoheader.biWidth, infoheader.biHeight, GL_RGB, GL_UNSIGNED_BYTE, infoheader.data);
    
    free(infoheader.data); // Free the memory we used to load the texture

    return (num_texture); // Returns the current texture OpenGL ID
}

void keyboard(unsigned char key, int x, int y)
{
   switch (key) {
      case 27: exit(0); break;
      case 'a': anglex+=5; break;
      case 'A': anglex-=5; break;
      case 's': angley-=5; break;
      case 'S': angley+=5; break;
      case 'd': anglez+=5; break;
      case 'D': anglez-=5; break;
      case 'u': locZ-=0.05; break;
      case 'U': locZ+=0.05; break;
      case 'k': locY+=0.05; break;
      case 'j': locY-=0.05; break;
      case 'l': locX+=0.05; break;
      case 'h': locX-=0.05; break;
      default: break; 
   }
   glutPostRedisplay();
}

void display(void)
{
   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );

   glEnable ( GL_TEXTURE_2D );
   glBindTexture ( GL_TEXTURE_2D, earthTexture);

   glMatrixMode(GL_MODELVIEW);
   glLoadIdentity();
   //rotate/translate entire scene with a,A,s,S,D,D and h,l,j,k,u,U keys
   glRotatef(anglez,0.0,0.0,1.0);
   glRotatef(angley,0.0,1.0,0.0);
   glRotatef(anglex,1.0,0.0,0.0);

   //draw textured sphere
   glPushMatrix();
      glTranslatef(locX,locY,locZ);
      glScalef(0.5,0.5,0.5);
      gluSphere( earth, 0.9, 36, 72);
   glPopMatrix();

   //draw textured rectangle
   glPushMatrix();
      glTranslatef(0.0,0.0,1.0);
      glScalef(0.5,0.5,0.5);
      glBegin(GL_POLYGON);
        glTexCoord2f(0.0, 0.0);
        glVertex3f( -1.0,-1.0, 0.0);
        glTexCoord2f(1.0, 0.0);
        glVertex3f(  1.0,-1.0, 0.0);
        glTexCoord2f(1.0, 1.0);
        glVertex3f(  1.0, 1.0, 0.0);
        glTexCoord2f(0.0, 1.0);
        glVertex3f( -1.0, 1.0, 0.0);
        glTexCoord2f(0.0, 0.0);
        glVertex3f( -1.0,-1.0, 0.0);
      glEnd();
   glPopMatrix();

   glDisable ( GL_TEXTURE_2D );

   glutSwapBuffers();
}

void init (void) 
{
  glClearColor(0.1f, 0.1f, 0.1f, 0.0f);

  glEnable(GL_DEPTH_TEST);

  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  glOrtho(-1.0, 1.0, -1.0, 1.0, -2.0, 2.0);
     
  earth = gluNewQuadric();
  gluQuadricTexture( earth, GL_TRUE);
  earthTexture = LoadBitmap("data/EarthTexture.bmp");
}

int main(int argc, char** argv)
{
   glutInit(&argc, argv);
   glutInitDisplayMode (GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
   glutCreateWindow ("Testing Texture");
   init ();
   glutDisplayFunc(display); 
   glutKeyboardFunc(keyboard);
   glutMainLoop();
   return 0; 
}

This is what happens, I took the screenshot at the point where the texture start to swap.
http://img34.imageshack.us/content.php?p…amp;via=mupload
I the texture I am using is this one saved as bitmap http://www.oera.net/How2/PlanetTexs/EarthMap_2500x1250.jpg

I will try also your Linux version. Thanks again for your help

Your Linux version works for me, except that the texture on the sphere is mirrored, but now does not has the issue I was experiencing before. The mapping stays the same.

Can you show a picture of what you mean by the image is mirrored on the sphere?

I downloaded your bmp and it ran fine on my Ubuntu 9.10/Nvidia 9600GT computer. I did have to rescale the image to 2048x1024 though. After retrying it worked fine with your original image size.

This is what happens, the texture is flipped http://yfrog.com/0wscreenshot20091121at705j

Are you seeing this mirroring at all rotations or just at some particular values?

Are you using your own setting for culling – something like


  glEnable(GL_CULL_FACE);
  glFrontFace(GL_CW);

Ill defined culling would definitely make the textured sphere appear to have a mirrored image.

Lets do some debugging, I cut and pasted my code from Post267424 to be certain we are using identical code and compiled it.


g++ test_texture.cpp -lglut

Next I downloaded your jpg. Since it was not a .BMP, I was forced to use the linux “convert” command to generate a corresponding BMP with


convert EarthMap_2500x1250.jpg EarthTexture.bmp

in the data/ folder and the converted bmp is posted here. After running the executable the result looks fine as follows

Could you try repeating these steps but with my converted jpg->bmp file? Does that help?

Did you use the “u” “U” keys to move the sphere into/away from the textured plane? If yes, you can move the Sphere past the clipping plan in which case you will see the “inside” of the sphere which could appear as a mirrored image.

I tested it with your image and it works. Probably the problems is due to the fact that I used preview on Mac OS X to convert the image. I was not knowing the convert command on Linux and I did the conversion before starting to write the openGL code.

Now my problem is definitively fixed, thank you so much.

“convert” command is very powerful tool. Good to hear it solved the problem.

FYI Now that you have seen how to load a particular BMP file type, you may want to someday load a jpg directly or some other formats like PNG, gif etc. There is a great cross platform library for just such a problem that I use all the time – DevIL image loading library. When and if you get to a point of using it there is another post with an example code at Post267514. Note to run that example you will need three image files: Logo.bmp, wmap.bmp, Frac.bmp. Now may not be the time to look at it but just beware of the library for future use.

I have the same problem… the image whit texture mirror on the other part.

/this is the problem: http://twitter.yfrog.com/0wscreenshot20091121at705j )

How can i resolve this problem?