Created
May 14, 2012 20:12
-
-
Save graphitemaster/2696371 to your computer and use it in GitHub Desktop.
Deferred Renderer - 350LOC
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* A simple deferred renderer using OpenGL, C90 and SDL, plugin and use | |
* with no issue at all. All code is public domain -- Dale Weiler 2012 | |
* a.k.a graphitemaster | |
*/ | |
#include <SDL.h> | |
#include <SDL/SDL_opengl.h> | |
typedef struct { | |
GLuint fbo; | |
GLuint diffuse_target, diffuse_texture; | |
GLuint position_target, position_texture; | |
GLuint normals_target, normals_texture; | |
GLuint depthbuffer; | |
GLuint width,height; | |
} r_fbo; | |
typedef struct { | |
GLhandleARB shader_vert; | |
GLhandleARB shader_frag; | |
GLhandleARB shader_prog; | |
const char *vsfilename; | |
const char *fsfilename; | |
} r_shader; | |
typedef struct { | |
r_shader *shader; | |
r_fbo *fbo; | |
GLuint width, height; | |
GLuint id_diffuse; | |
GLuint id_position; | |
GLuint id_normals; | |
} r_def; | |
typedef struct { | |
r_shader *shader; | |
GLuint worldmatrix; | |
float rotation_x,rotation_y,rotation_z; | |
float position_x,position_y,position_z; | |
GLuint texture, ogltex; | |
} r_mdl; | |
r_shader *r_shader_make(const char *vsfilename, const char *fsfilename) { | |
r_shader *shader = malloc(sizeof(r_shader)); | |
shader->vsfilename = vsfilename; | |
shader->fsfilename = fsfilename; | |
shader->shader_vert = glCreateShaderObjectARB(GL_VERTEX_SHADER_ARB); | |
shader->shader_frag = glCreateShaderObjectARB(GL_FRAGMENT_SHADER_ARB); | |
char *vd,*fd; | |
FILE *sf = NULL; | |
long count = 0; | |
/* vertex load */ | |
sf = fopen(vsfilename, "r"); | |
if (!sf) { | |
printf("r: failed to load vertex shader %s\n", vsfilename); | |
abort (); | |
} | |
fseek(sf, 0, SEEK_END); | |
count = ftell(sf); | |
rewind(sf); | |
vd = malloc(count + 1); | |
memset(vd, 0, count+1); | |
fread (vd, 1, count, sf); | |
fclose(sf); sf = NULL; | |
/* fragment load */ | |
sf = fopen(fsfilename, "r"); | |
if (!sf) { | |
printf("r: failed to load fragment shader %s\n", fsfilename); | |
abort (); | |
} | |
fseek(sf, 0, SEEK_END); | |
count = ftell(sf); | |
rewind(sf); | |
fd = malloc(count + 1); | |
memset(fd, 0, count+1); | |
fread (fd, 1, count, sf); | |
fclose(sf); sf = NULL; | |
int compiled = 0; | |
glShaderSourceARB (shader->shader_vert, 1, &vd, NULL); | |
glCompileShaderARB (shader->shader_vert); | |
glGetObjectParameterivARB(shader->shader_vert, GL_OBJECT_COMPILE_STATUS_ARB, &compiled); | |
if (compiled == 0) { | |
printf("r: failed to compile vertex shader %s\n", vsfilename); | |
abort (); | |
} | |
glShaderSourceARB (shader->shader_frag, 1, &fd, NULL); | |
glCompileShaderARB (shader->shader_frag); | |
glGetObjectParameterivARB(shader->shader_frag, GL_OBJECT_COMPILE_STATUS_ARB, &compiled); | |
if (compiled == 0) { | |
printf("r: failed to compile fragment shader %s\n", fsfilename); | |
abort (); | |
} | |
shader->shader_prog = glCreateProgramObjectARB(); | |
glAttachObjectARB(shader->shader_prog, shader->shader_vert); | |
glAttachObjectARB(shader->shader_prog, shader->shader_frag); | |
glLinkProgramARB (shader->shader_prog); | |
free(vd); | |
free(fd); | |
return shader; | |
} | |
r_mdl *r_mdl_make(const char *vsfilename, const char *fsfilename) { | |
r_mdl *mdl = malloc(sizeof(r_mdl)); | |
mdl->ogltex = 0; | |
mdl->shader = r_shader_make(vsfilename, fsfilename); | |
mdl->rotation_x = mdl->rotation_y = mdl->rotation_z = 0; | |
mdl->position_x = mdl->position_y = mdl->position_z = 0; | |
mdl->worldmatrix = glGetUniformLocationARB(mdl->shader->shader_prog, "t_pos"); | |
mdl->texture = glGetUniformLocationARB(mdl->shader->shader_prog, "t_dif"); | |
return mdl; | |
} | |
r_def *r_def_make(int w, int h, r_fbo *fbo) { | |
r_def *def = malloc(sizeof(r_def)); | |
def->shader = r_shader_make("data/dr.vs", "data/dr.fs"); | |
def->fbo = fbo; | |
def->width = w; | |
def->height = h; | |
def->id_diffuse = glGetUniformLocationARB(def->shader->shader_prog, "t_dif"); | |
def->id_position = glGetUniformLocationARB(def->shader->shader_prog, "t_pos"); | |
def->id_normals = glGetUniformLocationARB(def->shader->shader_prog, "t_nrm"); | |
} | |
void r_def_render(const r_def *def) { | |
glMatrixMode (GL_PROJECTION); | |
glPushMatrix (); | |
glLoadIdentity (); | |
glOrtho (0, def->width, 0, def->height, 0.1f, 2); | |
glMatrixMode (GL_MODELVIEW); | |
glPushMatrix (); | |
glUseProgramObjectARB (def->shader->shader_prog); | |
glActiveTextureARB (GL_TEXTURE0_ARB); | |
glEnable (GL_TEXTURE_2D); | |
glBindTexture (GL_TEXTURE_2D, def->fbo->diffuse_texture); | |
glUniform1iARB (def->id_diffuse, 0); | |
glActiveTextureARB (GL_TEXTURE1_ARB); | |
glEnable (GL_TEXTURE_2D); | |
glBindTexture (GL_TEXTURE_2D, def->fbo->position_texture); | |
glUniform1iARB (def->id_position,1); | |
glActiveTextureARB (GL_TEXTURE2_ARB); | |
glEnable (GL_TEXTURE_2D); | |
glBindTexture (GL_TEXTURE_2D, def->fbo->normals_texture); | |
glUniform1iARB (def->id_normals, 2); | |
glLoadIdentity (); | |
glColor3f (1,1,1); | |
glTranslatef (0,0,-1.0); | |
glBegin (GL_QUADS); | |
glTexCoord2f (0, 0); glVertex3f(0.0f, 0.0f, 0.0f); | |
glTexCoord2f (1, 0); glVertex3f((float)def->width, 0.0f, 0.0f); | |
glTexCoord2f (1, 1); glVertex3f((float)def->width, (float)def->height, 0.0f); | |
glTexCoord2f (0, 1); glVertex3f(0.0f, (float)def->height, 0.0f); | |
glEnd (); | |
glActiveTextureARB (GL_TEXTURE0_ARB); | |
glDisable (GL_TEXTURE_2D); | |
glBindTexture (GL_TEXTURE_2D, 0); | |
glActiveTextureARB (GL_TEXTURE1_ARB); | |
glDisable (GL_TEXTURE_2D); | |
glBindTexture (GL_TEXTURE_2D, 0); | |
glActiveTextureARB (GL_TEXTURE1_ARB); | |
glDisable (GL_TEXTURE_2D); | |
glBindTexture (GL_TEXTURE_2D, 0); | |
glUseProgramObjectARB (0); | |
glMatrixMode (GL_PROJECTION); | |
glPopMatrix (); | |
glMatrixMode (GL_MODELVIEW); | |
glPopMatrix (); | |
} | |
r_fbo *r_fbo_make(int w, int h) { | |
r_fbo *fbo = malloc(sizeof(r_fbo)); | |
fbo->width = w; | |
fbo->height = h; | |
glGenFramebuffersEXT(1, &fbo->fbo); | |
glGenFramebuffersEXT(1, &fbo->diffuse_target); | |
glGenFramebuffersEXT(1, &fbo->position_target); | |
glGenFramebuffersEXT(1, &fbo->normals_target); | |
glGenFramebuffersEXT(1, &fbo->depthbuffer); | |
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo->fbo); | |
glBindRenderbufferEXT (GL_RENDERBUFFER_EXT, fbo->diffuse_target); | |
glRenderbufferStorageEXT (GL_RENDERBUFFER_EXT, GL_RGBA, w, h); | |
glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT , GL_COLOR_ATTACHMENT0_EXT, GL_RENDERBUFFER_EXT, fbo->diffuse_target); | |
glBindRenderbufferEXT (GL_RENDERBUFFER_EXT, fbo->position_target); | |
glRenderbufferStorageEXT (GL_RENDERBUFFER_EXT, GL_RGBA32F_ARB, w, h); | |
glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT , GL_COLOR_ATTACHMENT1_EXT, GL_RENDERBUFFER_EXT, fbo->position_target); | |
glBindRenderbufferEXT (GL_RENDERBUFFER_EXT, fbo->normals_target); | |
glRenderbufferStorageEXT (GL_RENDERBUFFER_EXT, GL_RGBA16F_ARB, w, h); | |
glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT , GL_COLOR_ATTACHMENT2_EXT, GL_RENDERBUFFER_EXT, fbo->normals_target); | |
glBindRenderbufferEXT (GL_RENDERBUFFER_EXT, fbo->depthbuffer); | |
glRenderbufferStorageEXT (GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT24, w, h); | |
glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT , GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, fbo->depthbuffer); | |
glGenTextures(1, &fbo->diffuse_texture); | |
glGenTextures(1, &fbo->position_texture); | |
glGenTextures(1, &fbo->normals_texture); | |
glBindTexture (GL_TEXTURE_2D, fbo->diffuse_texture); | |
glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); | |
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); | |
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); | |
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); | |
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); | |
glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, fbo->diffuse_texture, 0); | |
glBindTexture (GL_TEXTURE_2D, fbo->position_texture); | |
glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA32F_ARB, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); | |
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); | |
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); | |
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); | |
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); | |
glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT1_EXT, GL_TEXTURE_2D, fbo->position_texture, 0); | |
glBindTexture (GL_TEXTURE_2D, fbo->normals_texture); | |
glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA16F_ARB, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); | |
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); | |
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); | |
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); | |
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); | |
glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT2_EXT, GL_TEXTURE_2D, fbo->normals_texture, 0); | |
GLenum check = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT); | |
if (check != GL_FRAMEBUFFER_COMPLETE_EXT) { | |
printf("r: failed to complete FBO target creation\n"); | |
abort(); | |
} | |
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); | |
return fbo; | |
} | |
void r_fbo_start(const r_fbo *fbo) { | |
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo->fbo); | |
glPushAttrib (GL_VIEWPORT_BIT); | |
glViewport (0, 0, fbo->width, fbo->height); | |
glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); | |
glClearColor (0.0f, 0.0f, 0.0f, 1.0f); | |
glActiveTextureARB (GL_TEXTURE0_ARB); | |
glEnable (GL_TEXTURE_2D); | |
GLenum buffers[] = { | |
GL_COLOR_ATTACHMENT0_EXT,GL_COLOR_ATTACHMENT1_EXT,GL_COLOR_ATTACHMENT2_EXT | |
}; | |
glDrawBuffers(3, buffers); | |
} | |
void r_fbo_stop (const r_fbo *fbo) { | |
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); | |
glPopAttrib (); | |
} | |
void r_fbo_show (const r_fbo *fbo, GLuint i, float sx, float sy, float x, float y) { | |
GLuint tex = fbo->diffuse_texture; | |
if (i == 1) tex = fbo->position_texture; | |
else if (i == 2) tex = fbo->normals_texture; | |
glMatrixMode (GL_PROJECTION); | |
glPushMatrix (); | |
glLoadIdentity (); | |
glOrtho (0, fbo->width, 0, fbo->height, 0.1f, 2); | |
glMatrixMode (GL_MODELVIEW); | |
glPushMatrix (); | |
glActiveTextureARB(GL_TEXTURE0_ARB); | |
glLoadIdentity (); | |
glTranslatef (x, -y, -1.0f); | |
glColor3f (1,1,1); | |
glBegin (GL_QUADS); | |
glTexCoord2f (0, 1); glVertex3f(0.0f, (float)fbo->height, 0.0f); | |
glTexCoord2f (0, 0); glVertex3f(0.0f, fbo->height-sy, 0.0f); | |
glTexCoord2f (1, 0); glVertex3f(sx, fbo->height-sy, 0.0f); | |
glTexCoord2f (1, 1); glVertex3f(sx, (float)fbo->height, 0.0f); | |
glEnd (); | |
glBindTexture (GL_TEXTURE_2D, 0); | |
glMatrixMode (GL_PROJECTION); | |
glPopMatrix (); | |
glMatrixMode (GL_MODELVIEW); | |
glPopMatrix (); | |
} | |
void r_mdl_render(r_mdl *mdl, int type) { | |
glPushMatrix(); | |
switch (type) { | |
/* | |
* Write all rendering in here, for some model | |
*/ | |
} | |
glUseProgramObjectARB(0); | |
glPopMatrix(); | |
} | |
int main() { | |
const SDL_VideoInfo *info = NULL; | |
int width = 0; | |
int height = 0; | |
int bpp = 0; | |
int flags = 0; | |
if (SDL_Init(SDL_INIT_VIDEO) < 0) { | |
printf("c: failed to initialize video %s\n", SDL_GetError()); | |
abort (); | |
} | |
info = SDL_GetVideoInfo(); | |
if (!info) { | |
printf("c: failed to query video %s\n", SDL_GetError()); | |
abort (); | |
} | |
width = 640; | |
height = 480; | |
bpp = info->vfmt->BitsPerPixel; | |
SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 5 ); | |
SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 5 ); | |
SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 5 ); | |
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 16); | |
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1 ); | |
flags = SDL_OPENGL; | |
if (SDL_SetVideoMode(width, height, bpp, flags) == 0) { | |
printf("c: failed to set view mode: %s\n", SDL_GetError()); | |
abort (); | |
} | |
r_fbo *multi = r_fbo_make(width, height); | |
r_def *defer = r_def_make(width, height, multi); | |
r_mdl *level = r_mdl_make([[VERTEX_SHADER_FILE_STRING_HERE]], [[FRAGMENT_SHADER_FILE_STRING_HERE]]); | |
/* more models are allowed */ | |
level->position_x = 2; | |
level->position_y = 2.5f; | |
level->position_z = 0; | |
glDisable (GL_LIGHTING); | |
glEnable (GL_TEXTURE_2D); | |
glShadeModel(GL_SMOOTH); | |
glEnable (GL_DEPTH_TEST); | |
glDepthFunc (GL_LEQUAL); | |
glHint (GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); | |
SDL_Event e; | |
for (;;) { | |
while (SDL_PollEvent(&e)) | |
switch (e.type) | |
case SDL_QUIT: abort(); break; | |
glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); | |
glClearColor (0.2f, 0.3f, 0.8f, 1.0f); | |
glLoadIdentity(); | |
glRotatef (20, 1, 0, 0); | |
glTranslatef (0.0f, -4.6f, -10.0f); | |
r_fbo_start (multi); | |
r_mdl_render (level, __COUNTER__); /* handle more models as needed */ | |
r_fbo_stop (multi); | |
/* | |
* There is a choice now: | |
* #1 use the deferred renderer | |
* #2 use the fixed renderer (to FBO) | |
*/ | |
r_def_render(defer); | |
/* | |
* Replace r_def_render(defer) with r_fbo_show(....) | |
* to use the fixed pipeline. | |
*/ | |
SDL_GL_SwapBuffers(); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment