Texture Mapping

I have an application that I want to map various pgm or ppm files over an object. I have a couple different drawing methods such as Wireframed, Shaded, Texture Mapped with the image that creates the object, and Texture Mapped with another image. I can successfully texture map with the ppm files, but I am having problems texture mapping with the pgm files. I read in a pgm file and store its intensities at each (x,y) in an array. Since it is a pgm file, I know texture mapping will just show a grayscale image. Here is how I set up my texture mapping for when I use a pgm file to map.

       // Set filtering parameter.
      glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
      glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );

      glTexImage2D( GL_TEXTURE_2D, 0, 0, ncTex, nrTex, 0,
                   GL_LUMINANCE, GL_UNSIGNED_BYTE, image );

       // Provide texture mapping method.
      glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE );  

Then in my display function, I call these calls based on an if-else statement:

          glEnable( GL_TEXTURE_2D );
         glDisable( GL_LIGHTING );
         glCallList( texture ); 

Here is the function I use to create my object when I COMPILE my display list.

 void drawTexture( )
{
   float v[3][3];
   float normal[3];
   float r[3], s[3];
   int x;
   int y;
   glBindTexture( GL_TEXTURE_2D, texName[0] );
   for( y = 0 ; y < ( nr - 1 ) ; y++ )
   {
      for( x = 0 ; x < ( nc - 1 ) ; x++ )
      {
         v[0][0] = x - nc / 2;
         v[0][1] = y - nr / 2;
         v[0][2] = image[y * nc + x];
         r[0] = ( (float) x ) / ( (float) ( ncTex - 1 ) );
         s[0] = ( (float) y ) / ( (float) ( nrTex - 1 ) );
         v[1][0] = x - nc / 2;
         v[1][1] = ( y + 1 ) - nr / 2;
         v[1][2] = image[( y + 1 ) * nc + x];
         r[1] = ( (float) x ) / ( (float) ( ncTex - 1 ) );
         s[1] = ( (float) ( y + 1 ) ) / ( (float) ( nrTex - 1 ) );
         v[2][0] = ( x + 1 ) - nc / 2;
         v[2][1] = y - nr / 2;
         v[2][2] = image[y * nc + ( x + 1 )];
         r[2] = ( (float) ( x + 1 ) ) / ( (float) ( ncTex - 1 ) );
         s[2] = ( (float) y ) / ( (float) ( nrTex - 1 ) );

         calcNormal( v, normal );
         glBegin( GL_POLYGON );
            glNormal3fv( normal );
            glTexCoord2f( r[0], s[0] );
            glVertex3fv( v[0] );
            glTexCoord2f( r[1], s[1] );
            glVertex3fv( v[1] );
            glTexCoord2f( r[2], s[2] );
            glVertex3fv( v[2] );
         glEnd( );

         v[0][0] = x - nc / 2;
         v[0][1] = ( y + 1 ) - nr / 2;
         v[0][2] = image[( y + 1 ) * nc + x];
         r[0] = ( (float) x ) / ( (float) ( ncTex - 1 ) );
         s[0] = ( (float) ( y + 1 ) ) / ( (float) ( nrTex - 1 ) );
         v[1][0] = ( x + 1 ) - nc / 2;
         v[1][1] = y - nr / 2;
         v[1][2] = image[y * nc + ( x + 1 )];
         r[1] = ( (float) ( x + 1 ) ) / ( (float) ( ncTex - 1 ) );
         s[1] = ( (float) y ) / ( (float) ( nrTex - 1 ) );
         v[2][0] = ( x + 1 ) - nc / 2;
         v[2][1] = ( y + 1 ) - nr / 2;
         v[2][2] = image[( y + 1 ) * nc + ( x + 1 )];
         r[2] = ( (float) ( x + 1 ) ) / ( (float) ( ncTex - 1 ) );
         s[2] = ( (float) ( y + 1 ) ) / ( (float) ( nrTex - 1 ) );

         calcNormal( v, normal );
         glBegin( GL_POLYGON );
            glNormal3fv( normal );
            glTexCoord2f( r[0], s[0] );
            glVertex3fv( v[0] );
            glTexCoord2f( r[1], s[1] );
            glVertex3fv( v[1] );
            glTexCoord2f( r[2], s[2] );
            glVertex3fv( v[2] );
         glEnd( );
      } // end for( x = 0 ; x < ( nc - 1 ) ; x++ )
   } // end for( y = 0 ; y < ( nr - 1 ) ; y++ )
} 

