--- /dev/null
+/*
+ * Test GL_ARB_draw_buffers, GL_EXT_framebuffer_object
+ * and GLSL's gl_FragData[].
+ *
+ * Brian Paul
+ * 11 March 2007
+ */
+
+
+#define GL_GLEXT_PROTOTYPES
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <GL/glut.h>
+#include "extfuncs.h"
+
+static int Win;
+static int Width = 400, Height = 400;
+static GLuint FBobject, RBobjects[3];
+static GLfloat Xrot = 0.0, Yrot = 0.0;
+
+
+static void
+CheckError(int line)
+{
+ GLenum err = glGetError();
+ if (err) {
+ printf("GL Error 0x%x at line %d\n", (int) err, line);
+ }
+}
+
+
+static void
+Display(void)
+{
+ GLubyte *buffer = malloc(Width * Height * 4);
+ static const GLenum buffers[2] = {
+ GL_COLOR_ATTACHMENT0_EXT,
+ GL_COLOR_ATTACHMENT1_EXT
+ };
+
+ /* draw to user framebuffer */
+ glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, FBobject);
+
+ /* Clear color buffer 0 (blue) */
+ glDrawBuffer(GL_COLOR_ATTACHMENT0_EXT);
+ glClearColor(0.5, 0.5, 1.0, 0.0);
+ glClear(GL_COLOR_BUFFER_BIT);
+
+ /* Clear color buffer 1 (1 - blue) */
+ glDrawBuffer(GL_COLOR_ATTACHMENT1_EXT);
+ glClearColor(0.5, 0.5, 0.0, 0.0);
+ glClear(GL_COLOR_BUFFER_BIT);
+
+ glClear(GL_DEPTH_BUFFER_BIT);
+
+ /* draw to two buffers w/ fragment shader */
+ glDrawBuffersARB(2, buffers);
+
+ glPushMatrix();
+ glRotatef(Xrot, 1, 0, 0);
+ glRotatef(Yrot, 0, 1, 0);
+ glutSolidTorus(0.75, 2.0, 10, 20);
+ glPopMatrix();
+
+ /* read from user framebuffer */
+ /* bottom half = colorbuffer 0 */
+ glReadBuffer(GL_COLOR_ATTACHMENT0_EXT);
+ glReadPixels(0, 0, Width, Height / 2, GL_RGBA, GL_UNSIGNED_BYTE,
+ buffer);
+ /* top half = colorbuffer 1 */
+ glReadBuffer(GL_COLOR_ATTACHMENT1_EXT);
+ glReadPixels(0, Height/2, Width, Height / 2, GL_RGBA, GL_UNSIGNED_BYTE,
+ buffer + Width * Height / 2 * 4);
+
+ /* draw to window */
+ glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
+ glWindowPos2iARB(0, 0);
+ glDrawPixels(Width, Height, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
+
+ free(buffer);
+ glutSwapBuffers();
+ CheckError(__LINE__);
+}
+
+
+static void
+Reshape(int width, int height)
+{
+ float ar = (float) width / (float) height;
+
+ glViewport(0, 0, width, height);
+ glMatrixMode(GL_PROJECTION);
+ glLoadIdentity();
+ glFrustum(-ar, ar, -1.0, 1.0, 5.0, 35.0);
+ glMatrixMode(GL_MODELVIEW);
+ glLoadIdentity();
+ glTranslatef(0.0, 0.0, -20.0);
+
+ Width = width;
+ Height = height;
+
+ glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, RBobjects[0]);
+ glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_RGB, Width, Height);
+ glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, RBobjects[1]);
+ glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_RGB, Width, Height);
+ glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, RBobjects[2]);
+ glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT,
+ Width, Height);
+}
+
+
+static void
+CleanUp(void)
+{
+ glDeleteFramebuffersEXT(1, &FBobject);
+ glDeleteRenderbuffersEXT(3, RBobjects);
+ glutDestroyWindow(Win);
+ exit(0);
+}
+
+
+static void
+Key(unsigned char key, int x, int y)
+{
+ (void) x;
+ (void) y;
+ switch (key) {
+ case 'x':
+ Xrot += 5.0;
+ break;
+ case 'y':
+ Yrot += 5.0;
+ break;
+ case 27:
+ CleanUp();
+ break;
+ }
+ glutPostRedisplay();
+}
+
+
+static void
+CheckExtensions(void)
+{
+ const char *version = (const char *) glGetString(GL_VERSION);
+ GLint numBuf;
+
+ if (!glutExtensionSupported("GL_EXT_framebuffer_object")) {
+ printf("Sorry, GL_EXT_framebuffer_object is required!\n");
+ exit(1);
+ }
+ if (!glutExtensionSupported("GL_ARB_draw_buffers")) {
+ printf("Sorry, GL_ARB_draw_buffers is required!\n");
+ exit(1);
+ }
+ if (version[0] != '2') {
+ printf("Sorry, OpenGL 2.0 is required!\n");
+ exit(1);
+ }
+
+ glGetIntegerv(GL_MAX_DRAW_BUFFERS_ARB, &numBuf);
+ printf("GL_MAX_DRAW_BUFFERS_ARB = %d\n", numBuf);
+ if (numBuf < 2) {
+ printf("Sorry, GL_MAX_DRAW_BUFFERS_ARB needs to be >= 2\n");
+ exit(1);
+ }
+
+ printf("GL_RENDERER = %s\n", (char *) glGetString(GL_RENDERER));
+}
+
+
+static void
+SetupRenderbuffers(void)
+{
+ glGenFramebuffersEXT(1, &FBobject);
+ glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, FBobject);
+
+ glGenRenderbuffersEXT(3, RBobjects);
+
+ glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, RBobjects[0]);
+ glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_RGB, Width, Height);
+
+ glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, RBobjects[1]);
+ glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_RGB, Width, Height);
+
+ glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, RBobjects[2]);
+ glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT,
+ Width, Height);
+
+ glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
+ GL_RENDERBUFFER_EXT, RBobjects[0]);
+ glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT1_EXT,
+ GL_RENDERBUFFER_EXT, RBobjects[1]);
+ glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT,
+ GL_RENDERBUFFER_EXT, RBobjects[2]);
+
+ CheckError(__LINE__);
+}
+
+
+static GLuint
+LoadAndCompileShader(GLenum target, const char *text)
+{
+ GLint stat;
+ GLuint shader = glCreateShader_func(target);
+ glShaderSource_func(shader, 1, (const GLchar **) &text, NULL);
+ glCompileShader_func(shader);
+ glGetShaderiv_func(shader, GL_COMPILE_STATUS, &stat);
+ if (!stat) {
+ GLchar log[1000];
+ GLsizei len;
+ glGetShaderInfoLog_func(shader, 1000, &len, log);
+ fprintf(stderr, "drawbuffers: problem compiling shader:\n%s\n", log);
+ exit(1);
+ }
+ return shader;
+}
+
+
+static void
+CheckLink(GLuint prog)
+{
+ GLint stat;
+ glGetProgramiv_func(prog, GL_LINK_STATUS, &stat);
+ if (!stat) {
+ GLchar log[1000];
+ GLsizei len;
+ glGetProgramInfoLog_func(prog, 1000, &len, log);
+ fprintf(stderr, "drawbuffers: shader link error:\n%s\n", log);
+ }
+}
+
+
+static void
+SetupShaders(void)
+{
+ /* second color output = 1 - first color */
+ static const char *fragShaderText =
+ "void main() {\n"
+ " gl_FragData[0] = gl_Color; \n"
+ " gl_FragData[1] = vec4(1.0) - gl_Color; \n"
+ "}\n";
+
+ GLuint fragShader, program;
+
+ fragShader = LoadAndCompileShader(GL_FRAGMENT_SHADER, fragShaderText);
+ program = glCreateProgram_func();
+
+ glAttachShader_func(program, fragShader);
+ glLinkProgram_func(program);
+ CheckLink(program);
+ glUseProgram_func(program);
+}
+
+
+static void
+SetupLighting(void)
+{
+ static const GLfloat frontMat[4] = { 1.0, 0.5, 0.5, 1.0 };
+ static const GLfloat backMat[4] = { 1.0, 0.5, 0.5, 1.0 };
+
+ glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, frontMat);
+ glMaterialfv(GL_BACK, GL_AMBIENT_AND_DIFFUSE, backMat);
+ glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, 1);
+ glEnable(GL_LIGHT0);
+ glEnable(GL_LIGHTING);
+}
+
+
+static void
+Init(void)
+{
+ CheckExtensions();
+ GetExtensionFuncs();
+ SetupRenderbuffers();
+ SetupShaders();
+ SetupLighting();
+ glEnable(GL_DEPTH_TEST);
+}
+
+
+int
+main(int argc, char *argv[])
+{
+ glutInit(&argc, argv);
+ glutInitWindowPosition(0, 0);
+ glutInitWindowSize(Width, Height);
+ glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE);
+ Win = glutCreateWindow(argv[0]);
+ glutReshapeFunc(Reshape);
+ glutKeyboardFunc(Key);
+ glutDisplayFunc(Display);
+ Init();
+ glutMainLoop();
+ return 0;
+}