Hi guys. I’ve been playing so much with Shadows these days, trying to get it right for a client. Im looking at Summed Area Variance Shadow maps and following GPU Gems 3. Ive found this paper:
http://www.shaderwrangler.com/publications/sat/SAT_EG2005.pdf
Which tells you how to create summed area maps for use in various effects, using the GPU. The algorithm seems straight forward but I cant seem to get it right:
-(void) generateSummedTables:(QCOpenGLContext *)context withTex:(GLuint) tex {
CGLContextObj cgl_ctx = [context CGLContextObj];
// Horizontal Scan
int nm = ceil(log2(mFBOSize));
glUseProgramObjectARB([mSummedTableShader programObject]);
glUniform1iARB([mSummedTableShader getUniformLocation:"texWidth"],mFBOSize);
GLuint atex = tex;
[mSummedFBO bindNoDraw];
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadIdentity();
glOrtho(-1, 1, -1, 1, 0.0, 10.0);
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glLoadIdentity();
glColor3f(1.0,1.0,1.0f);
unsigned int ni = 1;
BOOL usingA = TRUE;
for(int i=0; i < nm; i++){
//glClear(GL_COLOR_BUFFER_BIT);
if (usingA) {
glDrawBuffer(GL_COLOR_ATTACHMENT0_EXT);
} else {
glDrawBuffer(GL_COLOR_ATTACHMENT1_EXT);
}
// Start off with an input texture A
glUniform1iARB([mSummedTableShader getUniformLocation:"Ni"],ni);
glUniform1iARB([mSummedTableShader getUniformLocation:"texture"],atex);
glBindTexture(GL_TEXTURE_2D, atex);
glBegin(GL_QUADS);
glTexCoord2f(0.0, 0.0); glVertex3f(-1.0, -1.0, 0.0);
glTexCoord2f(1.0, 0.0); glVertex3f(1.0, -1.0, 0.0);
glTexCoord2f(1.0, 1.0); glVertex3f(1.0, 1.0, 0.0);
glTexCoord2f(0.0, 1.0); glVertex3f(-1.0, 1.0, 0.0);
glEnd();
ni = ni << 1; // Move up
if (usingA) {
atex = [mSummedFBO getTextureAtTarget:0];
} else {
atex = [mSummedFBO getTextureAtTarget:1];
}
usingA = !usingA;
}
// Vertical Scan
usingA = FALSE; // Sure about that?
ni = 1;
atex = [mSummedFBO getTextureAtTarget:1];
glUseProgramObjectARB([mSummedTableVShader programObject]);
glUniform1iARB([mSummedTableVShader getUniformLocation:"texWidth"],mFBOSize);
for(int i=0; i < nm; i++){
//glClear(GL_COLOR_BUFFER_BIT);
if (usingA) {
glDrawBuffer(GL_COLOR_ATTACHMENT0_EXT);
} else {
glDrawBuffer(GL_COLOR_ATTACHMENT1_EXT);
}
// Start off with an input texture A
glUniform1iARB([mSummedTableVShader getUniformLocation:"Ni"],ni);
glUniform1iARB([mSummedTableVShader getUniformLocation:"texture"],atex);
glBindTexture(GL_TEXTURE_2D, atex);
glBegin(GL_QUADS);
glTexCoord2f(0.0, 0.0); glVertex3f(-1.0, -1.0, 0.0);
glTexCoord2f(1.0, 0.0); glVertex3f(1.0, -1.0, 0.0);
glTexCoord2f(1.0, 1.0); glVertex3f(1.0, 1.0, 0.0);
glTexCoord2f(0.0, 1.0); glVertex3f(-1.0, 1.0, 0.0);
glEnd();
ni = ni << 1; // Move up
if (usingA) {
atex = [mSummedFBO getTextureAtTarget:0];
} else {
atex = [mSummedFBO getTextureAtTarget:1];
}
usingA = !usingA;
}
glPopMatrix();
glMatrixMode(GL_PROJECTION);
glPopMatrix();
[mSummedFBO unbindFBO];
glUseProgramObjectARB(NULL);
}
AND THE FRAGMENT SHADER FOR GENERATING TABLES (Vertical and Horizontal)
uniform int texWidth; // Should have size I imagine
uniform int Ni; // texels along 2 ^ i (so we pre power)
// TODO - this is practically identical to horiz therefore we should just have a swap variable or something
uniform sampler2D texture;
void main (void) {
// vertical Pass
vec2 s = gl_TexCoord[0].st;
vec2 sd = s;
sd.y = sd.y + ( 1.0/ float(texWidth) * float(Ni) );
vec4 c = texture2D(texture, s) + texture2D(texture, sd);
gl_FragColor = c;
// Now we SWAP textures
}
uniform int texWidth;
uniform int Ni; // texels along 2 ^ i (so we pre power)
uniform sampler2D texture;
void main (void) {
// Horizontal Pass
vec2 s = gl_TexCoord[0].st;
vec2 sd = s;
sd.x = sd.x + (1.0/ float(texWidth) * float(Ni));
vec4 c = texture2D(texture, s) + texture2D(texture, sd);
gl_FragColor = c;
// Now we SWAP textures
}
So what I have is a ping/pong fbo with two textures, both 512 x 512 power of two, square textures. They are GL_RGB32F_ARB textures represented with GL_FLOAT.
I go through horizontally summing and then vertically summing. Each texture has a clamp to border flag set with a colour of 0 to make sure that any overruns dont affect the summing result.
How would I check this is correct? I suppose the easiest way is to write a shader that converts back to the previous result probably?