I am drawing this image, triangle-by-triangle and I draw a whole square each iteration through the for-loop. ncTex and nrTex are the width and height of the object in a value of a power of 2, like 512x1024. I think I am mapping the texture correctly, especially since the ppm files work. The only thing I can think of is because the pgm files are grayscale. I have tried making them into RGBA with making R=G=B and A=255, but this seemed to make no difference. Also, when I try to map a pgm file after mapping the ppm file, the ppm file remains. It is as if no change is occurring. Thanks for your help!

I think I fixed my problem. I needed the 3rd argument in glTexImage2D() to be 1 instead of 0. I don’t know how many times I read the Red Book and SuperBible and I missed that every time. But I am getting the initial pgm file to map fine, now to get secondary pgm files to work. Thanks anyways!

I am now close to getting this finished, but I am having applying other pgm files onto the one that creates the object. I have created all the objects from all the pgm files, but to texture map a different pgm file from the one that created the object is giving me problems. My code to set up my texture mapping array looks like this:

 void read_image_file_texture(fp)
FILE *fp;
{
   char *p, c;
   double scale;
   int i, j, l;

   if( ( p = read_field( fp ) ) == NULL )
   {
      printf( "File is empty.
" );
   }
   else
   {
      if( ( strcmp( p, "P6" ) !=0 ) && ( strcmp( p, "P5" ) != 0 ) ) 
      {
         printf( "image format is not ppm or pgm.
" );
      }
      else
      {
         if( strcmp( p, "P6" ) == 0 )
         {
            p = read_field( fp );
            ncPpm = atoi( p );
            p = read_field( fp );
            nrPpm = atoi( p );
            printf( "image is of size %dx%d.
", nrPpm, ncPpm );
            if ( imageTexture != NULL )
            {
               free( imageTexture );
            }

            if( ( imageTexture = (unsigned char *) malloc( nrPpm * ncPpm * 4 ) ) == NULL )
            { 
               printf( "Can't allocate memory for imageTexture.
" );
               return;
            }

            scale = get_scale( fp );
            for( i = nrPpm - 1 ; i >= 0 ; i-- )
            {
               for( j = 0 ; j < ncPpm ; j++ )
               {
                  c = fgetc( fp );
                  l = (int) ( ( (double) c ) * scale );
                  if( l > 255 )
                  {
                     l = 255;
                  }
                  imageTexture[i * ncPpm * 4 + j * 4] = (unsigned char) l;
                  c = fgetc( fp );
                  l = (int) ( ( (double) c ) * scale );
                  if( l > 255 )
                  {
                     l = 255;
                  }
                  imageTexture[i * ncPpm * 4 + j * 4 + 1] = (unsigned char) l;
                  c = fgetc( fp );
                  l = (int) ( ( (double) c ) * scale );
                  if( l > 255 )
                  {
                     l = 255;
                  }
                  imageTexture[i * ncPpm * 4 + j * 4 + 2] = (unsigned char) l;
                  imageTexture[i * ncPpm * 4 + j * 4 + 3] = 255;
               } // end for( j = 0 ; j < ncPpm ; j++ )
            } // end for( i = nrPpm - 1 ; i >= 0 ; i-- )
         } // end if( strcmp( p, "P6" ) == 0 )
         else
         {
            p = read_field( fp );
            ncPpm = atoi( p );
            p = read_field( fp );
            nrPpm = atoi( p );
            printf( "image is of size %dx%d.
", nrPpm, ncPpm );
            if ( imageTexture != NULL )
            {
               free( imageTexture );
            }

            if( ( imageTexture = (unsigned char *) malloc( nrPpm * ncPpm * 4 ) ) == NULL )
            { 
               printf( "Can't allocate memory for imageTexture.
" );
               return;
            }

            scale = get_scale( fp );
            for( i = nrPpm - 1 ; i >= 0 ; i-- )
            {
               for( j = 0 ; j < ncPpm ; j++ )
               {
                  c = fgetc( fp );
                  l = (int) ( ( (double) c ) * scale );
		            if( l > 255 )
                  {
                     l = 255;
                  }
                   // Set R=G=B since PGM files are grayscale
                  imageTexture[i * ncPpm * 4 + j * 4] = (unsigned char) l;
                  imageTexture[i * ncPpm * 4 + j * 4 + 1] = (unsigned char) l;
                  imageTexture[i * ncPpm * 4 + j * 4 + 2] = (unsigned char) l;
                  imageTexture[i * ncPpm * 4 + j * 4 + 3] = 255;
               } // end for( j = 0 ; j < ncPpm ; j++ )
            } // end for( i = nrPpm - 1 ; i >= 0 ; i-- )
         } // end else
      } // end else
   }
} 

As you can see, the only real difference between putting in the information from the first file to the second is that in the second, I make R=G=B since it is grayscale. Then to set up my texture, I call this:

 glTexImage2D( GL_TEXTURE_2D, 0, 4, ncPpmTex, nrPpmTex, 0, GL_RGBA, GL_UNSIGNED_BYTE, imageTexture ); 

Any ideas why my program crashes at the above line when the texture mapping is being done by a PGM file but works fine when being done with a PPM file since I set up my array in the same manner every time and give my PGM array RGBA values? Thanks for your help!

Just a thought
imageTexture[i * ncPpm * 4 + j * 4]
shouldn’t it be
imageTexture[i * ncPpm + j * 4]

No, with the way the array is set up, I need the * 4 in there. I did try to take it out and only the bottom 1/4 of the image was mapped. Thanks though!

Yea, but that because your only increasing j with 1 instead of 4 as you should.

the thing is that glTexImage2D is a bit sensetive about having a data that is to large, to small is sort of ok, but not to large.

I’ve just decided to convert all of my pgm files into ppm files since I can sucecssfully read in the ppm files. Thanks for your help anyways.

Converting PGM to PPM is not a good idea for me. PGM is grayscale and PPM is colour. So you increase the data size 3 times bigger.

And, there is no difference between loading PPM and PGM, except the magic number(ID). The implementation for reading PPM and PGM should be almost same.

hi , I am and old try-to-be a 3D programmer.
I am trying to set some 3D texture. When I compile my program it says that GL_TEXTUR_3D is an undeclared identifier. Help ?
thanks. I swear my next wuestion will ne more exciting…
martial

Maybe it is GL_TEXTURE_3D instead of GL_TEXTUR_3D ? :smiley:

Check if you have GL 1.2 at least. Otherwise it won’t be supported.

I figured out why I couldn’t do PGM files, my image was not 2^x by 2^x so I was trying to force it to be that. But then I realized in 2.0 texture maps don’t need this requirement so I just sent in the texture’s size and it maps. Unfortunately it maps it incorrectly. I have a PGM file of a mountain range. I also have a program that shows the top-level 2D image and I have created a window to turn this into a 3D image. When I put the windows next to each other, the peaks of my 3D mountains match the locations of the bright, white spots on the 2D image. But when I texture map the image onto the 3D scene, the white spots don’t match up to the peaks in the scene. Since my 3D image matches the 2D image, I know I am drawing the scene right. I have 2 variables, nc and nr which are the number of columns in the image (width) and number of rows (height). I use these to access my array which can be cycled through in an array like:

for( i = 0 ; i < nr ; i++ )
   {
      for( j = 0 ; j < nc ; j++ )
      {
         image[i * nc + j];
      }
   }

I use this same array to render my 3D scene and to draw the texture map. Here is my code to render my scene:

 void drawTexture( )
{
   float v[3][3];
   float normal[3];
   float r[3], s[3];
   int x;
   int y;
   for( y = 0 ; y < ( nr - 1 ) ; y++ )
   {
      for( x = 0 ; x < ( nc - 1 ) ; x++ )
      {
          // Draw the first triangle
         v[0][0] = x - nc / 2;
         v[0][1] = y - nr / 2;
         v[0][2] = image[y * nc + x];
         r[0] = ( (float) x ) / ( (float) ( nc - 1 ) );
         s[0] = ( (float) y ) / ( (float) ( nr - 1 ) );
         v[1][0] = x - nc / 2;
         v[1][1] = ( y + 1 ) - nr / 2;
         v[1][2] = image[( y + 1 ) * nc + x];
         r[1] = ( (float) x ) / ( (float) ( nc - 1 ) );
         s[1] = ( (float) ( y + 1 ) ) / ( (float) ( nr - 1 ) );
         v[2][0] = ( x + 1 ) - nc / 2;
         v[2][1] = y - nr / 2;
         v[2][2] = image[y * nc + ( x + 1 )];
         r[2] = ( (float) ( x + 1 ) ) / ( (float) ( nc - 1 ) );
         s[2] = ( (float) y ) / ( (float) ( nr - 1 ) );

         calcNormal( v, normal );
         glBegin( GL_POLYGON );
            glNormal3fv( normal );
            glTexCoord2f( r[0], s[0] );
            glVertex3fv( v[0] );
            glTexCoord2f( r[1], s[1] );
            glVertex3fv( v[1] );
            glTexCoord2f( r[2], s[2] );
            glVertex3fv( v[2] );
         glEnd( );

          // Draw the second triangle , v[0] becomes the old v[1] and 
          // v[2] stays the same
         v[0][0] = v[1][0];
         v[0][1] = v[1][1];
         v[0][2] = v[1][2];
         r[0] = r[1];
         s[0] = s[1];
         v[1][0] = ( x + 1 ) - nc / 2;
         v[1][1] = ( y + 1 ) - nr / 2;
         v[1][2] = image[( y + 1 ) * nc + ( x + 1 )];
         r[1] = ( (float) ( x + 1 ) ) / ( (float) ( nc - 1 ) );
         s[1] = ( (float) ( y + 1 ) ) / ( (float) ( nr - 1 ) );

         calcNormal( v, normal );
         glBegin( GL_POLYGON );
            glNormal3fv( normal );
            glTexCoord2f( r[0], s[0] );
            glVertex3fv( v[0] );
            glTexCoord2f( r[1], s[1] );
            glVertex3fv( v[1] );
            glTexCoord2f( r[2], s[2] );
            glVertex3fv( v[2] );
         glEnd( );
      } // end for( x = 0 ; x < ( nc - 1 ) ; x++ )
   } // end for( y = 0 ; y < ( nr - 1 ) ; y++ )
} 

I draw it triangle by triangle so I can calculate the normals for each one. I wanted to use GL_TRIANGLE_STRIP but didn’t know how to calculate the normals for each triangle in that manner so this is what I do. These two triangles forms a square each iteration through the image. I store this objectin a display list. Am I finding my glTexCoord2f incorrectly? image is the array holding my image to be made 3D and to be used as the texture map. I store this object in a display list and upon drawing this texture mapped image I enable GL_TEXTURE and disable GL_LIGHTING and set up my texture stuff this way:

        // Set filtering parameter.
      glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
      glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );

      glTexImage2D( GL_TEXTURE_2D, 0, 1, nc, nr, 0,
                    GL_LUMINANCE, GL_UNSIGNED_BYTE, image );

       // Provide texture mapping method.
      glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE ); 

But what texture maps definately doesn’t match up the correct 3D image. I will try to post some images and post them to show you. So am I doing something wrong in displaying the PGM files? I can successfully display the PPM files, but all of those files are in a 2^x-by-2^x size. Thanks!!

Here is an image of the actual 2D image, it is drawn by glDrawPixels where all of my 3D images are drawn as GL_TRIANGLES

Here is my correctly drawn 3D lighted image. You can match the peaks to each other and see the two images match.

Here is the same 3D image texture mapped with itself using the same array I used to create the scene.

And here is the scene texture mapped with an image that is of size 2^x-by-2^x and is a PPM file.

Notice you can see where the peaks of the mountains are. You can see the peaks in the 3rd image and see they don’t match up to the white spots of the image. Now you can see what I mean when they don’t match up. Any more ideas would be greatly appreciated as I have tried flipping numbers around for many hours!! Thanks!

the “black peaks” looks like the near clipping plane cuts into the mountains.
the distorsion of the texture map is simply the size you specify is not correct. And check out for alignment issues at the section 7. Watch Your Pixel Store Alignment

I know the black peaks are caused by the clipping plane, but I am saying they represent where the high points are in the 3D image and are in the same location as the white peaks on the 2D image. The size I put in for the texture map is correct because when I read it in the second and third things I read in are the width and height as stated in the file, and I save these as nc and nr. The pixel store stuff is only used by the 2D image, which was given to me and was said to be working 100% correctly. So any other ideas?

I know in OpenGL 2.0 there is supposed to be support for non 2^x by non 2^x texture mapped images, but does this work correctly? I am able to texture map 2^x by 2^x PPM and PGM files without a problem, but when I try to texture map non 2^x by non 2^x, they do not texture map correctly. Thanks for any help you have in this or if there is something I need to do to allow these images to map correctly.

Did you ensure you have support for NPOT textures, also did you ensure your code is doing the loading well ?
If this is not the first guess, then it ‘has to be’ the second one.

How do I ensure I have support for NPOT textures? I know the code is working because I can load POT textures and have no problem displaying them in the same program. It is only NPOT textures that don’t display correctly. Thanks for your help!

What hardware are you running? I know all Nvidia from 6 series and newer have NPOT support, now ATI I know x800 series and older don’t and not 100% sure on the x1000 series… I am going to assume the new x1000 series supports NPOT. HTH

I have a GeForce 6800 but I do not know what kind of cards the other machines will have so while this way is inefficient, I made another array to hold a POT texture map and just gave it black pixels where there was no actual image. It is not as efficient as using a NPOT image, but this is the best way to ensure it will work on the different systems it could be used on. Thanks for all your help!!

>> How to I ensure I have support for NPOT textures ?
glGetString(GL_EXTENSIONS); should contain “GL_ARB_texture_non_power_of_two”, as defined in the extension spec : http://oss.sgi.com/projects/ogl-sample/registry/ARB/texture_non_power_of_two.txt