/* * rasonly.c - * Demonstrates the use of OpenGL for rasterization-only, with * perspective-correct texture mapping. * * Michael I. Gold * Silicon Graphics Computer Systems, May 1997 * * Since current low-end 3D accelerators support only rasterization in * hardware, a number of developers have expressed interested in using * OpenGL as an interface to rasterization hardware while retaining * control of transformations and lighting in the application code. * Many OpenGL implementations detect and optimize for identity xforms, * so this approach is entirely reasonable. * * Setting up rasterization-only is fairly straightforward. The projection * matrix is set up as a one-to-one mapping between eye and clip coordinates, * and the modelview matrix is set up as identity, e.g. object coordinates * map directly to eye coordinates. This can be achieved as follows: * * glMatrixMode(GL_PROJECTION); * glLoadIdentity(); * glOrtho(0.0f, (GLfloat) width, 0.0f, (GLfloat) height, -1.0f, 1.0f); * glMatrixMode(GL_MODELVIEW); * glLoadIdentity(); * glViewport(0, 0, width, height); * * where (width, height) represent the window dimensions. * * Now transformed geometry may be specified directly through the standard * interfaces (e.g. glVertex*()). The only tricky part that remains is * specifying texture coordinates such that perspective correction may * occur. The answer is to use glTexCoord4*(), and perform the perspective * divide on the texture coordinates directly. */ #include #include #include GLboolean motion = GL_TRUE; /* Matrices */ GLfloat rot = 0.0f; GLfloat ModelView[16]; GLfloat Projection[16]; GLfloat Viewport[4]; /* Sample geometry */ GLfloat quadV[][4] = { { -1.0f, 0.0f, -1.0f, 1.0f }, { 1.0f, 0.0f, -1.0f, 1.0f }, { 1.0f, 0.5f, -0.2f, 1.0f }, { -1.0f, 0.5f, -0.2f, 1.0f }, }; GLfloat quadC[][3] = { { 1.0f, 0.0f, 0.0f }, { 0.0f, 1.0f, 0.0f }, { 0.0f, 0.0f, 1.0f }, { 1.0f, 1.0f, 1.0f }, }; GLfloat quadT[][2] = { { 0.0f, 0.0f }, { 0.0f, 1.0f }, { 1.0f, 1.0f }, { 1.0f, 0.0f }, }; /********************************************************************* * Utility functions */ int texWidth = 128; int texHeight = 128; /* Create and download the application texture map */ static void setCheckedTexture(void) { int texSize; void *textureBuf; GLubyte *p; int i,j; /* malloc for rgba as worst case */ texSize = texWidth*texHeight*4; textureBuf = malloc(texSize); if (NULL == textureBuf) return; p = (GLubyte *)textureBuf; for (i=0; i < texWidth; i++) { for (j=0; j < texHeight; j++) { if ((i ^ j) & 8) { p[0] = 0xff; p[1] = 0xff; p[2] = 0xff; p[3] = 0xff; } else { p[0] = 0x08; p[1] = 0x08; p[2] = 0x08; p[3] = 0xff; } p += 4; } } gluBuild2DMipmaps(GL_TEXTURE_2D, 4, texWidth, texHeight, GL_RGBA, GL_UNSIGNED_BYTE, textureBuf); free(textureBuf); } /* Perform one transform operation */ static void Transform(GLfloat *matrix, GLfloat *in, GLfloat *out) { int ii; for (ii=0; ii<4; ii++) { out[ii] = in[0] * matrix[0*4+ii] + in[1] * matrix[1*4+ii] + in[2] * matrix[2*4+ii] + in[3] * matrix[3*4+ii]; } } /* Transform a vertex from object coordinates to window coordinates. * Lighting is left as an exercise for the reader. */ static void DoTransform(GLfloat *in, GLfloat *out) { GLfloat tmp[4]; GLfloat invW; /* 1/w */ /* Modelview xform */ Transform(ModelView, in, tmp); /* Lighting calculation goes here! */ /* Projection xform */ Transform(Projection, tmp, out); if (out[3] == 0.0f) /* do what? */ return; invW = 1.0f / out[3]; /* Perspective divide */ out[0] *= invW; out[1] *= invW; out[2] *= invW; /* Map to 0..1 range */ out[0] = out[0] * 0.5f + 0.5f; out[1] = out[1] * 0.5f + 0.5f; out[2] = out[2] * 0.5f + 0.5f; /* Map to viewport */ out[0] = out[0] * Viewport[2] + Viewport[0]; out[1] = out[1] * Viewport[3] + Viewport[1]; /* Store inverted w for performance */ out[3] = invW; } /********************************************************************* * Application code begins here */ /* For the sake of brevity, I'm use OpenGL to compute my matrices. */ void UpdateModelView(void) { glPushMatrix(); glLoadIdentity(); gluLookAt(0.0f, 1.0f, -4.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f); glRotatef(rot, 0.0f, 1.0f, 0.0f); /* Retrieve the matrix */ glGetFloatv(GL_MODELVIEW_MATRIX, ModelView); glPopMatrix(); } void InitMatrices(void) { /* Calculate projection matrix */ glMatrixMode(GL_PROJECTION); glPushMatrix(); glLoadIdentity(); gluPerspective(45.0f, 1.0f, 1.0f, 100.0f); /* Retrieve the matrix */ glGetFloatv(GL_PROJECTION_MATRIX, Projection); glPopMatrix(); glMatrixMode(GL_MODELVIEW); UpdateModelView(); } void Init(void) { glClearColor(0.2f, 0.2f, 0.6f, 1.0f); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); glEnable(GL_TEXTURE_2D); glEnable(GL_DEPTH_TEST); setCheckedTexture(); InitMatrices(); } void Redraw(void) { GLfloat tmp[4]; int ii; glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT); glBegin(GL_QUADS); for (ii = 0; ii < 4; ii++) { /* Transform a vertex from object to window coordinates. * 1/w is returned as tmp[3] for perspective-correcting * the texture coordinates. */ DoTransform(quadV[ii], tmp); /* Ideally the colors will be computed by the lighting equation, * but I've hard-coded values for this example. */ glColor3fv(quadC[ii]); /* Scale by 1/w (stored in tmp[3]) */ glTexCoord4f(quadT[ii][0] * tmp[3], quadT[ii][1] * tmp[3], 0.0f, tmp[3]); /* Note I am using Vertex3, not Vertex4, since we have already * performed the perspective divide. */ glVertex3fv(tmp); } glEnd(); glutSwapBuffers(); } void Motion(void) { rot += 3.0f; if (rot >= 360.0f) rot -= 360.0f; UpdateModelView(); Redraw(); } /* ARGSUSED1 */ void Key(unsigned char key, int x, int y) { switch (key) { case 27: exit(0); case 'm': motion = !motion; glutIdleFunc(motion ? Motion : NULL); break; } } /* ARGSUSED1 */ void Button(int button, int state, int x, int y) { switch (button) { case GLUT_LEFT_BUTTON: if (state == GLUT_DOWN) { rot -= 15.0f; UpdateModelView(); Redraw(); } break; case GLUT_RIGHT_BUTTON: if (state == GLUT_DOWN) { rot += 15.0f; UpdateModelView(); Redraw(); } break; } } void Reshape(int width, int height) { glMatrixMode(GL_PROJECTION); glLoadIdentity(); glOrtho(0.0f, (GLfloat) width, 0.0f, (GLfloat) height, -1.0f, 1.0f); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glViewport(0, 0, width, height); Viewport[0] = Viewport[1] = 0.0f; Viewport[2] = (GLfloat) width; Viewport[3] = (GLfloat) height; } int main(int argc, char *argv[]) { char *t; glutInit(&argc, argv); glutInitDisplayMode(GLUT_RGB|GLUT_DOUBLE|GLUT_DEPTH); glutInitWindowSize(400, 400); glutCreateWindow((t=strrchr(argv[0], '\\')) != NULL ? t+1 : argv[0]); Init(); glutDisplayFunc(Redraw); glutReshapeFunc(Reshape); glutKeyboardFunc(Key); glutMouseFunc(Button); glutIdleFunc(Motion); glutMainLoop(); return 0; }