// uncomment exactly one of these... // #define SEE_COLORS // #define SEE_NORMALS // #define SEE_DEPTH #define SEE_OUTPUT // uncomment this to use raw DEPTH_COMPONENT // #define BUGGY_PACKED_DEPTH_STENCIL #include #include #include #if defined(__APPLE__) #include #else #include #include #endif #include "shader.h" #define extension_available(name) \ gluCheckExtension((const GLubyte *)"GL_" #name, glGetString(GL_EXTENSIONS)) static const char *framebuffer_status_to_string(GLenum status) { switch (status) { case GL_FRAMEBUFFER_COMPLETE_EXT : return "FRAMEBUFFER_COMPLETE_EXT 0x8CD5"; case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT : return "FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT 0x8CD6"; case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT : return "FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT 0x8CD7"; case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT : return "FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT 0x8CD9"; case GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT : return "FRAMEBUFFER_INCOMPLETE_FORMATS_EXT 0x8CDA"; case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT : return "FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT 0x8CDB"; case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT : return "FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT 0x8CDC"; case GL_FRAMEBUFFER_UNSUPPORTED_EXT : return "FRAMEBUFFER_UNSUPPORTED_EXT 0x8CDD"; } abort(); } const char *storage_vertex_shader_text = "varying vec3 normal;\n" "\n" "void main()\n" "{\n" " gl_Position = ftransform();\n" " normal = gl_NormalMatrix * gl_Normal;\n" "}\n" ; const char *storage_fragment_shader_text = "varying vec3 normal;\n" "\n" "void main()\n" "{\n" " gl_FragData[0].rgba = vec4(0.5, 1.0, 0.5, 1.0) *\n" " vec4(dot(normalize(normal), vec3(1.0, 1.0, 0.0)));\n" "\n" " gl_FragData[1].rgb = normalize(normal);\n" " gl_FragData[1].a = 1.0;\n" "}\n" ; const char *toon_vertex_shader_text = "void main()\n" "{\n" " gl_Position = ftransform();\n" " gl_TexCoord[0] = gl_TextureMatrix[0] * gl_MultiTexCoord0;\n" "}\n" ; const char *toon_fragment_shader_text = #if defined(SEE_COLORS) || defined(SEE_NORMALS) || defined(SEE_DEPTH) "uniform sampler2DRect colors;\n" "uniform sampler2DRect normals;\n" "uniform sampler2DRect depth;\n" "void main()\n" "{\n" #if defined(SEE_COLORS) " gl_FragColor = texture2DRect(colors, gl_TexCoord[0].st);\n" #elif defined(SEE_NORMALS) " gl_FragColor = texture2DRect(normals, gl_TexCoord[0].st);\n" #elif defined(SEE_DEPTH) " gl_FragColor = texture2DRect(depth, gl_TexCoord[0].st);\n" #else #error whoopsie #endif "}\n" #elif defined(SEE_OUTPUT) "uniform sampler2DRect colors;\n" "uniform sampler2DRect normals;\n" "uniform sampler2DRect depth;\n" "\n" "const float ds = 1.0;\n" "const float dt = 1.0;\n" "\n" "const float depth_threshold = 0.01;\n" "const float normal_threshold = 0.97;\n" "\n" "void main()\n" "{\n" " float depth_x0y0 = texture2DRect(depth, gl_TexCoord[0].st + vec2(-ds, -dt)).r;\n" " float depth_x2y0 = texture2DRect(depth, gl_TexCoord[0].st + vec2( ds, -dt)).r;\n" " float depth_x1y1 = texture2DRect(depth, gl_TexCoord[0].st + vec2(0.0, 0.0)).r;\n" " float depth_x0y2 = texture2DRect(depth, gl_TexCoord[0].st + vec2(-ds, dt)).r;\n" " float depth_x2y2 = texture2DRect(depth, gl_TexCoord[0].st + vec2( ds, dt)).r;\n" " \n" " float dx0y0 = abs(depth_x1y1 - depth_x0y0);\n" " float dx2y0 = abs(depth_x1y1 - depth_x2y0);\n" " float dx0y2 = abs(depth_x1y1 - depth_x0y2);\n" " float dx2y2 = abs(depth_x1y1 - depth_x2y2);\n" " \n" " float magnitude = dx0y0 + dx2y0 + dx0y2 + dx2y2 - depth_threshold;\n" " float depth_value = 1.0 - ceil(magnitude);\n" " \n" " vec3 normal_x0y0 = texture2DRect(normals, gl_TexCoord[0].st + vec2(-ds, -dt)).rgb;\n" " vec3 normal_x2y0 = texture2DRect(normals, gl_TexCoord[0].st + vec2( ds, -dt)).rgb;\n" " vec3 normal_x1y1 = texture2DRect(normals, gl_TexCoord[0].st + vec2(0.0, 0.0)).rgb;\n" " vec3 normal_x0y2 = texture2DRect(normals, gl_TexCoord[0].st + vec2(-ds, dt)).rgb;\n" " vec3 normal_x2y2 = texture2DRect(normals, gl_TexCoord[0].st + vec2( ds, dt)).rgb;\n" " \n" " float dot_x0y0 = dot(normal_x1y1, normal_x0y0) - normal_threshold;\n" " float dot_x2y0 = dot(normal_x1y1, normal_x2y0) - normal_threshold;\n" " float dot_x0y2 = dot(normal_x1y1, normal_x0y2) - normal_threshold;\n" " float dot_x2y2 = dot(normal_x1y1, normal_x2y2) - normal_threshold;\n" " \n" " float normal_value = ceil(dot_x0y0) * \n" " ceil(dot_x2y0) * \n" " ceil(dot_x0y2) * \n" " ceil(dot_x2y2);\n" " \n" " float gray = normal_value * depth_value;\n" " \n" " vec4 color = texture2DRect(colors, gl_TexCoord[0].st);\n" " \n" " gl_FragColor = color * gray;\n" "}\n" #else #error uncomment exactly one SEE_x define at the top of main.c #endif ; GLuint color_texture = 0; GLuint normal_texture = 0; GLuint depth_texture = 0; GLuint fbo = 0; shader_t storage_shader = NULL; shader_t toon_shader = NULL; #define ROTATION_SPEED 2.0 void display(void) { static int frame_count = 0; ++frame_count; static int last_fps_time = 0; if (last_fps_time == 0) { last_fps_time = glutGet(GLUT_ELAPSED_TIME); } static int last_frame_time = 0; if (last_frame_time == 0) { last_frame_time = glutGet(GLUT_ELAPSED_TIME); } int now = glutGet(GLUT_ELAPSED_TIME); double dt = 0.001 * (now - last_frame_time); last_frame_time = now; int elapsed_since_last_fps_time = now - last_fps_time; if (elapsed_since_last_fps_time > 1000) { printf( "%0.2f FPS\n", frame_count / (0.001 * elapsed_since_last_fps_time)); frame_count = 0; last_fps_time = now; } glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo); { glViewport(0, 0, 640, 480); GLenum buffers[2] = { GL_COLOR_ATTACHMENT0_EXT, GL_COLOR_ATTACHMENT1_EXT }; glDrawBuffersARB(2, buffers); glClearColor(1.0, 1.0, 0.0, 0.0); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); static double angle = 0.0; angle += ROTATION_SPEED * dt; if (angle > M_PI) { angle -= 2.0 * M_PI; } glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(45.0, 1.3333, 1.0, 20.0); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); gluLookAt( 5.0 * cos(angle), 2.0, 5.0 * sin(angle), 0.0, 0.0, 0.0, 0.0, 1.0, 0.0); glEnable(GL_DEPTH_TEST); glUseProgramObjectARB(shader_program(storage_shader)); { glPushMatrix(); glTranslatef(-2.0, 0.0, 0.0); glutSolidCube(1.0); glPopMatrix(); glPushMatrix(); glTranslatef(2.0, 0.0, 0.0); glutSolidSphere(1.0, 64, 64); glPopMatrix(); } glUseProgramObjectARB(0); glDisable(GL_DEPTH_TEST); } glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); { glViewport(0, 0, glutGet(GLUT_WINDOW_WIDTH), glutGet(GLUT_WINDOW_HEIGHT)); glDrawBuffer(GL_BACK); glMatrixMode(GL_PROJECTION); glLoadIdentity(); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glUseProgramObjectARB(shader_program(toon_shader)); glActiveTextureARB(GL_TEXTURE0_ARB); glBindTexture(GL_TEXTURE_RECTANGLE_ARB, color_texture); glActiveTextureARB(GL_TEXTURE1_ARB); glBindTexture(GL_TEXTURE_RECTANGLE_ARB, normal_texture); glActiveTextureARB(GL_TEXTURE2_ARB); glBindTexture(GL_TEXTURE_RECTANGLE_ARB, depth_texture); { glBegin(GL_QUADS); glTexCoord2i(0, 0); glVertex2f(-1.0f, -1.0f); glTexCoord2i(640, 0); glVertex2f( 1.0f, -1.0f); glTexCoord2i(640, 480); glVertex2f( 1.0f, 1.0f); glTexCoord2i(0, 480); glVertex2f(-1.0f, 1.0f); glEnd(); } glActiveTextureARB(GL_TEXTURE2_ARB); glBindTexture(GL_TEXTURE_RECTANGLE_ARB, 0); glActiveTextureARB(GL_TEXTURE1_ARB); glBindTexture(GL_TEXTURE_RECTANGLE_ARB, 0); glActiveTextureARB(GL_TEXTURE0_ARB); glBindTexture(GL_TEXTURE_RECTANGLE_ARB, 0); glUseProgramObjectARB(0); } glutSwapBuffers(); glutReportErrors(); } void reshape(int width, int height) { // unnecessary maybe? glViewport(0, 0, width, height); } void idle(void) { glutPostRedisplay(); } int main(int argc, char** argv) { glutInit(&argc, argv); glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE); glutInitWindowSize(640, 480); glutCreateWindow("ARB_draw_buffers Test"); #if !defined(__APPLE__) glewInit(); #endif if (!extension_available(ARB_multitexture) || !extension_available(ARB_draw_buffers) || !extension_available(EXT_framebuffer_object) || !extension_available(EXT_packed_depth_stencil) || !extension_available(ARB_texture_rectangle) || !extension_available(ARB_vertex_shader) || !extension_available(ARB_fragment_shader) || !extension_available(ARB_shader_objects) || !extension_available(ARB_shading_language_100) || !extension_available(ATI_texture_float) || !extension_available(SGIS_texture_edge_clamp)) { fprintf(stderr, "Need more goodies!\n"); return EXIT_FAILURE; } glGenTextures(1, &color_texture); glBindTexture(GL_TEXTURE_RECTANGLE_ARB, color_texture); glTexImage2D( GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGBA_FLOAT16_ATI, 640, 480, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE_SGIS); glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE_SGIS); glBindTexture(GL_TEXTURE_RECTANGLE_ARB, 0); glGenTextures(1, &normal_texture); glBindTexture(GL_TEXTURE_RECTANGLE_ARB, normal_texture); glTexImage2D( GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGBA_FLOAT16_ATI, 640, 480, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE_SGIS); glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE_SGIS); glBindTexture(GL_TEXTURE_RECTANGLE_ARB, 0); glGenTextures(1, &depth_texture); glBindTexture(GL_TEXTURE_RECTANGLE_ARB, depth_texture); glTexImage2D( GL_TEXTURE_RECTANGLE_ARB, 0, #if defined(BUGGY_PACKED_DEPTH_STENCIL) GL_DEPTH_COMPONENT24, #else GL_DEPTH24_STENCIL8_EXT, #endif 640, 480, 0, #if defined(BUGGY_PACKED_DEPTH_STENCIL) GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, #else GL_DEPTH_STENCIL_EXT, GL_UNSIGNED_INT_24_8_EXT, #endif NULL); glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE_SGIS); glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE_SGIS); glBindTexture(GL_TEXTURE_RECTANGLE_ARB, 0); glGenFramebuffersEXT(1, &fbo); glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo); glFramebufferTexture2DEXT( GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_RECTANGLE_ARB, color_texture, 0); glFramebufferTexture2DEXT( GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT1_EXT, GL_TEXTURE_RECTANGLE_ARB, normal_texture, 0); glFramebufferTexture2DEXT( GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_RECTANGLE_ARB, depth_texture, 0); #if !defined(BUGGY_PACKED_DEPTH_STENCIL) glFramebufferTexture2DEXT( GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT, GL_TEXTURE_RECTANGLE_ARB, depth_texture, 0); #endif printf("framebuffer completeness: %s\n", framebuffer_status_to_string( glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT))); glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); storage_shader = make_shader( 1, &storage_vertex_shader_text, 1, &storage_fragment_shader_text); if (shader_error_text(storage_shader) != NULL) { fprintf(stderr, shader_error_text(storage_shader)); return EXIT_FAILURE; } toon_shader = make_shader( 1, &toon_vertex_shader_text, 1, &toon_fragment_shader_text); if (shader_error_text(toon_shader) != NULL) { fprintf(stderr, shader_error_text(toon_shader)); return EXIT_FAILURE; } GLint colors_location = glGetUniformLocationARB( shader_program(toon_shader), "colors"); GLint normals_location = glGetUniformLocationARB( shader_program(toon_shader), "normals"); GLint depth_location = glGetUniformLocationARB( shader_program(toon_shader), "depth"); glUseProgramObjectARB(shader_program(toon_shader)); glUniform1iARB(colors_location, 0); glUniform1iARB(normals_location, 1); glUniform1iARB(depth_location, 2); glUseProgramObjectARB(0); glutDisplayFunc(display); glutReshapeFunc(reshape); glutIdleFunc(idle); glutMainLoop(); return EXIT_SUCCESS; }