diff options
author | Kenneth Graunke <kenneth@whitecape.org> | 2011-03-23 01:03:53 -0700 |
---|---|---|
committer | Kenneth Graunke <kenneth@whitecape.org> | 2011-03-23 02:18:11 -0700 |
commit | 5e72b1ba825fc284b2692df669fb87cf0f408ea3 (patch) | |
tree | 9612b5942ec4e864e926741801cd47f17099293e | |
parent | d133fb5f644ad6f4edd3526ebc064a39f414f0b6 (diff) |
Implement SSAO.ssao
This uses MRT to have the phong shader write four things:
1. The color value
2. The eye space position
3. The eye space normal
4. The depth value
-rwxr-xr-x | src/main.cpp | 83 | ||||
-rwxr-xr-x | src/phong.frag | 7 | ||||
-rwxr-xr-x | src/phong.vert | 12 | ||||
-rw-r--r-- | src/ssao.frag | 95 | ||||
-rw-r--r-- | src/ssao.vert | 10 |
5 files changed, 185 insertions, 22 deletions
diff --git a/src/main.cpp b/src/main.cpp index cf56f38..8b95dc4 100755 --- a/src/main.cpp +++ b/src/main.cpp @@ -36,18 +36,26 @@ static bool done = false; static int win_width, win_height; static GLuint main_fbo; -static GLuint main_fbo_tex; -static GLuint main_fbo_depth_tex; +static GLuint color_tex; +static GLuint depth_tex; +static GLuint es_pos_tex; +static GLuint es_normal_tex; -static GLuint prog; -static GLuint sampler_slot; +static GLuint ssao; +static GLuint color_sampler_slot; +static GLuint depth_sampler_slot; +static GLuint es_pos_sampler_slot; +static GLuint es_normal_sampler_slot; static void Redisplay() { // Render scene to FBO - glCullFace(GL_BACK); glBindFramebuffer(GL_FRAMEBUFFER, main_fbo); + // ...but first, turn on MRT: + GLenum buffers[] = { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1, GL_COLOR_ATTACHMENT2 }; + glDrawBuffers(3, buffers); + glClearColor(0.3, 0.3, 0.3, 1.0); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); Scene::Reshape(win_width, win_height); @@ -55,10 +63,20 @@ Redisplay() // Blit FBO to screen glBindFramebuffer(GL_FRAMEBUFFER, 0); - glUseProgram(prog); - - glBindTexture(GL_TEXTURE_2D, main_fbo_tex); - glUniform1i(sampler_slot, 0); + glUseProgram(ssao); + + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, color_tex); + glUniform1i(color_sampler_slot, 0); + glActiveTexture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_2D, depth_tex); + glUniform1i(depth_sampler_slot, 1); + glActiveTexture(GL_TEXTURE2); + glBindTexture(GL_TEXTURE_2D, es_pos_tex); + glUniform1i(es_pos_sampler_slot, 2); + glActiveTexture(GL_TEXTURE3); + glBindTexture(GL_TEXTURE_2D, es_normal_tex); + glUniform1i(es_normal_sampler_slot, 3); GLfloat quad[4][2] = {{-1, 1}, {-1, -1}, {1, 1}, {1, -1}}; @@ -99,11 +117,14 @@ Init() Scene::Init(); - if (!load_and_compile_program_code("blit", prog, "vertex", 0, NULL)) { + if (!load_and_compile_program_code("ssao", ssao, "vertex", 0, NULL)) { printf("Failed to load post-processing shader.\n"); exit(-1); } - sampler_slot = glGetUniformLocation(prog, "sampler"); + color_sampler_slot = glGetUniformLocation(ssao, "s_color"); + depth_sampler_slot = glGetUniformLocation(ssao, "s_depth"); + es_pos_sampler_slot = glGetUniformLocation(ssao, "s_es_pos"); + es_normal_sampler_slot = glGetUniformLocation(ssao, "s_es_normal"); } @@ -121,27 +142,55 @@ Reshape(int width, int height) glewInit(); + GLint max_color_attachments; + glGetIntegerv(GL_MAX_COLOR_ATTACHMENTS, &max_color_attachments); + printf("Max color attachments = %d\n", max_color_attachments); + if (max_color_attachments < 3) { + printf("Not enough color attachments, sorry\n"); + exit(1); + } + /* Create the main FBO - color texture and depth renderbuffer */ glGenFramebuffers(1, &main_fbo); glBindFramebuffer(GL_FRAMEBUFFER, main_fbo); - glGenTextures(1, &main_fbo_tex); - glBindTexture(GL_TEXTURE_2D, main_fbo_tex); + glGenTextures(1, &color_tex); + glBindTexture(GL_TEXTURE_2D, color_tex); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_FLOAT, 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); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, color_tex, 0); + + glGenTextures(1, &es_pos_tex); + glBindTexture(GL_TEXTURE_2D, es_pos_tex); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_FLOAT, 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); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, es_pos_tex, 0); + + glGenTextures(1, &es_normal_tex); + glBindTexture(GL_TEXTURE_2D, es_normal_tex); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_FLOAT, 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); - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, main_fbo_tex, 0); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT2, GL_TEXTURE_2D, es_normal_tex, 0); - glGenTextures(1, &main_fbo_depth_tex); - glBindTexture(GL_TEXTURE_2D, main_fbo_depth_tex); + glGenTextures(1, &depth_tex); + glBindTexture(GL_TEXTURE_2D, depth_tex); glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT24, width, height, 0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL); + /* Disable depth comparisons so we can access the raw value */ + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_NONE); 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); - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, main_fbo_depth_tex, 0); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, depth_tex, 0); if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { printf("main FBO not complete\n"); diff --git a/src/phong.frag b/src/phong.frag index 0ca8241..2894305 100755 --- a/src/phong.frag +++ b/src/phong.frag @@ -2,6 +2,9 @@ varying vec3 light_ss; varying vec3 eye_ss; +varying vec3 pos_es; +varying vec3 normal_es; + const vec3 base_color = vec3(1.0, 0.7, 0.0); /* Specular exponent. @@ -19,5 +22,7 @@ void main(void) vec4 light = step(0.0, dot(n, l)) * vec4(diff + vec3(spec), 1.0); - gl_FragColor = light; + gl_FragData[0] = light; + gl_FragData[1] = vec4(pos_es, 1.0); + gl_FragData[2] = vec4(normal_es, 1.0); } diff --git a/src/phong.vert b/src/phong.vert index 9a83ba8..2e49593 100755 --- a/src/phong.vert +++ b/src/phong.vert @@ -17,19 +17,23 @@ varying vec3 light_ss; // ??? varying vec3 eye_ss; +varying vec3 pos_es; +varying vec3 normal_es; + void main(void) { gl_Position = mvp * gl_Vertex; + pos_es = vec3(mv * gl_Vertex); + normal_es = vec3(mv * normal); + vec3 tangent_es = vec3(mv * tangent); - vec3 normal_es = vec3(mv * normal); vec3 bitangent_es = cross(normal_es, tangent_es); mat3 tbn = mat3(tangent_es, bitangent_es, normal_es); - vec3 vertex_pos_es = vec3(mv * gl_Vertex); - vec3 light_direction_es = vec3(light_pos_es) - vertex_pos_es; + vec3 light_direction_es = vec3(light_pos_es) - pos_es; /* Surface space vectors for lighting */ light_ss = normalize(light_direction_es * tbn); - eye_ss = -normalize(vertex_pos_es * tbn); + eye_ss = -normalize(pos_es * tbn); } diff --git a/src/ssao.frag b/src/ssao.frag new file mode 100644 index 0000000..d5fe3ef --- /dev/null +++ b/src/ssao.frag @@ -0,0 +1,95 @@ +#version 130 + +varying vec2 texcoord; + +/* Color value from original scene rendering pass */ +uniform sampler2D s_color; +/* Depth value from original scene rendering pass */ +uniform sampler2D s_depth; +/* Eye-space position texture */ +uniform sampler2D s_es_pos; +/* Eye-space normal texture */ +uniform sampler2D s_es_normal; + +const float twoPi = 6.28318530717959; + +/* S_Psi - surface area of the solid angle of the circle */ +float surface_area(vec3 p, vec3 c, float r) +{ + return twoPi * (1 - cos(asin(r/distance(p, c)))); +} + +/* A_Psi - approximate ambient occlusion due to a sphere */ +float ao(vec3 c, float r, vec3 p, vec3 n) +{ + vec3 pc = c - p; /* vector from 'c' to 'p' */ + return surface_area(p, c, r) * max(dot(n, normalize(pc)), 0); +} + +void main(void) +{ + /* Just sample a stupid 3x3 grid; I don't bloody care */ + const ivec2 offsets[9] = ivec2[](ivec2( 0, 0), + ivec2( 0,-1), + ivec2( 0, 1), + ivec2(-1, 0), + ivec2(-1,-1), + ivec2(-1, 1), + ivec2( 1, 0), + ivec2( 1,-1), + ivec2( 1, 1)); + + /* Constant offsets are so bullshit */ + float depth[9]; + depth[0] = textureOffset(s_depth, texcoord, offsets[0]).x; + depth[1] = textureOffset(s_depth, texcoord, offsets[1]).x; + depth[2] = textureOffset(s_depth, texcoord, offsets[2]).x; + depth[3] = textureOffset(s_depth, texcoord, offsets[3]).x; + depth[4] = textureOffset(s_depth, texcoord, offsets[4]).x; + depth[5] = textureOffset(s_depth, texcoord, offsets[5]).x; + depth[6] = textureOffset(s_depth, texcoord, offsets[6]).x; + depth[7] = textureOffset(s_depth, texcoord, offsets[7]).x; + depth[8] = textureOffset(s_depth, texcoord, offsets[8]).x; + + vec3 pos_es[9]; + pos_es[0] = textureOffset(s_es_pos, texcoord, offsets[0]).xyz; + pos_es[1] = textureOffset(s_es_pos, texcoord, offsets[1]).xyz; + pos_es[2] = textureOffset(s_es_pos, texcoord, offsets[2]).xyz; + pos_es[3] = textureOffset(s_es_pos, texcoord, offsets[3]).xyz; + pos_es[4] = textureOffset(s_es_pos, texcoord, offsets[4]).xyz; + pos_es[5] = textureOffset(s_es_pos, texcoord, offsets[5]).xyz; + pos_es[6] = textureOffset(s_es_pos, texcoord, offsets[6]).xyz; + pos_es[7] = textureOffset(s_es_pos, texcoord, offsets[7]).xyz; + pos_es[8] = textureOffset(s_es_pos, texcoord, offsets[8]).xyz; + + vec3 normal_es[9]; + normal_es[0] = textureOffset(s_es_normal, texcoord, offsets[0]).xyz; + normal_es[1] = textureOffset(s_es_normal, texcoord, offsets[1]).xyz; + normal_es[2] = textureOffset(s_es_normal, texcoord, offsets[2]).xyz; + normal_es[3] = textureOffset(s_es_normal, texcoord, offsets[3]).xyz; + normal_es[4] = textureOffset(s_es_normal, texcoord, offsets[4]).xyz; + normal_es[5] = textureOffset(s_es_normal, texcoord, offsets[5]).xyz; + normal_es[6] = textureOffset(s_es_normal, texcoord, offsets[6]).xyz; + normal_es[7] = textureOffset(s_es_normal, texcoord, offsets[7]).xyz; + normal_es[8] = textureOffset(s_es_normal, texcoord, offsets[8]).xyz; + + /* A guide to variables: + * - p is the fragment we're shading + * - n is the normal vector at p + * - c is the center of the occluding sphere + * - r is the radius of the occluding sphere + */ + vec3 p = pos_es[0]; + + float occlusion = 0; + for (int i = 1; i < 9; i++) { + /* Bias the sphere's center away from p slightly */ + vec3 c = pos_es[i] + normalize(pos_es[i] - p)*0.01; + vec3 n = normal_es[i]; + float r = 1.0; // XXX: arbitrary. should depend on depth of c? + occlusion += ao(c, r, p, n); + } + + vec4 original_color = texture(s_color, texcoord); + gl_FragColor = mix(original_color, vec4(0,0,0,1), occlusion); +} diff --git a/src/ssao.vert b/src/ssao.vert new file mode 100644 index 0000000..b19e383 --- /dev/null +++ b/src/ssao.vert @@ -0,0 +1,10 @@ +#version 130 + +attribute vec2 vertex; +varying vec2 texcoord; + +void main(void) +{ + texcoord = vertex * 0.5 + 0.5; + gl_Position = vec4(vertex, 0, 1); +} |