#include "os.h" #include "ph.h" #include "sphere.c" #define COMPACT 0 #define BUFFER_WIDTH 128 #define BUFFER_HEIGHT 128 #define FILTER_COUNT 4 #define KERNEL_SIZE 3 #define BALL_COUNT 100 #define STRINGIFY(A) #A #if COMPACT #define VIEW_WIDTH (BUFFER_WIDTH * 2) #define VIEW_HEIGHT (BUFFER_HEIGHT) #else #define VIEW_WIDTH (BUFFER_WIDTH + 1 + BUFFER_WIDTH + 1 + BUFFER_WIDTH + 1 + BUFFER_WIDTH) #define VIEW_HEIGHT (BUFFER_HEIGHT + 1 + (BUFFER_HEIGHT / 2) + 1 + (BUFFER_HEIGHT / 4) + 1 + (BUFFER_HEIGHT / 8)) #endif #include "shaders/passv.glsl" #include "shaders/blitf.glsl" #include "shaders/combine4f.glsl" #include "shaders/row3f.glsl" typedef struct { float r, g, b; float x, y, z; float vx, vy, vz; } Ball; PHsurface window; PHsurface pass0[FILTER_COUNT]; PHsurface pass1[FILTER_COUNT]; GLuint sphereVerts; GLuint sphereFaces; GLuint blitProg; GLuint combineProg; GLuint filterProg; Ball balls[BALL_COUNT]; GLfloat darkblue[4] = {7, 16, 141, 0}; GLfloat lightblue[4] = {122, 143, 248, 0}; float kernel[KERNEL_SIZE] = { 5, 6, 5 }; typedef enum {HORIZONTAL, VERTICAL} Direction; static void blur(PHsurface *sources, PHsurface *dests, int count, Direction dir) { GLint loc; int p; if (!GLEW_VERSION_2_0) fatalf("OpenGL 2.0 is required"); if (!glewIsSupported("GL_EXT_framebuffer_object")) fatalf("GL_EXT_framebuffer_object is required"); // Set up the filter. glUseProgram(filterProg); loc = glGetUniformLocation(filterProg, "source"); glUniform1i(loc, 0); loc = glGetUniformLocation(filterProg, "coefficients"); glUniform1fv(loc, KERNEL_SIZE, kernel); loc = glGetUniformLocation(filterProg, "offsetx"); glUniform1f(loc, 0); loc = glGetUniformLocation(filterProg, "offsety"); glUniform1f(loc, 0); if (dir == HORIZONTAL) loc = glGetUniformLocation(filterProg, "offsetx"); // Perform the blurring. for (p = 0; p < count; p++) { float offset = 1.2f / sources[p].width; glUniform1f(loc, offset); phBindSurface(dests + p); glBindTexture(GL_TEXTURE_2D, sources[p].texture); glBegin(GL_QUADS); glTexCoord2i(0, 0); glVertex2i(-1, -1); glTexCoord2i(1, 0); glVertex2i(1, -1); glTexCoord2i(1, 1); glVertex2i(1, 1); glTexCoord2i(0, 1); glVertex2i(-1, 1); glEnd(); } } static void init() { int p, c; float sum; GLsizei width; GLsizei height; glGenBuffers(1, &sphereVerts); glBindBuffer(GL_ARRAY_BUFFER, sphereVerts); glBufferData(GL_ARRAY_BUFFER, sizeof(vertData), (void *) vertData, GL_STATIC_DRAW); glGenBuffers(1, &sphereFaces); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, sphereFaces); glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(faceData), (void *) faceData, GL_STATIC_DRAW); glMatrixMode(GL_MODELVIEW); // Compile shaders blitProg = phCompile(passv, blitf); combineProg = phCompile(passv, combine4f); filterProg = phCompile(passv, row3f); // Normalize kernel coefficients sum = 0; for (c = 0; c < KERNEL_SIZE; c++) sum += kernel[c]; for (c = 0; c < KERNEL_SIZE; c++) kernel[c] /= sum; // Normalize colors for (c = 0; c < 4; c++) { darkblue[c] /= 255.0f; lightblue[c] /= 255.0f; } // Create balls for (c = 0; c < BALL_COUNT; c++) { balls[c].r = (float) rand() / RAND_MAX - darkblue[0]; balls[c].g = (float) rand() / RAND_MAX - darkblue[1]; balls[c].b = (float) rand() / RAND_MAX - darkblue[2]; balls[c].x = (0.5f - (float) rand() / RAND_MAX) * 10; balls[c].y = (0.5f - (float) rand() / RAND_MAX) * 10; balls[c].z = (0.5f - (float) rand() / RAND_MAX) * 10; balls[c].vx = (0.5f - (float) rand() / RAND_MAX); balls[c].vy = (0.5f - (float) rand() / RAND_MAX); balls[c].vz = (0.5f - (float) rand() / RAND_MAX); } // Create Window Surface window.fbo = 0; window.depth = 0; window.width = VIEW_WIDTH; window.height = VIEW_HEIGHT; window.clearColor[0] = lightblue[0]; window.clearColor[1] = lightblue[1]; window.clearColor[2] = lightblue[2]; window.clearColor[3] = lightblue[3]; window.viewport.x = 0; window.viewport.y = 0; window.viewport.width = window.width; window.viewport.height = window.height; glLoadIdentity(); glGetFloatv(GL_MODELVIEW_MATRIX, window.modelview); glOrtho(0, window.width, window.height, 0, 0, 10); glGetFloatv(GL_MODELVIEW_MATRIX, window.projection); glLoadIdentity(); // Create 3D Scene Surface width = BUFFER_WIDTH; height = BUFFER_HEIGHT; pass0[0].width = width; pass0[0].height = height; pass0[0].clearColor[0] = 0; pass0[0].clearColor[1] = 0; pass0[0].clearColor[2] = 0; pass0[0].clearColor[3] = 0; pass0[0].viewport.x = 0; pass0[0].viewport.y = 0; pass0[0].viewport.width = width; pass0[0].viewport.height = height; glGetFloatv(GL_MODELVIEW_MATRIX, pass0[0].modelview); glGetFloatv(GL_MODELVIEW_MATRIX, pass0[0].projection); phCreateSurface(pass0, GL_FALSE, GL_FALSE, GL_TRUE); width = width >> 1; height = height >> 1; // Create Pass Surfaces for (p = 1; p < FILTER_COUNT; p++) { pass0[p].width = width; pass0[p].height = height; pass0[p].viewport.x = 0; pass0[p].viewport.y = 0; pass0[p].viewport.width = width; pass0[p].viewport.height = height; glGetFloatv(GL_MODELVIEW_MATRIX, pass0[p].modelview); glGetFloatv(GL_MODELVIEW_MATRIX, pass0[p].projection); phCreateSurface(pass0 + p, GL_FALSE, GL_FALSE, GL_TRUE); width = width >> 1; height = height >> 1; } width = BUFFER_WIDTH; height = BUFFER_HEIGHT; for (p = 0; p < FILTER_COUNT; p++) { pass1[p].width = width; pass1[p].height = height; pass1[p].viewport.x = 0; pass1[p].viewport.y = 0; pass1[p].viewport.width = width; pass1[p].viewport.height = height; glGetFloatv(GL_MODELVIEW_MATRIX, pass1[p].modelview); glGetFloatv(GL_MODELVIEW_MATRIX, pass1[p].projection); phCreateSurface(pass1 + p, GL_FALSE, GL_FALSE, GL_FALSE); width = width >> 1; height = height >> 1; } } static void draw() { int p; GLint loc; // Draw 3D scene. phBindSurface(pass0 + 0); phClearSurface(); glScalef(0.125f, 0.125f, 0.125f); glBindBuffer(GL_ARRAY_BUFFER, sphereVerts); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, sphereFaces); glEnableClientState(GL_VERTEX_ARRAY); glVertexPointer(3, GL_FLOAT, 0, 0); for (p = 0; p < BALL_COUNT; p++) { float ax, ay, az; glPushMatrix(); glColor3f(balls[p].r, balls[p].g, balls[p].b); glTranslatef(balls[p].x, balls[p].y, balls[p].z); glDrawElements(GL_TRIANGLES, FACE_COUNT * 3, GL_UNSIGNED_INT, 0); glPopMatrix(); balls[p].x += 0.25f * balls[p].vx; balls[p].y += 0.25f * balls[p].vy; balls[p].z += 0.25f * balls[p].vz; ax = 0.0125f * balls[p].x * balls[p].x; ay = 0.0125f * balls[p].y * balls[p].y; az = 0.0125f * balls[p].z * balls[p].z; if (balls[p].x > 0) ax = -ax; if (balls[p].y > 0) ay = -ay; if (balls[p].z > 0) az = -az; balls[p].vx += ax; balls[p].vy += ay; balls[p].vz += az; } glColor3f(1, 1, 1); glBindBuffer(GL_ARRAY_BUFFER, 0); glDisableClientState(GL_VERTEX_ARRAY); // Downsample the scene into the source surfaces. glEnable(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D, pass0[0].texture); for (p = 1; p < FILTER_COUNT; p++) { phBindSurface(pass0 + p); glBegin(GL_QUADS); glTexCoord2i(0, 0); glVertex2i(-1, -1); glTexCoord2i(1, 0); glVertex2i(1, -1); glTexCoord2i(1, 1); glVertex2i(1, 1); glTexCoord2i(0, 1); glVertex2i(-1, 1); glEnd(); } // Perform the horizontal blurring pass. blur(pass0, pass1, FILTER_COUNT, HORIZONTAL); // Draw left portion of window. glUseProgram(blitProg); loc = glGetUniformLocation(blitProg, "bkgd"); glUniform4fv(loc, 1, darkblue); loc = glGetUniformLocation(blitProg, "source"); glUniform1i(loc, 0); phBindSurface(&window); phClearSurface(); glPushMatrix(); for (p = 0; p < (COMPACT ? 1 : FILTER_COUNT); p++) { glBindTexture(GL_TEXTURE_2D, pass0[p].texture); glBegin(GL_QUADS); glTexCoord2i(0, 0); glVertex2i(0, 0); glTexCoord2i(1, 0); glVertex2i(pass0[p].width, 0); glTexCoord2i(1, 1); glVertex2i(pass0[p].width, pass0[p].height); glTexCoord2i(0, 1); glVertex2i(0, pass0[p].height); glEnd(); glTranslatef(0, (GLfloat) pass0[p].height + 1, 0); } glPopMatrix(); glTranslatef((GLfloat) pass0[0].width + 1, 0, 0); for (p = 0; p < FILTER_COUNT; p++) { glBindTexture(GL_TEXTURE_2D, pass1[p].texture); glBegin(GL_QUADS); glTexCoord2i(0, 0); glVertex2i(0, 0); glTexCoord2i(1, 0); glVertex2i(pass1[p].width, 0); glTexCoord2i(1, 1); glVertex2i(pass1[p].width, pass1[p].height); glTexCoord2i(0, 1); glVertex2i(0, pass1[p].height); glEnd(); glTranslatef(0, (GLfloat) pass1[p].height + 1, 0); } // Perform the vertical blurring pass. blur(pass1, pass0, FILTER_COUNT, VERTICAL); // Draw right portion of window. phBindSurface(&window); if (COMPACT) { glTranslatef((GLfloat) pass1[0].width, 0, 0); } else { glTranslatef((GLfloat) 2 * pass1[0].width + 2, 0, 0); glUseProgram(blitProg); glPushMatrix(); for (p = 0; p < FILTER_COUNT; p++) { glBindTexture(GL_TEXTURE_2D, pass0[p].texture); glBegin(GL_QUADS); glTexCoord2i(0, 0); glVertex2i(0, 0); glTexCoord2i(1, 0); glVertex2i(pass0[p].width, 0); glTexCoord2i(1, 1); glVertex2i(pass0[p].width, pass0[p].height); glTexCoord2i(0, 1); glVertex2i(0, pass0[p].height); glEnd(); glTranslatef(0, (GLfloat) pass0[p].height + 1, 0); } glPopMatrix(); glTranslatef((GLfloat) pass0[0].width + 1, 0, 0); } glUseProgram(combineProg); loc = glGetUniformLocation(combineProg, "bkgd"); glUniform4fv(loc, 1, darkblue); for (p = 0; p < FILTER_COUNT; p++) { char name[] = "Pass#"; glActiveTexture(GL_TEXTURE0 + p); glBindTexture(GL_TEXTURE_2D, pass0[p].texture); glEnable(GL_TEXTURE_2D); sprintf(name, "Pass%d", p); loc = glGetUniformLocation(combineProg, name); glUniform1i(loc, p); } glBegin(GL_QUADS); glTexCoord2i(0, 0); glVertex2i(0, 0); glTexCoord2i(1, 0); glVertex2i(pass0[0].width, 0); glTexCoord2i(1, 1); glVertex2i(pass0[0].width, pass0[0].height); glTexCoord2i(0, 1); glVertex2i(0, pass0[0].height); glEnd(); glUseProgram(0); for (p = 0; p < FILTER_COUNT; p++) { glActiveTexture(GL_TEXTURE0 + p); glDisable(GL_TEXTURE_2D); } glActiveTexture(GL_TEXTURE0); } int main(int argc, char** argv) { int done = 0; OS_Event event; osInit("Sneaky Bloom" , VIEW_WIDTH, VIEW_HEIGHT, OS_OVERLAY, 0); osWaitVsync(0); init(); while (!done) { while (osPollEvent(&event)) { switch(event.type) { case OS_PAINT: draw(); osSwapBuffers(); break; case OS_KEYUP: switch (event.key.key) { case OSK_ESCAPE: case 'X': case 'x': case 'Q': case 'q': done = 1; break; } break; case OS_QUIT: done = 1; break; } } draw(); osSwapBuffers(); } osQuit(); return 0; }