summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKenneth Graunke <kenneth@whitecape.org>2011-03-23 01:03:53 -0700
committerKenneth Graunke <kenneth@whitecape.org>2011-03-23 02:18:11 -0700
commit5e72b1ba825fc284b2692df669fb87cf0f408ea3 (patch)
tree9612b5942ec4e864e926741801cd47f17099293e
parentd133fb5f644ad6f4edd3526ebc064a39f414f0b6 (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-xsrc/main.cpp83
-rwxr-xr-xsrc/phong.frag7
-rwxr-xr-xsrc/phong.vert12
-rw-r--r--src/ssao.frag95
-rw-r--r--src/ssao.vert10
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);
+}