/* * Test rendering into a cube map texture with FBOs. * * Brian Paul * May 2011 */ #include #include #include #include #include #include #include "glut_wrap.h" #include "shaderutil.h" #define DEG_TO_RAD (3.14159 / 180.0) #define ELEMENTS(array) (sizeof(array) / sizeof(array[0])) static GLint WindowWidth = 750, WindowHeight = 450; static GLint Win; static GLfloat Xrot = 10, Yrot = 0, Zrot = 0; static GLfloat GroundColor[4] = {0.5, 0.5, 0.75, 1}; static GLfloat GroundY = -1.0f; static GLfloat ViewDist = 30.0; static GLfloat SpherePos[4] = { 0, 2.5, 0, 1 }; static GLfloat LightDist = 15; static GLfloat LightLatitude = 30.0; static GLfloat LightLongitude = 45.0; static GLfloat LightPos[4]; static GLboolean Anim = GL_TRUE; static GLboolean NeedNewCubeMap = GL_TRUE; static GLuint CubeTexture; struct cylinder { float PosX, PosY; float Radius, Height; float Color[4]; }; #define NUM_CYLINDERS 30 static struct cylinder Cylinders[NUM_CYLINDERS]; static float RandomFloat(float min, float max) { const int k = 10000; float t = (rand() % k) / (float) (k - 1); /* t in [0,1] */ float r = min + t * (max - min); return r; } static void CheckError(int line) { GLenum err = glGetError(); if (err) { printf("GL Error 0x%x at line %d\n", (int) err, line); } } static void GenerateCylinders(void) { int i; for (i = 0; i < NUM_CYLINDERS; i++) { float r = RandomFloat(5.0, 9.0); float a = RandomFloat(0, 2 * M_PI); Cylinders[i].PosX = r * cos(a); Cylinders[i].PosY = r * sin(a); Cylinders[i].Radius = RandomFloat(0.25, 1.0); Cylinders[i].Height = RandomFloat(1.0, 7.0); Cylinders[i].Color[0] = RandomFloat(0.25, 1.0); Cylinders[i].Color[1] = RandomFloat(0.25, 1.0); Cylinders[i].Color[2] = RandomFloat(0.25, 1.0); Cylinders[i].Color[3] = RandomFloat(0.25, 1.0); } } static void DrawCylinder(const struct cylinder *cyl) { glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, cyl->Color); glPushMatrix(); glTranslatef(cyl->PosX, GroundY, cyl->PosY); glRotatef(-90, 1, 0, 0); glutSolidCone(cyl->Radius, cyl->Height, 12, 2); glPopMatrix(); } static void DrawLightSource(void) { glPushMatrix(); glTranslatef(LightPos[0], LightPos[1], LightPos[2]); glColor3f(1, 1, 1); glutSolidSphere(0.25, 8, 8); glPopMatrix(); } static void DrawScene(void) { int i; glEnable(GL_LIGHTING); for (i = 0; i < NUM_CYLINDERS; i++) { DrawCylinder(&Cylinders[i]); } glDisable(GL_LIGHTING); if (1) DrawLightSource(); /* ground plane */ if (1) { GLfloat k = 10.0, g = GroundY; CheckError(__LINE__); glPushMatrix(); glColor3fv(GroundColor); glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, GroundColor); glNormal3f(0, 1, 0); glBegin(GL_POLYGON); glVertex3f(-k, g, -k); glVertex3f( k, g, -k); glVertex3f( k, g, k); glVertex3f(-k, g, k); glEnd(); glPopMatrix(); } CheckError(__LINE__); } /* * Draw sphere with cube map texture that reflects rest of the scene. */ static void DrawShinySphere(void) { glEnable(GL_TEXTURE_CUBE_MAP); #if 0 glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_REFLECTION_MAP_ARB); glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_REFLECTION_MAP_ARB); glTexGeni(GL_R, GL_TEXTURE_GEN_MODE, GL_REFLECTION_MAP_ARB); #else glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_NORMAL_MAP_ARB); glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_NORMAL_MAP_ARB); glTexGeni(GL_R, GL_TEXTURE_GEN_MODE, GL_NORMAL_MAP_ARB); #endif glEnable(GL_TEXTURE_GEN_S); glEnable(GL_TEXTURE_GEN_T); glEnable(GL_TEXTURE_GEN_R); glPushMatrix(); glTranslatef(SpherePos[0], SpherePos[1], SpherePos[2]); glColor3f(0.75, 0.75, 0.75); if (1) { glMatrixMode(GL_TEXTURE); glLoadIdentity(); glRotatef(-Yrot, 0, 1, 0); glRotatef(-Xrot, 1, 0, 0); } glutSolidSphere(2.5, 16, 16); glMatrixMode(GL_MODELVIEW); glPopMatrix(); glDisable(GL_TEXTURE_CUBE_MAP); glDisable(GL_TEXTURE_GEN_S); glDisable(GL_TEXTURE_GEN_T); glDisable(GL_TEXTURE_GEN_R); } /** * Setup modelview and projection for drawing a cube face. */ static void SetupCubeFaceView(GLenum face, const GLfloat centerPos[4]) { GLfloat near_val = 0.5, far_val = 20.0; glMatrixMode(GL_PROJECTION); glLoadIdentity(); glFrustum(-near_val, near_val, -near_val, near_val, near_val, far_val); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); switch (face) { case GL_TEXTURE_CUBE_MAP_POSITIVE_X: glRotatef(180, 1, 0, 0); glRotatef(-90, 0, 1, 0); break; case GL_TEXTURE_CUBE_MAP_NEGATIVE_X: glRotatef(180, 1, 0, 0); glRotatef(90, 0, 1, 0); break; case GL_TEXTURE_CUBE_MAP_POSITIVE_Y: glRotatef(-90, 1, 0, 0); break; case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y: glRotatef(90, 1, 0, 0); break; case GL_TEXTURE_CUBE_MAP_POSITIVE_Z: glRotatef(180, 1, 0, 0); break; case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z: glRotatef(0, 1, 0, 0); glRotatef(180, 0, 0, 1); break; } glTranslatef(-centerPos[0], -centerPos[1], -centerPos[2]); glLightfv(GL_LIGHT0, GL_POSITION, LightPos); } static void ComputeLightPos(GLfloat dist, GLfloat latitude, GLfloat longitude, GLfloat pos[4]) { pos[0] = dist * sin(longitude * DEG_TO_RAD); pos[1] = dist * sin(latitude * DEG_TO_RAD); pos[2] = dist * cos(latitude * DEG_TO_RAD) * cos(longitude * DEG_TO_RAD); pos[3] = 1; } static GLint ChooseCubeSize(void) { if (WindowWidth >= 1024 && WindowHeight >= 1024) { return 1024; } else if (WindowWidth >= 512 && WindowHeight >= 512) { return 512; } else if (WindowWidth >= 256 && WindowHeight >= 256) { return 256; } else { return 128; } } static GLuint CreateCubeTexture(GLint size) { GLboolean linearFilter = GL_TRUE; GLuint cube, face; glGenTextures(1, &cube); glBindTexture(GL_TEXTURE_CUBE_MAP, cube); /* Set the filter mode so that the texture is texture-complete. * Otherwise it will cause the framebuffer to fail the framebuffer * completeness test. */ if (linearFilter) { glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR); } else { glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_NEAREST); } glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); for (face = 0; face < 6; face++) { glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, 0, GL_RGBA, size, size, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); } CheckError(__LINE__); return cube; } /** * Render the six faces of a cube map. */ static void RenderCubeMap(void) { GLuint fbo, face, depthStencilRb; GLint cubeSize; CheckError(__LINE__); cubeSize = ChooseCubeSize(); printf("Rendering %d x %d cube texture\n", cubeSize, cubeSize); CubeTexture = CreateCubeTexture(cubeSize); /* * Make FBO. */ glGenFramebuffersEXT(1, &fbo); glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo); /* * Make depth/stencil renderbuffer. */ glGenRenderbuffersEXT(1, &depthStencilRb); glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, depthStencilRb); glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_STENCIL, cubeSize, cubeSize); glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, depthStencilRb); glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, depthStencilRb); glViewport(0, 0, cubeSize, cubeSize); /* * Render into cube faces. */ for (face = 0; face < 6; face++) { GLenum status; /* Render color into face of cubemap */ glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, CubeTexture, 0); status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT); CheckError(__LINE__); if (status != GL_FRAMEBUFFER_COMPLETE_EXT) { fprintf(stderr, "FBO not complete! status = 0x%04x\n", status); assert(status == GL_FRAMEBUFFER_COMPLETE_EXT); } CheckError(__LINE__); /* Render the depth image into cube face */ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); SetupCubeFaceView(GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, SpherePos); DrawScene(); } glDeleteRenderbuffersEXT(1, &depthStencilRb); glDeleteFramebuffersEXT(1, &fbo); glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); CheckError(__LINE__); } /** * Redraw window image */ static void Display(void) { const GLfloat ar = (GLfloat) WindowWidth / (GLfloat) WindowHeight; ComputeLightPos(LightDist, LightLatitude, LightLongitude, LightPos); if (NeedNewCubeMap) { /* regenerate cube map faces */ RenderCubeMap(); NeedNewCubeMap = GL_FALSE; } glViewport(0, 0, WindowWidth, WindowHeight); /* draw scene from camera's view */ glMatrixMode(GL_PROJECTION); glLoadIdentity(); glFrustum(-ar, ar, -1.0, 1.0, 4.0, 50.0); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glTranslatef(0.0, 0.0, -ViewDist); glRotatef(Xrot, 1, 0, 0); glRotatef(Yrot, 0, 1, 0); glRotatef(Zrot, 0, 0, 1); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); glLightfv(GL_LIGHT0, GL_POSITION, LightPos); DrawScene(); DrawShinySphere(); glutSwapBuffers(); } static void Reshape(int width, int height) { WindowWidth = width; WindowHeight = height; NeedNewCubeMap = GL_TRUE; } static void Idle(void) { static double t0 = -1.; double dt, t = glutGet(GLUT_ELAPSED_TIME) / 1000.0; if (t0 < 0.0) t0 = t; dt = t - t0; t0 = t; Yrot += 40.0 * dt; glutPostRedisplay(); } static void Key(unsigned char key, int x, int y) { const GLfloat step = 3.0; (void) x; (void) y; switch (key) { case 'a': Anim = !Anim; if (Anim) glutIdleFunc(Idle); else glutIdleFunc(NULL); break; case 'r': GenerateCylinders(); NeedNewCubeMap = GL_TRUE; break; case 'z': Zrot -= step; break; case 'Z': Zrot += step; break; case 27: glutDestroyWindow(Win); exit(0); break; } fflush(stdout); glutPostRedisplay(); } static void SpecialKey(int key, int x, int y) { const GLfloat step = 3.0; const int mod = glutGetModifiers(); (void) x; (void) y; switch (key) { case GLUT_KEY_UP: if (mod) //LightLatitude += step; SpherePos[1] += .1*step; else Xrot += step; break; case GLUT_KEY_DOWN: if (mod) //LightLatitude -= step; SpherePos[1] -= .1*step; else Xrot -= step; break; case GLUT_KEY_LEFT: if (mod) LightLongitude += step; else Yrot += step; break; case GLUT_KEY_RIGHT: if (mod) LightLongitude -= step; else Yrot -= step; break; } if (mod) NeedNewCubeMap = GL_TRUE; glutPostRedisplay(); } static void Init(void) { const char *extensions[3] = { "GL_ARB_depth_texture", "GL_ARB_texture_cube_map", "GL_EXT_framebuffer_object" }; int i; for (i = 0; i < ELEMENTS(extensions); i++) { if (!glutExtensionSupported(extensions[i])) { printf("Sorry, this demo requires %s\n", extensions[i]); exit(1); } } glClearColor(0.25, 0.25, 0.25, 1.0); glEnable(GL_DEPTH_TEST); glEnable(GL_LIGHTING); glEnable(GL_LIGHT0); GenerateCylinders(); printf("GL_RENDERER = %s\n", (char *) glGetString(GL_RENDERER)); } static void PrintHelp(void) { printf("Keys:\n"); printf(" a = toggle animation\n"); printf(" cursor keys = rotate scene\n"); printf(" + up/down = move sphere\n"); printf(" + left/right = move light\n"); fflush(stdout); } int main(int argc, char *argv[]) { glutInitWindowSize(WindowWidth, WindowHeight); glutInit(&argc, argv); glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH | GLUT_STENCIL); Win = glutCreateWindow(argv[0]); glewInit(); glutReshapeFunc(Reshape); glutKeyboardFunc(Key); glutSpecialFunc(SpecialKey); glutDisplayFunc(Display); if (Anim) glutIdleFunc(Idle); Init(); PrintHelp(); glutMainLoop(); return 0; }