Thread: openGL: textures, gluLookAt, and undefined behavior

  1. #1
    spurious conceit MK27's Avatar
    Join Date
    Jul 2008
    Location
    segmentation fault
    Posts
    8,300

    openGL: textures, gluLookAt, and undefined behavior

    Boy am I ever hoping all the math and 3D geniuses around here can help me today! This is not actually a texture question. Or indirectly it is, because it was trying to get a texture to work that led to it.

    Thus far with GL I've learned movement and lighting in 3D: I can create little scenes, use materials, get the normals right, move the camera around, move the lights around, set objects to move in predetermined orbits, etc. Intermittently, I come back to it, since most of my time is spent doing other things. So the day before yesterday I sat down hoping to learn textures, which I figured shouldn't be that hard. As far as I can tell it isn't, except I still haven't gotten one to appear in my own code.

    So I finally figured the thing to do would be to take some example code which I know works and slowly rework it to suit my needs and hopefully discover why my textures weren't appearing -- a method I've never felt compelled to resort to in my entire brief programming career before. The first thing I did (more or less) was add gluLookAt(), which is one half of my camera handling routine (the other half being, position the camera). When I used the call at the beginning of the "display" function, the scene then failed to appear despite the fact that the camera is at (0,0,50), looking at the origin where the scene is drawn. So for whatever reason (by this point, I am throwing darts at a board), I moved it to the end of the display function.

    That's where things get really interesting: now, the executable does not behave consistently. After compiling ONCE, and running the exact same binary ten times, six times the scene appeared properly and four times it appeared briefly and then immediately disappears (which that's when I realized it's really a dartboard I'm throwing things at). I've never noticed an executable do that before.

    Anyway, the code is very simple and straightforward, exercise 9-1 in the redbook. There is one -Wall warning and I noted the line (near the end); I don't think it is relevant. The gluLookAt line I added is about in the middle. The whole thing is <100 lines.
    Code:
    #include <GL/glu.h>
    #include <GL/gl.h>
    #include <GL/glext.h>
    #include <GL/glut.h>
    #include <stdlib.h>
    #include <stdio.h>
    
    #define	checkImageWidth 64
    #define	checkImageHeight 64
    
    void change (int w, int h); 
    void display(void); 
    void init(void);     
    void makeCheckImage(void); 
    
    static GLubyte checkImage[checkImageHeight][checkImageWidth][4];
    static GLuint texName;
    
    int main(int argc, char** argv) {
    	glutInit(&argc, argv);
    	glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB | GLUT_DEPTH);
    	glutInitWindowSize(1200,800);
    	glutInitWindowPosition(100, 100);
    	glutCreateWindow(argv[0]);
    	init();
    	glutDisplayFunc(display);
    	glutReshapeFunc(change);
    	glutMainLoop();
    	return 0; 
    }
    
    void change (int w, int h) {
    	float aspect=(float)w/(float)h;
    	glViewport(0, 0, (GLsizei)w, (GLsizei)h);
    	glMatrixMode(GL_PROJECTION);
    	glLoadIdentity();
    	gluPerspective(60.0f,aspect,1.0f,30.0f);
    	glMatrixMode(GL_MODELVIEW);
    	glLoadIdentity();
    	glTranslatef(0.0, 0.0, -3.6);
    }
    
    void display(void) {
    	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    	glEnable(GL_TEXTURE_2D);
    	glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);
    	glBindTexture(GL_TEXTURE_2D, texName);
    	
    	/* move gluLookAt to here and the scene never appears at all*/
    	glBegin(GL_QUADS);
    	glTexCoord2f(0.0, 0.0); glVertex3f(-2.0, -1.0, 0.0);
    	glTexCoord2f(0.0, 1.0); glVertex3f(-2.0, 1.0, 0.0);
    	glTexCoord2f(1.0, 1.0); glVertex3f(0.0, 1.0, 0.0);
    	glTexCoord2f(1.0, 0.0); glVertex3f(0.0, -1.0, 0.0);
    
    	glTexCoord2f(0.0, 0.0); glVertex3f(1.0, -1.0, 0.0);
    	glTexCoord2f(0.0, 1.0); glVertex3f(1.0, 1.0, 0.0);
    	glTexCoord2f(1.0, 1.0); glVertex3f(2.41421, 1.0, -1.41421);
    	glTexCoord2f(1.0, 0.0); glVertex3f(2.41421, -1.0, -1.41421);
    	glEnd();
    	gluLookAt(0.0f,0.0f,50.0f,0.0f,0.0f,0.0f,0.0f,1.0f,0.0f);
    	glFlush();
    	glDisable(GL_TEXTURE_2D);
    }
    
    void init(void) {    
    	glClearColor (0.0, 0.0, 0.0, 0.0);
    	glShadeModel(GL_FLAT);
    	glEnable(GL_DEPTH_TEST);
    
    	makeCheckImage();
    	glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
    
    	glGenTextures(1, &texName);
    	glBindTexture(GL_TEXTURE_2D, texName);
    
    	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, checkImageWidth, checkImageHeight, 
                    0, GL_RGBA, GL_UNSIGNED_BYTE, checkImage);
    }
    
    void makeCheckImage(void) {
       int i, j, c;
        
       for (i = 0; i < checkImageHeight; i++) {
          for (j = 0; j < checkImageWidth; j++) {
    	/* gcc wants even more parantheses here...*/
             c = ((((i&0x8)==0)^((j&0x8))==0))*255;
             checkImage[i][j][0] = (GLubyte) c;
             checkImage[i][j][1] = (GLubyte) c;
             checkImage[i][j][2] = (GLubyte) c;
             checkImage[i][j][3] = (GLubyte) 255;
          }
       }
    }
    If some nice person could at least test this just to confirm that it is not my hardware or some mysterious interstellar influence that'b great.
    C programming resources:
    GNU C Function and Macro Index -- glibc reference manual
    The C Book -- nice online learner guide
    Current ISO draft standard
    CCAN -- new CPAN like open source library repository
    3 (different) GNU debugger tutorials: #1 -- #2 -- #3
    cpwiki -- our wiki on sourceforge

  2. #2
    Registered User
    Join Date
    Oct 2006
    Posts
    250
    Your problem is in the change() function. You should place the calls to glLoadIdentity() and glTranslatef() with active modelview matrix in your main display() function.

    To see why this is so, consider: your window is created, change() is called, therefore your projection and modelview matrices are set up as per your request, i.e. projection according to perspective projection, and modelview positioned 3.6 units out of the scene.

    After that, the program proceeds to call your display() function and draws the textured quads correctly. Afterwards it adjusts the scene according to your gluLookAt() call.

    Up until now, there is no problem. The scene is drawn as expected.

    HOWEVER, if display() is called more than once in succession, WITHOUT an intermittent change() call, your modelview matrix is never reset. So when display()ing the second time, you call gluLookAt() on an already properly adjusted modelview matrix. As a result, your screen appears to go black because you are in effect directing the program to look somewhere else.

    display() is not necessarily called continuously however, only when a redraw is needed, i.e. your mouse pointer polluted the screen, or you shaded the window and reactivated it etc. You should note that resizing your window will fix the scene for you.

    You can easily fix this by moving the last two glLoadIdentity() and glTranslatef() calls from change() to display(). I.e.:

    Code:
    void change (int w, int h) {
            float aspect=(float)w/(float)h;
            glViewport(0, 0, (GLsizei)w, (GLsizei)h);
            glMatrixMode(GL_PROJECTION);
            glLoadIdentity();
            gluPerspective(60.0f,aspect,1.0f,30.0f);
            glMatrixMode(GL_MODELVIEW);
            
            //glLoadIdentity(); // move to the start of display()
            //glTranslatef(0.0, 0.0, -3.6); // same here
    }
    Remember to always call glLoadIdentity() on the modelview matrix before (re)drawing a dynamic scene with a moving camera. Any pre-positioning you perform should also be done in a display() function, instead of a change() function so that the prepositioning is performed correctly every frame, instead of only once after a window resizing operation.

    EDIT: As for where to put your gluLookAt call, you should picture in what sequence operation are performed, as in the camera analogy on page 95 of the redbook (somewhere in chapter 3). What gluLookAt does effectively (see manpage):

    gluLookAt is equivalent to

    glMultMatrixf(M);
    glTranslated(-eyex, -eyey, -eyez);
    So it first rotates the camera, assuming it is placed at the origin of the world, together with your scene. It then moves the camera (i.e. origin) away from your scene. If you were to first call gluLookAt, it would rotate in an empty world, then move the origin from the empty world. Afterwards you would be building your world on top of the camera (which is at the origin) so you would see nothing, since everything too close to the camera is clipped by either the near clipping plane, or the viewing frustum. You could attempt to draw the scene a distance away from the camera equal to the distance you moved the camera, which should result in a visible scene.
    Last edited by MWAAAHAAA; 04-23-2009 at 10:25 AM.

  3. #3
    spurious conceit MK27's Avatar
    Join Date
    Jul 2008
    Location
    segmentation fault
    Posts
    8,300
    Wow, MWAAAHAAA, you might be bang on with this; the real test will be when I try to apply the logic back into my own code.

    I moved those two lines to the beginning of display and kept gluLookAt last, and now the executable consistently does what I was hoping/expecting.

    Although the original redbook exercise was not (I suppose) intended to include gluLookAt, it seems very "hackish" to me that they (the "openGL architecture review board"!) would have a glTranslate at the end of the change/reshape function*. The reason I left it as is was because I was trying to figure out what I was doing wrong with my textures; it seems ridiculous that using the "official guide" would only compound my difficulties.

    Anyway, many thanks again for taking the time to examine the problem and present a real solution.

    * in other words, that they would write a piece of patchwork that accomplishes a primitive goal as a "demo" but is apparently not stable enough to be developed into anything else. But I like to blame my problems on other people, because I already know I'm too smart to be wrong
    C programming resources:
    GNU C Function and Macro Index -- glibc reference manual
    The C Book -- nice online learner guide
    Current ISO draft standard
    CCAN -- new CPAN like open source library repository
    3 (different) GNU debugger tutorials: #1 -- #2 -- #3
    cpwiki -- our wiki on sourceforge

  4. #4
    Registered User
    Join Date
    Oct 2006
    Posts
    250
    Quote Originally Posted by MWAAAHAAA View Post
    So it first rotates the camera, assuming it is placed at the origin of the world, together with your scene. It then moves the camera (i.e. origin) away from your scene. If you were to first call gluLookAt, it would rotate in an empty world, then move the origin from the empty world. Afterwards you would be building your world on top of the camera (which is at the origin) so you would see nothing, since everything too close to the camera is clipped by either the near clipping plane, or the viewing frustum. You could attempt to draw the scene a distance away from the camera equal to the distance you moved the camera, which should result in a visible scene.
    I just realized I misinformed you here.

    To rectify: one first positions the camera and afterwards one places the objects in the scene. That means you should first place the call to gluLookAt and then place the objects wherever you want to. The origin stays where it is: if you move the camera 50 units back, and then draw a point at the origin, you will see the point show up.

    Now the reason why you did not see your scene when you placed the gluLookAt call at the start of the scene is that it is positioned behind the far clipping plane:

    Code:
    gluPerspective(60.0f,aspect,1.0f,30.0f);
    ...
    gluLookAt(0.0f,0.0f,50.0f,0.0f,0.0f,0.0f,0.0f,1.0f,0.0f);
    You placed the far clipping plane at 30 units from the camera, and would then have moved the camera away 50 units, so that your scene is completely outside of the viewing frustum.

    The reason you were seeing anything at all when you misplaced the gluLookAt call to the end of the function is due to the glTranslatef call that moved your camera 3.6 units out.

    That should be correct. I hope I did not cause you too much confusion.

  5. #5
    spurious conceit MK27's Avatar
    Join Date
    Jul 2008
    Location
    segmentation fault
    Posts
    8,300
    Quote Originally Posted by MWAAAHAAA View Post
    You placed the far clipping plane at 30 units from the camera, and would then have moved the camera away 50 units, so that your scene is completely outside of the viewing frustum.
    That should be correct. I hope I did not cause you too much confusion.
    No way! Look, the Borg Castle is complete! You're right about the clipping/frustum thing. And I will stick to what I was trying to imply about about the openGL documenteurs: while they may pass the SAMS!* spell corrected "Publish Yourself in 24 hours" standard of computer literature, it is still subpar in my book.

    *actually SAMS is not the publisher of either book, hopefully someone gets my meaning here. And those books are not the end of the drivel either; 3Ders are a sulky bunch
    C programming resources:
    GNU C Function and Macro Index -- glibc reference manual
    The C Book -- nice online learner guide
    Current ISO draft standard
    CCAN -- new CPAN like open source library repository
    3 (different) GNU debugger tutorials: #1 -- #2 -- #3
    cpwiki -- our wiki on sourceforge

  6. #6
    Registered User
    Join Date
    Oct 2006
    Posts
    250
    That screenshot somehow reminds me of "The Sentinel".

  7. #7
    spurious conceit MK27's Avatar
    Join Date
    Jul 2008
    Location
    segmentation fault
    Posts
    8,300
    Quote Originally Posted by MWAAAHAAA View Post
    That screenshot somehow reminds me of "The Sentinel".
    The Judas Priest song? Now that's abstraction

    It made me think of the Led Zepplin record cover for "Presence" (but a lot of things do that).
    C programming resources:
    GNU C Function and Macro Index -- glibc reference manual
    The C Book -- nice online learner guide
    Current ISO draft standard
    CCAN -- new CPAN like open source library repository
    3 (different) GNU debugger tutorials: #1 -- #2 -- #3
    cpwiki -- our wiki on sourceforge

  8. #8
    Registered User
    Join Date
    Oct 2006
    Posts
    250
    Quote Originally Posted by MK27 View Post
    The Judas Priest song? Now that's abstraction
    Hehe, I meant the 1986 computer game.

Popular pages Recent additions subscribe to a feed