/* cgfx_bumpdemo.c - OpenGL-based bump mapping example using a CgFX effect with Cg programs from Chapter 8 of "The Cg Tutorial" (Addison-Wesley, ISBN 0321194969). */ /* Explained in the whitepaper "Re-implementing the Follow-up Cg Runtime Tutorial with CgFX" by Mark Kilgard. */ /* Requires the OpenGL Utility Toolkit (GLUT) and Cg runtime (version 1.4 or higher). */ #include #include #include #include #include #include CGcontext myCgContext; CGeffect myCgEffect; CGtechnique myCgTechnique; CGparameter myCgEyePositionParam, myCgModelViewProjParam; static void initCgFX(); static void initOpenGL(); static void display(void); static void keyboard(unsigned char c, int x, int y); int main(int argc, char **argv) { glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH); glutInitWindowSize(400, 400); glutInit(&argc, argv); glutCreateWindow("cgfx_bumpdemo"); initCgFX(); initOpenGL(); glutDisplayFunc(display); glutKeyboardFunc(keyboard); glutMainLoop(); return 0; } static void checkForCgError(const char *situation) { CGerror error; const char *string = cgGetLastErrorString(&error); if (error != CG_NO_ERROR) { printf("cgfx_bumpdemo: %s: %s\n", situation, string); if (error == CG_COMPILER_ERROR) { printf("%s\n", cgGetLastListing(myCgContext)); } exit(1); } } static void initCgFX(void) { myCgContext = cgCreateContext(); cgGLRegisterStates(myCgContext); cgGLSetManageTextureParameters(myCgContext, CG_TRUE); checkForCgError("establishing Cg context"); myCgEffect = cgCreateEffectFromFile(myCgContext, "bumpdemo.cgfx", NULL); checkForCgError("creating bumpdemo.cgfx effect"); myCgTechnique = cgGetFirstTechnique(myCgEffect); while (myCgTechnique && cgValidateTechnique(myCgTechnique) == CG_FALSE) { fprintf(stderr, "cgfx_bumpdemo: Technique %s did not validate. Skipping.\n", cgGetTechniqueName(myCgTechnique)); myCgTechnique = cgGetNextTechnique(myCgTechnique); } if (myCgTechnique) { fprintf(stderr, "cgfx_bumpdemo: Use technique %s.\n", cgGetTechniqueName(myCgTechnique)); } else { fprintf(stderr, "cgfx_bumpdemo: No valid technique\n"); exit(1); } myCgModelViewProjParam = cgGetEffectParameterBySemantic(myCgEffect, "ModelViewProjection"); if (!myCgModelViewProjParam) { fprintf(stderr, "cgfx_bumpdemo: no parameter with ModelViewProjection semantic\n"); exit(1); } myCgEyePositionParam = cgGetNamedEffectParameter(myCgEffect, "EyePosition"); if (!myCgEyePositionParam) { fprintf(stderr, "cgfx_bumpdemo: no parameter named EyePosition\n"); exit(1); } } /* OpenGL texture object (TO) handles. */ enum { TO_NORMALIZE_VECTOR_CUBE_MAP = 1, TO_NORMAL_MAP = 2, }; static const GLubyte myBrickNormalMapImage[3*(128*128+64*64+32*32+16*16+8*8+4*4+2*2+1*1)] = { /* RGB8 image data for mipmapped 128x128 normal map for a brick pattern */ #include "brick_image.h" }; static const GLubyte myNormalizeVectorCubeMapImage[6*3*32*32] = { /* RGB8 image data for normalization vector cube map with 32x32 faces */ #include "normcm_image.h" }; static void useSamplerParameter(CGeffect effect, const char *paramName, GLuint texobj) { CGparameter param; param = cgGetNamedEffectParameter(effect, paramName); if (!param) { fprintf(stderr, "cgfx_bumpdemo: expected effect parameter named %s\n", paramName); exit(1); } cgGLSetTextureParameter(param, texobj); cgSetSamplerState(param); } static void initOpenGL(void) { const GLubyte *image; unsigned int face; glPixelStorei(GL_UNPACK_ALIGNMENT, 1); /* Tightly packed texture data. */ /* OpenGL tokens for cube maps missing from Windows version of */ #define GL_TEXTURE_CUBE_MAP 0x8513 #define GL_TEXTURE_CUBE_MAP_POSITIVE_X 0x8515 glBindTexture(GL_TEXTURE_CUBE_MAP, TO_NORMALIZE_VECTOR_CUBE_MAP); useSamplerParameter(myCgEffect, "normalizeCube", TO_NORMALIZE_VECTOR_CUBE_MAP); /* Load each 32x32 face (without mipmaps) of range-compressed "normalize vector" cube map. */ for (face = 0, image = myNormalizeVectorCubeMapImage; face < 6; face++, image += 3*32*32) { glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, 0, GL_RGB8, 32, 32, 0, GL_RGB, GL_UNSIGNED_BYTE, image); } glBindTexture(GL_TEXTURE_2D, TO_NORMAL_MAP); useSamplerParameter(myCgEffect, "normalMap", TO_NORMAL_MAP); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, 128, 128, 0, GL_RGB, GL_UNSIGNED_BYTE, myBrickNormalMapImage); glEnable(GL_DEPTH_TEST); glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective( 60.0, /* Field of view in degree */ 1.0, /* Aspect ratio */ 0.1, /* Z near */ 100.0); /* Z far */ glMatrixMode(GL_MODELVIEW); glClearColor(0.1, 0.3, 0.6, 0.0); /* Blue background */ } /* Draw a flat 2D patch that can be "rolled & bent" into a 3D torus by a vertex program. */ void drawFlatPatch(float rows, float columns) { const float m = 1.0f/columns; const float n = 1.0f/rows; int i, j; for (i=0; i 2*3.14159) myEyeAngle -= 2*3.14159; glutPostRedisplay(); } static void keyboard(unsigned char c, int x, int y) { static int animating = 0; switch (c) { case ' ': animating = !animating; /* Toggle */ glutIdleFunc(animating ? advanceAnimation : NULL); break; case 27: /* Esc key */ cgDestroyEffect(myCgEffect); cgDestroyContext(myCgContext); exit(0); break; } }