/*===========================================================================*\ * * * OpenFlipper * * Copyright (C) 2001-2011 by Computer Graphics Group, RWTH Aachen * * www.openflipper.org * * * *--------------------------------------------------------------------------- * * This file is part of OpenFlipper. * * * * OpenFlipper is free software: you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License as * * published by the Free Software Foundation, either version 3 of * * the License, or (at your option) any later version with the * * following exceptions: * * * * If other files instantiate templates or use macros * * or inline functions from this file, or you compile this file and * * link it with other files to produce an executable, this file does * * not by itself cause the resulting executable to be covered by the * * GNU Lesser General Public License. This exception does not however * * invalidate any other reasons why the executable file might be * * covered by the GNU Lesser General Public License. * * * * OpenFlipper is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU Lesser General Public License for more details. * * * * You should have received a copy of the GNU LesserGeneral Public * * License along with OpenFlipper. If not, * * see . * * * \*===========================================================================*/ /*===========================================================================*\ * * * $Revision$ * * $LastChangedBy$ * * $Date$ * * * \*===========================================================================*/ #include "SSAO.hh" #include #include // shader debug mode triggers a shader reload after resizing the view window //#define SSAO_SHADER_DEBUG_MODE const unsigned int SSAOPlugin::numSamples_ = 32; SSAOPlugin::SSAOPlugin() : randomVecTex_(0) { for (unsigned int i = 0; i < 10; ++i) shaders_[i] = 0; for (unsigned int i = 0; i < 6; ++i) programs_[i] = 0; } SSAOPlugin::~SSAOPlugin() { } SSAOPlugin::ViewerResources::ViewerResources() { memset(this, 0, sizeof(ViewerResources)); } void SSAOPlugin::initializePlugin() { memset(shaders_, 0, sizeof(shaders_)); memset(programs_, 0, sizeof(programs_)); randomVecTex_ = 0; generateSamplingKernel(); } ////////////////////////////////////////////////////////////////////////// void SSAOPlugin::generateSamplingKernel() { for (unsigned int i = 0; i < numSamples_; ++i) { ACG::Vec3f r; // get 3 random floats in [-0.5, 0.5] for (int k = 0; k < 3; ++k) { unsigned int x = (rand()*rand()*rand()) & RAND_MAX; r[k] = float(x) / float(RAND_MAX); // [0, 1] r[k] -= 0.5f; } // sphere to hemisphere r[2] = fabsf(r[2]); r.normalize(); // more samples near the fragment // compute a sample distance accordingly float d = float(i+1) / float(numSamples_); d *= d; if (d < 0.1f) d = 0.1f; r *= d; samplingKernel_[i] = r; } } ////////////////////////////////////////////////////////////////////////// void SSAOPlugin::exit() { destroyResources(); } ////////////////////////////////////////////////////////////////////////// QString SSAOPlugin::rendererName() { return QString("SSAO Renderer"); } ////////////////////////////////////////////////////////////////////////// void SSAOPlugin::supportedDrawModes(ACG::SceneGraph::DrawModes::DrawMode& _mode) { _mode = ACG::SceneGraph::DrawModes::DEFAULT; } ////////////////////////////////////////////////////////////////////////// void SSAOPlugin::reloadResources(int _viewerId, unsigned int _sceneTexWidth, unsigned int _sceneTexHeight) { ViewerResources* p = &viewerRes_[_viewerId]; // save window size unsigned int vpWidth = p->glWidth_, vpHeight = p->glHeight_; if (!p->glWidth_ || !p->glHeight_) return; destroyResources(_viewerId); p->glWidth_ = vpWidth; p->glHeight_ = vpHeight; p->rtWidth_ = p->glWidth_; p->rtHeight_ = p->glHeight_; p->rtDownWidth_ = p->rtWidth_ / 2; p->rtDownHeight_ = p->rtHeight_ / 2; p->rtSceneWidth_ = _sceneTexWidth; p->rtSceneHeight_ = _sceneTexHeight; // the scene texture contains the color result of a standard scene render pass // format: R8G8B8A8 glGenTextures(1, &p->sceneBufTex_); ACG::GLState::bindTexture(GL_TEXTURE_2D, p->sceneBufTex_); // texture access: clamped glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); // filter: none (1 to 1 mapping in final pass) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, p->rtSceneWidth_, p->rtSceneHeight_, 0, GL_RGB, GL_UNSIGNED_BYTE, 0); // depth buf render texture // format: R32F, maybe change to R16F if it works ok glGenTextures(1, &p->depthBufTex_); ACG::GLState::bindTexture(GL_TEXTURE_2D, p->depthBufTex_); // texture access: clamped glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); // filter: linear glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexImage2D(GL_TEXTURE_2D, 0, GL_R32F, p->glWidth_, p->glHeight_, 0, GL_RGB, GL_FLOAT, 0); // scene normals glGenTextures(1, &p->sceneNormalTex_); ACG::GLState::bindTexture(GL_TEXTURE_2D, p->sceneNormalTex_); // texture access: clamped glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); // filter: linear glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, p->glWidth_, p->glHeight_, 0, GL_RGB, GL_UNSIGNED_BYTE, 0); // downsampled render textures // format: R32F for (int i = 0; i < 2; ++i) { glGenTextures(1, i ? (&p->downsampledTex_) : (&p->downsampledTmpTex_)); ACG::GLState::bindTexture(GL_TEXTURE_2D, i ? (p->downsampledTex_) : (p->downsampledTmpTex_)); // texture access: clamped glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); // filter: linear glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexImage2D(GL_TEXTURE_2D, 0, GL_R32F, p->rtDownWidth_, p->rtDownHeight_, 0, GL_RGB, GL_FLOAT, 0); } glGenTextures(1, &p->occlusionTex_); ACG::GLState::bindTexture(GL_TEXTURE_2D, p->occlusionTex_); // texture access: clamped glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); // filter: linear glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexImage2D(GL_TEXTURE_2D, 0, GL_R32F, p->glWidth_, p->glHeight_, 0, GL_RGB, GL_FLOAT, 0); // end of texture creation ACG::GLState::bindTexture(GL_TEXTURE_2D, 0); ACG::glCheckErrors(); // create depth render buffer glGenRenderbuffersEXT(1, &p->depthSSAORenderBuf_); glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, p->depthSSAORenderBuf_); glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT, p->glWidth_, p->glHeight_); glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0); // initialize the fbo glGenFramebuffersEXT(1, &p->ssaoFbo_); ACG::GLState::bindFramebuffer(GL_FRAMEBUFFER_EXT, p->ssaoFbo_); // color_attachment order: // scene color, depth, scene normals, occlusion glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, p->depthBufTex_, 0); glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT1_EXT, GL_TEXTURE_2D, p->sceneNormalTex_, 0); glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT2_EXT, GL_TEXTURE_2D, p->occlusionTex_, 0); glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, p->depthSSAORenderBuf_); GLenum fboStatus = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT); if(fboStatus != GL_FRAMEBUFFER_COMPLETE_EXT) printf("SSAO Plugin: ssaoFbo failed to initialize\n"); glGenFramebuffersEXT(1, &p->sceneFbo_); ACG::GLState::bindFramebuffer(GL_FRAMEBUFFER_EXT, p->sceneFbo_); glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, p->sceneBufTex_, 0); if (p->rtSceneWidth_ > p->rtWidth_ || p->rtSceneHeight_ > p->rtHeight_) { // use new depth buffer for multisampling glGenRenderbuffersEXT(1, &p->depthSceneRenderBuf_); glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, p->depthSceneRenderBuf_); glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT, p->rtSceneWidth_, p->rtSceneHeight_); glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0); glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, p->depthSceneRenderBuf_); } else glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, p->depthSSAORenderBuf_); fboStatus = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT); if(fboStatus != GL_FRAMEBUFFER_COMPLETE_EXT) printf("SSAO Plugin: sceneFbo failed to initialize\n"); glGenFramebuffersEXT(1, &p->blurFbo_); ACG::GLState::bindFramebuffer(GL_FRAMEBUFFER_EXT, p->blurFbo_); glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, p->downsampledTex_, 0); glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT1_EXT, GL_TEXTURE_2D, p->downsampledTmpTex_, 0); fboStatus = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT); if(fboStatus != GL_FRAMEBUFFER_COMPLETE_EXT) printf("SSAO Plugin: blurFbo failed to initialize\n"); ACG::GLState::bindFramebuffer(GL_FRAMEBUFFER_EXT, 0); // load shaders const char* ShaderFiles[] = {"SSAO/init_vertex.glsl", "SSAO/fullscreen_vertex.glsl", "SSAO/init_fragment.glsl", "SSAO/downsampling_fragment.glsl", "SSAO/blur_fragment.glsl", "SSAO/ssao_fragment.glsl", "SSAO/final_fragment.glsl", "SSAO/final_MSAA_vertex.glsl", "SSAO/final_MSAA_fragment.glsl"}; for (int i = 0; i < 9; ++i) { QString shaderFile = OpenFlipper::Options::shaderDirStr() + QDir::separator() + QString(ShaderFiles[i]); #ifdef SSAO_SHADER_DEBUG_MODE delete shaders_[i]; #else if (shaders_[i]) continue; #endif if (i < 2 || i == 7) // first two are vertex shaders shaders_[i] = GLSL::loadVertexShader(shaderFile.toUtf8()); else shaders_[i] = GLSL::loadFragmentShader(shaderFile.toUtf8()); if (!shaders_[i]) { log(LOGERR, QString(ShaderFiles[i]) + QString(" could not be loaded and compiled")); return; } } // all needed glprograms for (int i = 0; i < 6; ++i) { #ifndef SSAO_SHADER_DEBUG_MODE if (!programs_[i]) #endif { delete programs_[i]; programs_[i] = new GLSL::Program(); GLSL::Program* pr = programs_[i]; switch (i) { case PROG_INIT: pr->attach(shaders_[0]); pr->attach(shaders_[2]); break; case PROG_DOWNSAMPLING: pr->attach(shaders_[1]); pr->attach(shaders_[3]); break; case PROG_BLUR: pr->attach(shaders_[1]); pr->attach(shaders_[4]); break; case PROG_SSAO: pr->attach(shaders_[1]); pr->attach(shaders_[5]); break; case PROG_FINAL: pr->attach(shaders_[1]); pr->attach(shaders_[6]); break; case PROG_FINAL_MSAA: pr->attach(shaders_[7]); pr->attach(shaders_[8]); break; } pr->link(); } } if (!randomVecTex_) { // random vector texture glGenTextures(1, &randomVecTex_); ACG::GLState::bindTexture(GL_TEXTURE_2D, randomVecTex_); // texture access: wrapped glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); // filter: none glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); ACG::Vec4f randVecs[16]; for (int i = 0; i < 16; ++i) { ACG::Vec3f x; for (int k = 0; k < 3; ++k) x[k] = float(rand()) / float(RAND_MAX); float theta = x[0] * 6.2831853f; // 2pi float phi = x[1] * 6.2831853f; randVecs[i][0] = sinf(phi); randVecs[i][1] = cosf(phi); randVecs[i][2] = sinf(theta); randVecs[i][3] = cosf(theta); } glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F_ARB, 4, 4, 0, GL_RGBA, GL_FLOAT, randVecs); } ACG::GLState::bindTexture(GL_TEXTURE_2D, 0); ACG::glCheckErrors(); } ////////////////////////////////////////////////////////////////////////// void SSAOPlugin::destroyResources() { for (unsigned int i = 0; i < sizeof(programs_) / sizeof(programs_[0]); ++i) { delete programs_[i]; programs_[i] = 0; } for (unsigned int i = 0; i < sizeof(shaders_) / sizeof(shaders_[0]); ++i) { delete shaders_[i]; shaders_[i] = 0; } if (randomVecTex_) glDeleteTextures(1, &randomVecTex_); randomVecTex_ = 0; // free all viewer specific resources std::map::iterator resIt = viewerRes_.begin(); for (; resIt != viewerRes_.end(); ++resIt) destroyResources(resIt->first); } ////////////////////////////////////////////////////////////////////////// void SSAOPlugin::destroyResources(int _viewerId) { ViewerResources* p = &viewerRes_[_viewerId]; if (p->sceneFbo_) glDeleteFramebuffersEXT(1, &p->sceneFbo_); if (p->ssaoFbo_) glDeleteFramebuffersEXT(1, &p->ssaoFbo_); if (p->blurFbo_) glDeleteFramebuffersEXT(1, &p->blurFbo_); if (p->depthSSAORenderBuf_) glDeleteRenderbuffersEXT(1, &p->depthSSAORenderBuf_); if (p->depthSceneRenderBuf_) glDeleteRenderbuffersEXT(1, &p->depthSceneRenderBuf_); if (p->sceneBufTex_) glDeleteTextures(1, &p->sceneBufTex_); if (p->depthBufTex_) glDeleteTextures(1, &p->depthBufTex_); if (p->downsampledTex_) glDeleteTextures(1, &p->downsampledTex_); if (p->downsampledTmpTex_) glDeleteTextures(1, &p->downsampledTmpTex_); if (p->occlusionTex_) glDeleteTextures(1, &p->occlusionTex_); if (p->sceneNormalTex_) glDeleteTextures(1, &p->sceneNormalTex_); // zero out memset(p, 0, sizeof(ViewerResources)); } ////////////////////////////////////////////////////////////////////////// void SSAOPlugin::drawQuadProj(float x0, float y0, float w, float h) { // quad in projection space // here only position are passed to GL // tex-coords can be generated in a vertex-shader as follows // uv = pos * (.5, -.5) + (.5, .5) glBegin(GL_QUADS); { glVertex2f(x0, y0); glVertex2f(x0, y0-h); glVertex2f(x0+w, y0-h); glVertex2f(x0+w, y0); } glEnd(); } ////////////////////////////////////////////////////////////////////////// void SSAOPlugin::drawScenePass(ACG::GLState* _glState, Viewer::ViewerProperties& _properties, BaseNode* _sceneGraphRoot) { ACG::SceneGraph::DrawAction action(_properties.drawMode(), *_glState, false); ACG::SceneGraph::traverse_multipass(_sceneGraphRoot, action, *_glState, _properties.drawMode()); } ////////////////////////////////////////////////////////////////////////// void SSAOPlugin::gaussianBlurPass(const ViewerResources* _pViewer, const float* _texelSize, GLenum _targetAttachement, GLuint _srcTexture) { // standard deviation for gaussian blur filter const float gaussStDev = 1.0f; ACG::GLState::drawBuffer(_targetAttachement); float gaussKernel[5]; float sum = 0.0f; // sum of kernel tabs for (int i = 0; i < 5; ++i) { // 1 / (4 pi s^2) e^(-x^2 / s^2 ), constant factor useless here gaussKernel[i] = powf(2.71828f, -float(i*i)*(_texelSize[0]*_texelSize[0] + _texelSize[1]*_texelSize[1]) / (gaussStDev*gaussStDev)); sum += gaussKernel[i]; } // normalize kernel for (int i = 0; i < 5; ++i) gaussKernel[i] /= sum; ACG::GLState::activeTexture(GL_TEXTURE0); ACG::GLState::bindTexture(GL_TEXTURE_2D, _srcTexture); programs_[PROG_BLUR]->setUniform("Tex", 0); programs_[PROG_BLUR]->setUniform("TexelSize", ACG::Vec2f(_texelSize)); programs_[PROG_BLUR]->setUniform("Kernel", gaussKernel, 5); drawQuadProj(); } ////////////////////////////////////////////////////////////////////////// void SSAOPlugin::render(ACG::GLState* _glState, Viewer::ViewerProperties& _properties) { glPushAttrib(GL_ALL_ATTRIB_BITS); const GLuint targetFbo = ACG::GLState::getFramebufferDraw(); int viewerId = _properties.viewerId(); ViewerResources* pViewer = &viewerRes_[viewerId]; pViewer->glWidth_ = _glState->viewport_width(); pViewer->glHeight_ = _glState->viewport_height(); if (_properties.multisampling()) { if ((pViewer->glWidth_ * 2 != pViewer->rtSceneWidth_) || (pViewer->glHeight_ * 2 != pViewer->rtSceneHeight_)) reloadResources(viewerId, pViewer->glWidth_ * 2, pViewer->glHeight_ * 2); } else if ((pViewer->glWidth_ != pViewer->rtSceneWidth_) || (pViewer->glHeight_ != pViewer->rtSceneHeight_)) reloadResources(viewerId, pViewer->glWidth_, pViewer->glHeight_); BaseNode* sceneGraphRoot = PluginFunctions::getSceneGraphRootNode(); GLenum drawBuffers[] = {GL_COLOR_ATTACHMENT0_EXT, GL_COLOR_ATTACHMENT1_EXT, GL_COLOR_ATTACHMENT2_EXT, GL_COLOR_ATTACHMENT3_EXT, GL_COLOR_ATTACHMENT4_EXT, GL_COLOR_ATTACHMENT5_EXT, GL_COLOR_ATTACHMENT6_EXT}; // the farthest depth value possible in the depth buffer const float maxDepth = 1000.0f; GLint oldViewport[4]; glGetIntegerv(GL_VIEWPORT, oldViewport); for (int i = 0; i < 6; ++i) { ACG::GLState::activeTexture(GL_TEXTURE0 + i); ACG::GLState::bindTexture(GL_TEXTURE_2D, 0); } float texelSize[4] = {1.0f / float(pViewer->rtWidth_), 1.0f / float(pViewer->rtHeight_), 0.0f, 0.0f}; // --------------------------------------------- // 1. render scene with standard materials: glViewport(0, 0, pViewer->rtSceneWidth_, pViewer->rtSceneHeight_); ACG::GLState::bindFramebuffer(GL_FRAMEBUFFER_EXT, pViewer->sceneFbo_); ACG::GLState::drawBuffer(drawBuffers[0]); // scene buffer in render target 0 glClearColor(_glState->clear_color()[0], _glState->clear_color()[1], _glState->clear_color()[2], 0); // NOTE: for some reason the early z pass optimization does not work here // using the depth buffer from previous pass gives z fighting // early z cull optimization settings: // ACG::GLState::enable(GL_DEPTH_TEST); // ACG::GLState::depthFunc(GL_LEQUAL); // ACG::GLState::lockDepthFunc(); // glDepthMask(GL_FALSE); // disable z writing // glClear(GL_COLOR_BUFFER_BIT); // without early z cull: ACG::GLState::enable(GL_DEPTH_TEST); ACG::GLState::depthFunc(GL_LESS); glDepthMask(GL_TRUE); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); drawScenePass(_glState, _properties, sceneGraphRoot); ACG::GLState::unlockDepthFunc(); // unlock less-equal depth function if (pViewer->rtSceneWidth_ != pViewer->glWidth_ || pViewer->rtSceneHeight_ != pViewer->glHeight_) glViewport(0, 0, pViewer->glWidth_, pViewer->glHeight_); // --------------------------------------------- // 2. init depth and normal targets ACG::GLState::bindFramebuffer(GL_FRAMEBUFFER_EXT, pViewer->ssaoFbo_); ACG::GLState::enable(GL_DEPTH_TEST); ACG::GLState::depthFunc(GL_LESS); glDepthMask(GL_TRUE); // color attachment 0 and 1 stores the scene depth and normals // clear first ACG::GLState::drawBuffer(drawBuffers[0]); glClearColor(maxDepth, 0.0f, 0.0f, 0.0f); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); ACG::GLState::drawBuffer(drawBuffers[1]); glClearColor(0.5f, 0.5f, 0.0f, 0.0f); glClear(GL_COLOR_BUFFER_BIT); ACG::GLState::drawBuffers(2, drawBuffers); programs_[PROG_INIT]->use(); drawScenePass(_glState, _properties, sceneGraphRoot); programs_[PROG_INIT]->disable(); // --------------------------------------------- // 3. compute occlusion ACG::GLState::drawBuffer(drawBuffers[2]); // occlusion buffer in render target 2 ACG::GLState::disable(GL_DEPTH_TEST); texelSize[0] = 1.0f / float(pViewer->rtWidth_); texelSize[1] = 1.0f / float(pViewer->rtHeight_); programs_[PROG_SSAO]->use(); programs_[PROG_SSAO]->setUniform("TexelSize", ACG::Vec2f(texelSize[0], texelSize[1])); programs_[PROG_SSAO]->setUniform("ScreenSize", ACG::Vec2f(pViewer->rtWidth_, pViewer->rtHeight_)); { GLint location = programs_[PROG_SSAO]->getUniformLocation("Kernel"); glUniform3fv(location, 32, (GLfloat*)samplingKernel_); } programs_[PROG_SSAO]->setUniform("RandTex", 3); programs_[PROG_SSAO]->setUniform("NormalTex", 2); programs_[PROG_SSAO]->setUniform("SceneTex", 1); programs_[PROG_SSAO]->setUniform("DepthTex", 0); ACG::GLState::activeTexture(GL_TEXTURE3); ACG::GLState::bindTexture(GL_TEXTURE_2D, randomVecTex_); ACG::GLState::activeTexture(GL_TEXTURE2); ACG::GLState::bindTexture(GL_TEXTURE_2D, pViewer->sceneNormalTex_); ACG::GLState::activeTexture(GL_TEXTURE1); ACG::GLState::bindTexture(GL_TEXTURE_2D, pViewer->sceneBufTex_); ACG::GLState::activeTexture(GL_TEXTURE0); ACG::GLState::bindTexture(GL_TEXTURE_2D, pViewer->depthBufTex_); drawQuadProj(); ACG::GLState::activeTexture(GL_TEXTURE2); ACG::GLState::bindTexture(GL_TEXTURE_2D, 0); // --------------------------------------------- // 4. downsample the occlusion texture to 1/4 its size glViewport(0, 0, pViewer->rtDownWidth_, pViewer->rtDownHeight_); ACG::GLState::bindFramebuffer(GL_FRAMEBUFFER_EXT, pViewer->blurFbo_); ACG::GLState::drawBuffer(drawBuffers[0]); // disable depth testing and writing from now on ACG::GLState::disable(GL_DEPTH_TEST); programs_[PROG_DOWNSAMPLING]->use(); programs_[PROG_DOWNSAMPLING]->setUniform("TexelSize", ACG::Vec2f(texelSize)); // bind depth rt ACG::GLState::activeTexture(GL_TEXTURE0); ACG::GLState::bindTexture(GL_TEXTURE_2D, pViewer->occlusionTex_); programs_[PROG_DOWNSAMPLING]->setUniform("Tex", 0); drawQuadProj(); //----------------------------------------- // 5. gaussian blur filter programs_[PROG_BLUR]->use(); programs_[PROG_BLUR]->setUniform("DepthTex", 1); programs_[PROG_BLUR]->setUniform("EdgeBlur", _properties.multisampling() ? 0.3f : 0.0f); ACG::GLState::activeTexture(GL_TEXTURE1); ACG::GLState::bindTexture(GL_TEXTURE_2D, pViewer->depthBufTex_); // horizontal texelSize[0] = 1.0f / float(pViewer->rtDownWidth_); texelSize[1] = 0.0f; gaussianBlurPass(pViewer, texelSize, drawBuffers[1], pViewer->downsampledTex_); // vertical texelSize[0] = 0.0f; texelSize[1] = 1.0f / float(pViewer->rtDownHeight_); gaussianBlurPass(pViewer, texelSize, drawBuffers[0], pViewer->downsampledTmpTex_); // blurred result in pViewer->downsampledTex_ //----------------------------------------- // 6. final pass, present result glViewport(oldViewport[0], oldViewport[1], oldViewport[2], oldViewport[3]); ACG::GLState::bindFramebuffer(GL_FRAMEBUFFER_EXT, targetFbo); ACG::GLState::activeTexture(GL_TEXTURE1); ACG::GLState::bindTexture(GL_TEXTURE_2D, pViewer->downsampledTex_); ACG::GLState::activeTexture(GL_TEXTURE0); ACG::GLState::bindTexture(GL_TEXTURE_2D, pViewer->sceneBufTex_); GLSL::Program* finalProg = programs_[_properties.multisampling() ? PROG_FINAL_MSAA : PROG_FINAL]; finalProg->use(); finalProg->setUniform("OcclusionTex", 1); finalProg->setUniform("SceneTex", 0); if (_properties.multisampling()) finalProg->setUniform("SceneTexelSize", ACG::Vec2f(1.0f / float(pViewer->rtSceneWidth_), 1.0f / float(pViewer->rtSceneHeight_))); drawQuadProj(); finalProg->disable(); //----------------------------------------- glPopAttrib(); } Q_EXPORT_PLUGIN2( ssaoplugin , SSAOPlugin );