/*===========================================================================*\ * * * 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 "DepthPeeling.hh" #include #include // this define enables a shader export of the generated peel shader for debugging purpose //#define DEPTHPEELING_SHADER_EXPORT DepthPeelingPlugin::DepthPeelingPlugin() : blendQueryID_(0), glStateTmp_(0), numLights_(0) { for (unsigned int i = 0; i < 16; ++i) lightTypes_[i] = LIGHTTYPE_POINT; for (unsigned int i = 0; i < PEEL_NUM_COMBINATIONS; ++i) peelProgs_[i] = 0; for (unsigned int i = 0; i < PEEL_NUM_COMBINATIONS*3; ++i) peelShaders_[i] = 0; for (unsigned int i = 0; i < 8; ++i) blendShaders_[i] = 0; for (unsigned int i = 0; i < 4; ++i) blendDualPeelProg_[i] = 0; } DepthPeelingPlugin::~DepthPeelingPlugin() { } DepthPeelingPlugin::ViewerResources::ViewerResources() { rtWidth_ = rtHeight_ = 0; glWidth_ = glHeight_ = 0; memset(blendDualPeelTexID_, 0, 7*4); blendDualPeelFbo_ = 0; } void DepthPeelingPlugin::initializePlugin() { memset(blendShaders_, 0, sizeof(blendShaders_)); memset(peelShaders_, 0, sizeof(peelShaders_)); memset(blendDualPeelProg_, 0, sizeof(blendDualPeelProg_)); memset(peelProgs_, 0, sizeof(peelProgs_)); blendQueryID_ = 0; numLights_ = 0; ACG::glCheckErrors(); } ////////////////////////////////////////////////////////////////////////// void DepthPeelingPlugin::exit() { destroyResources(); } ////////////////////////////////////////////////////////////////////////// QString DepthPeelingPlugin::rendererName() { return QString("Depth Peeling Renderer"); } ////////////////////////////////////////////////////////////////////////// void DepthPeelingPlugin::supportedDrawModes(ACG::SceneGraph::DrawModes::DrawMode& _mode) { _mode = ACG::SceneGraph::DrawModes::DEFAULT; } ////////////////////////////////////////////////////////////////////////// void DepthPeelingPlugin::reloadResources(int _viewerId) { // called in constructor and resizeEvent() ACG::GLState::syncFromGL(); ViewerResources* p = &viewerRes_[_viewerId]; if (!p->glWidth_ || !p->glHeight_) return; destroyResources(_viewerId); // dual depth peeling rt's glGenTextures(7, p->blendDualPeelTexID_); GLint DualPeelIntFmt[] = {GL_RG32F, GL_RG32F, // depth0, depth1 GL_RGBA, GL_RGBA, // front blender0, ..._1 GL_RGBA, GL_RGBA, // back_temp_tex0, ..._1, GL_RGB}; // back_blender for (int i = 0; i < 7; ++i) { GLint fmt = GL_RGB; // fmt for depth textures if (i >= 2) fmt = GL_RGBA; // fmt for front_blender01 and back_temp01 if (i == 6) fmt = GL_RGB; // fmt for back_blender ACG::GLState::bindTexture(GL_TEXTURE_RECTANGLE_EXT, p->blendDualPeelTexID_[i]); // texture access: clamped glTexParameteri(GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_WRAP_S, GL_CLAMP); glTexParameteri(GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_WRAP_T, GL_CLAMP); // filter: none glTexParameteri(GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexImage2D(GL_TEXTURE_RECTANGLE_EXT, 0, DualPeelIntFmt[i], p->glWidth_, p->glHeight_, 0, fmt, GL_FLOAT, 0); ACG::glCheckErrors(); } p->rtWidth_ = p->glWidth_; p->rtHeight_ = p->glHeight_; // dual depth peeling fbo's glGenFramebuffersEXT(1, &p->blendDualPeelFbo_); { // layer_peel fbo ACG::GLState::bindFramebuffer(GL_FRAMEBUFFER, p->blendDualPeelFbo_); // color_attachment order: // depth0, front_blend0, back_temp0, depth1, front_blend1, back_temp1, back_blender_tex_id for (int i = 0; i < 6; ++i) { glFramebufferTexture2DEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0+i, GL_TEXTURE_RECTANGLE_EXT, p->blendDualPeelTexID_[i < 3 ? 2*i : 2*(i-3) +1], 0); } // back_blender_tex_id glFramebufferTexture2DEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT6, GL_TEXTURE_RECTANGLE_EXT, p->blendDualPeelTexID_[6], 0); } ACG::GLState::bindFramebuffer(GL_FRAMEBUFFER, 0); // load shaders const char* ShaderFiles[] = {"Blending/dual_peeling_init_vertex.glsl", "Blending/dual_peeling_init_fragment.glsl", "Blending/dual_peeling_blend_vertex.glsl", "Blending/dual_peeling_blend_fragment.glsl", "Blending/dual_peeling_final_vertex.glsl", "Blending/dual_peeling_final_fragment.glsl"}; for (int i = 0; i < 6; ++i) { QString shaderFile = OpenFlipper::Options::shaderDirStr() + QDir::separator() + QString(ShaderFiles[i]); if (blendShaders_[i]) continue; if (i & 1) // alternating vertex/fragment shader blendShaders_[i] = GLSL::loadFragmentShader(shaderFile.toUtf8()); else blendShaders_[i] = GLSL::loadVertexShader(shaderFile.toUtf8()); if (!blendShaders_[i]) { log(LOGERR, QString(ShaderFiles[i]) + QString(" could not be loaded and compiled")); return; } } // dual depth peeling programs if (!blendDualPeelProg_[0]) { for (int i = 0; i < 4; ++i) { if (i != 1) blendDualPeelProg_[i] = new GLSL::Program(); } // init shaders blendDualPeelProg_[0]->attach(blendShaders_[0]); blendDualPeelProg_[0]->attach(blendShaders_[1]); blendDualPeelProg_[0]->link(); // blend shaders blendDualPeelProg_[2]->attach(blendShaders_[2]); blendDualPeelProg_[2]->attach(blendShaders_[3]); blendDualPeelProg_[2]->link(); // final shaders blendDualPeelProg_[3]->attach(blendShaders_[4]); blendDualPeelProg_[3]->attach(blendShaders_[5]); blendDualPeelProg_[3]->link(); } if (!blendQueryID_) glGenQueries(1, &blendQueryID_); ACG::glCheckErrors(); } ////////////////////////////////////////////////////////////////////////// void DepthPeelingPlugin::destroyResources() { // called in destructor and reloadBlendingTech() if (blendQueryID_) glDeleteQueries(1, &blendQueryID_); blendQueryID_ = 0; for (int i = 0; i < 4; ++i) { delete blendDualPeelProg_[i]; blendDualPeelProg_[i] = 0; } for (unsigned int i = 0; i < sizeof(blendShaders_) / sizeof(blendShaders_[0]); ++i) { delete blendShaders_[i]; blendShaders_[i] = 0; } for (unsigned int i = 0; i < sizeof(peelShaders_) / sizeof(peelShaders_[0]); ++i) { delete peelShaders_[i]; peelShaders_[i] = 0; } for (unsigned int i = 0; i < sizeof(peelProgs_) / sizeof(peelProgs_[0]); ++i) { delete peelProgs_[i]; peelProgs_[i] = 0; } // free all viewer specific resources std::map::iterator resIt = viewerRes_.begin(); for (; resIt != viewerRes_.end(); ++resIt) destroyResources(resIt->first); } ////////////////////////////////////////////////////////////////////////// void DepthPeelingPlugin::destroyResources(int _viewerId) { ViewerResources* p = &viewerRes_[_viewerId]; if (p->blendDualPeelFbo_) glDeleteFramebuffersEXT(1, &p->blendDualPeelFbo_); p->blendDualPeelFbo_ = 0; if (p->blendDualPeelTexID_) glDeleteTextures(7, p->blendDualPeelTexID_); memset(p->blendDualPeelTexID_, 0, sizeof(p->blendDualPeelTexID_)); } ////////////////////////////////////////////////////////////////////////// void DepthPeelingPlugin::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 DepthPeelingPlugin::traverseLightNodes( BaseNode* _node) { std::vector pLightTypes; if (_node) { BaseNode::StatusMode status(_node->status()); bool process_children(status != BaseNode::HideChildren); // If the subtree is hidden, ignore this node and its children while rendering if (status != BaseNode::HideSubtree) { if (_node->status() != BaseNode::HideNode) { ACG::SceneGraph::LightNode* lnode = dynamic_cast(_node); if (lnode) { ACG::SceneGraph::LightSource light; lnode->getLightSource(&light); // GLenum lightID = lnode->getGLLightID(); GLenum lightID = numLights_ + GL_LIGHT0; if (lightID != GL_INVALID_ENUM) { int id = lightID - GL_LIGHT0; if (numLights_ < 8) { if (light.directional()) lightTypes_[numLights_] = LIGHTTYPE_DIRECTIONAL; else if (light.spotCutoff() > 179.5f) lightTypes_[numLights_] = LIGHTTYPE_POINT; else lightTypes_[numLights_] = LIGHTTYPE_SPOT; glLightIDs_[numLights_] = id; ++numLights_; } } } } if (process_children) { BaseNode::ChildIter cIt, cEnd(_node->childrenEnd()); // Process all children which are not second pass for (cIt = _node->childrenBegin(); cIt != cEnd; ++cIt) if (~(*cIt)->traverseMode() & BaseNode::SecondPass) traverseLightNodes(*cIt); // Process all children which are second pass for (cIt = _node->childrenBegin(); cIt != cEnd; ++cIt) if ((*cIt)->traverseMode() & BaseNode::SecondPass) traverseLightNodes(*cIt); } } } } // depth peeling shader generation void DepthPeelingPlugin::generatePeelingShaders(GLSL::StringList* _strVertexShaderOut, GLSL::StringList* _strFragmentShaderOut, GLSL::StringList* _strGeometryShaderOut, bool _textured, bool _flatShaded, bool _phong, bool _vertexColor, bool _wireFrame) { if (_flatShaded) _phong = false; if (_vertexColor) _phong = false; std::string strColor = "color"; std::string strNormal = "normal"; std::string strFragPos = "vPosVS"; if (_flatShaded) strFragPos = "VPosVS"; if (_wireFrame) _flatShaded = _vertexColor = _textured = _phong = false; std::string strCode = ""; for (unsigned int i = 0; i < numLights_ && (!_wireFrame); ++i) { // new code block for this light strCode += "{\n"; std::string strLight = "gl_LightSource["; std::string strMaterial = "gl_FrontLightProduct["; { char szTmp[8]; sprintf(szTmp, "%d]", glLightIDs_[i]); strLight += szTmp; strMaterial += szTmp; } // light vector if (lightTypes_[i] == LIGHTTYPE_DIRECTIONAL) strCode += "vec3 vLightVS = " + strLight + ".position.xyz;\n"; else strCode += "vec3 vLightVS = " + strLight + ".position.xyz - " + strFragPos + ".xyz;\n"; strCode += "vec3 vLightDir = normalize(vLightVS);\n"; // ambient strCode += "vec4 colLight = " + strMaterial + ".ambient;\n"; // diffuse strCode += "float ldotn = dot(vLightDir, " + strNormal + ");\n"; strCode += "ldotn = clamp(ldotn, 0.0, 1.0);\n"; strCode += "colLight += ldotn * "; if (_textured && _phong) strCode += "diffColor * "; if (_vertexColor) { if (_flatShaded) strCode += "vColor[2];\n"; else strCode += "gl_Color;\n"; } else strCode += strMaterial + ".diffuse;\n"; //specular strCode += "colLight += floor(ldotn + 0.9) * pow(ldotn, gl_FrontMaterial.shininess) * " + strMaterial + ".specular;\n"; if (lightTypes_[i] == LIGHTTYPE_DIRECTIONAL) strCode += strColor + ".rgb += colLight.rgb;\n"; else { // attenuation strCode += "float fLenSq = dot(vLightVS, vLightVS);\n"; strCode += "float atten = " + strLight + ".constantAttenuation + " + strLight + ".linearAttenuation * sqrt(fLenSq) + " + strLight + ".quadraticAttenuation * fLenSq;\n"; if (lightTypes_[i] == LIGHTTYPE_POINT) strCode += strColor + ".rgb += colLight.rgb / atten;\n"; else { // spotlight strCode += "float spot = -dot(vLightDir, " + strLight + ".spotDirection);\n"; strCode += "spot *= step(" + strLight + ".spotCosCutoff, spot);\n"; strCode += "spot *= pow(spot, " + strLight + ".spotExponent);\n"; strCode += strColor + ".rgb += " + "(spot / atten) * colLight.rgb;\n"; } } strCode += "}\n"; } const char* szVertexShader[] = {"varying vec2 vTexCoord;", "varying vec3 vNormal; // normal: view space", "varying vec4 vPosVS; // position in view space", "varying vec4 vColor; // used in gouraud / flat shading only", "", "void main(void)", "{", " gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;", " ", " vNormal = gl_NormalMatrix * gl_Normal; // gl_NormalMatrix : upper left 3x3 of WorldViewIT", " vPosVS = (gl_ModelViewMatrix * gl_Vertex);", " vTexCoord = gl_MultiTexCoord0.xy;", ""}; // NOTE: this vertex shader code misses the ending '}' to append the lighting code later // geometry shader for flat shading only const char* szGeometryShaderStart[] = { "#version 120", "#extension GL_ARB_geometry_shader4 : enable", "", "varying vec2 vTexCoord[3];", "varying vec4 vPosVS[3];", "varying vec3 vNormal[3];", "varying vec4 vColor[3];", "", "varying vec2 VTexCoord;", "varying vec3 VNormal;", "varying vec4 VPosVS;", "varying vec4 VColor;", "", "", "void main()", "{", " // recompute triangle normal", // " vec3 normal = cross(vNormal[2].xyz - vNormal[0].xyz, vNormal[1].xyz - vNormal[0].xyz);", " vec3 normal = cross(gl_PositionIn[1].xyz - gl_PositionIn[0].xyz, gl_PositionIn[2].xyz - gl_PositionIn[0].xyz);", " normal = normalize(normal);", " normal = gl_NormalMatrix * normal;", // " normal = vNormal[2].xyz;", " ", " VPosVS = (gl_PositionIn[0] + gl_PositionIn[1] + gl_PositionIn[2]) / 3.0;", " VPosVS = gl_ModelViewMatrix * VPosVS; // triangle center in view space needed for point and spot lights", " vec4 color = vec4(gl_FrontMaterial.emission.rgb, gl_FrontMaterial.ambient.a);", ""}; const char* szGeometryShaderEnd[] = { "", " for(int i = 0; i < 3; i++)", " {", " gl_Position = gl_ModelViewProjectionMatrix * gl_PositionIn[i];", " VTexCoord = vTexCoord[i];", " VNormal = normal;", " VPosVS = vPosVS[i];", // " VColor = (normal+vec3(0.5,0.5,0.5)).xyzz*0.5;", " VColor = color;", " EmitVertex();", " }", " EndPrimitive();", " ", "}"}; const char* szFragmentShaderStart[] = {"varying vec2 vTexCoord;", "varying vec3 vNormal; ", "varying vec4 vPosVS;", "varying vec4 vColor;", "", "uniform sampler2D DiffuseTex;", "uniform sampler2DRect DepthBlenderTex;", "uniform sampler2DRect FrontBlenderTex;", "", "#define MAX_DEPTH 1.0", "", "", "void main(void)", "{", " // window-space depth interpolated linearly in screen space", " float fragDepth = gl_FragCoord.z;", "", " vec2 depthBlender = texture2DRect(DepthBlenderTex, gl_FragCoord.xy).xy;", " vec4 forwardTemp = texture2DRect(FrontBlenderTex, gl_FragCoord.xy);", " ", " // Depths and 1.0-alphaMult always increase", " // so we can use pass-through by default with MAX blending", " gl_FragData[0].xy = depthBlender;", " ", " // Front colors always increase (DST += SRC*ALPHA_MULT)", " // so we can use pass-through by default with MAX blending", " gl_FragData[1] = forwardTemp;", " ", " // Because over blending makes color increase or decrease,", " // we cannot pass-through by default.", " // Each pass, only one fragment writes a color greater than 0", " gl_FragData[2] = vec4(0.0);", "", " float nearestDepth = -depthBlender.x;", " float farthestDepth = depthBlender.y;", " float alphaMultiplier = 1.0 - forwardTemp.w;", "", " if (fragDepth < nearestDepth || fragDepth > farthestDepth) {", " // Skip this depth in the peeling algorithm", " gl_FragData[0].xy = vec2(-MAX_DEPTH);", " return;", " }", " ", " if (fragDepth > nearestDepth && fragDepth < farthestDepth) {", " // This fragment needs to be peeled again", " gl_FragData[0].xy = vec2(-fragDepth, fragDepth);", " return;", " }", " ", " // If we made it here, this fragment is on the peeled layer from last pass", " // therefore, we need to shade it, and make sure it is not peeled any farther", "",}; const char* szFragmentShaderEnd[] = {"", " gl_FragData[0].xy = vec2(-MAX_DEPTH);", " ", " if (fragDepth == nearestDepth) {", " gl_FragData[1].xyz += color.rgb * color.a * alphaMultiplier;", " gl_FragData[1].w = 1.0 - alphaMultiplier * (1.0 - color.a);", " } else {", " gl_FragData[2] += color;", " }", "}"}; // assemble final shaders: // vertex shader for (unsigned int i = 0; i < sizeof(szVertexShader) / sizeof(char*); ++i) { std::string str = szVertexShader[i]; if (_phong) { if (!strncmp("varying vec4 vColor", str.c_str(), strlen("varying vec4 vColor"))) { // str.replace(str.begin(), str.begin() + 7, "flat out"); str += "\n"; continue; } } str += "\n"; _strVertexShaderOut->push_back(str); } // finalize vertex shader if (!_phong && !_flatShaded && !_wireFrame) { // lighting code: _strVertexShaderOut->push_back("\nvec4 color = vec4(gl_FrontMaterial.emission.rgb, gl_FrontMaterial.ambient.a);"); _strVertexShaderOut->push_back("\nvec3 normal = vNormal;\n"); std::string::size_type curPos = 0; std::string::size_type newLinePos = strCode.find_first_of('\n'); while (newLinePos != std::string::npos) { _strVertexShaderOut->push_back(strCode.substr(curPos, newLinePos - curPos + 1)); curPos = newLinePos + 1; newLinePos = strCode.find_first_of('\n', curPos); } _strVertexShaderOut->push_back("\nvColor = color;\n"); } if (_flatShaded) { if (_vertexColor) _strVertexShaderOut->push_back("vColor = gl_Color;\n"); _strVertexShaderOut->push_back("gl_Position = gl_Vertex;\n"); } _strVertexShaderOut->push_back("\n}"); // fragment shader: for (unsigned int i = 0; i < sizeof(szFragmentShaderStart) / sizeof(char*); ++i) { std::string str = szFragmentShaderStart[i]; if (_phong) { if (!strncmp("varying vec4 vColor", str.c_str(), strlen("varying vec4 vColor"))) { // str.replace(str.begin(), str.begin() + 7, "flat in"); str += "\n"; continue; } } if (_flatShaded) { if (!strncmp("varying vec2 vTexCoord", str.c_str(), strlen("varying vec2 vTexCoord"))) str = "varying vec2 VTexCoord;"; if (!strncmp("varying vec3 vNormal", str.c_str(), strlen("varying vec3 vNormal"))) str = "varying vec3 VNormal;"; if (!strncmp("varying vec4 vPosVS", str.c_str(), strlen("varying vec4 vPosVS"))) str = "varying vec4 VPosVS;"; if (!strncmp("varying vec4 vColor", str.c_str(), strlen("varying vec4 vColor"))) str = "varying vec4 VColor;"; } str += "\n"; _strFragmentShaderOut->push_back(str); } if (!_wireFrame) { if (!_flatShaded) { _strFragmentShaderOut->push_back(" vec3 normal = normalize(vNormal);\n"); // _strFragmentShaderOut->push_back(" vec3 normal = normalize(cross(dFdx(vPosVS.xyz), dFdy(vPosVS.xyz)));\n"); _strFragmentShaderOut->push_back(" vec4 color = vec4(gl_FrontMaterial.emission.rgb, gl_FrontMaterial.ambient.a);\n"); if (_textured) _strFragmentShaderOut->push_back(" vec4 diffColor = texture2D(DiffuseTex, vTexCoord); color.a *= diffColor.a;\n"); } else { _strFragmentShaderOut->push_back(" vec4 color = vec4(gl_FrontMaterial.emission.rgb, gl_FrontMaterial.ambient.a);\n"); _strFragmentShaderOut->push_back(" vec4 vColor = VColor;\n"); if (_textured) _strFragmentShaderOut->push_back(" vec4 diffColor = texture2D(DiffuseTex, VTexCoord); color.a *= diffColor.a;\n"); } if (!_phong) { if (_textured) _strFragmentShaderOut->push_back(" color = diffColor * vColor;\n"); else _strFragmentShaderOut->push_back(" color = vColor;\n"); } else { // lighting code: std::string::size_type curPos = 0; std::string::size_type newLinePos = strCode.find_first_of('\n'); while (newLinePos != std::string::npos) { _strFragmentShaderOut->push_back(strCode.substr(curPos, newLinePos - curPos + 1)); curPos = newLinePos + 1; newLinePos = strCode.find_first_of('\n', curPos); } } } else // wireframe: no shading necessary _strFragmentShaderOut->push_back(" vec4 color = vec4(1.0, 1.0, 1.0, gl_FrontMaterial.ambient.a);\n"); for (unsigned int i = 0; i < sizeof(szFragmentShaderEnd) / sizeof(char*); ++i) { std::string str = szFragmentShaderEnd[i]; str += "\n"; _strFragmentShaderOut->push_back(str); } // geometry shader: if (_flatShaded) { for (unsigned int i = 0; i < sizeof(szGeometryShaderStart) / sizeof(char*); ++i) { std::string str = szGeometryShaderStart[i]; str += "\n"; _strGeometryShaderOut->push_back(str); } // lighting code: std::string::size_type curPos = 0; std::string::size_type newLinePos = strCode.find_first_of('\n'); while (newLinePos != std::string::npos) { _strGeometryShaderOut->push_back(strCode.substr(curPos, newLinePos - curPos + 1)); curPos = newLinePos + 1; newLinePos = strCode.find_first_of('\n', curPos); } for (unsigned int i = 0; i < sizeof(szGeometryShaderEnd) / sizeof(char*); ++i) { std::string str = szGeometryShaderEnd[i]; str += "\n"; _strGeometryShaderOut->push_back(str); } } // debug output: #ifdef DEPTHPEELING_SHADER_EXPORT FILE* pShaderOut = fopen("peel_vertex.glsl", "wt"); for (GLSL::StringList::iterator it = _strVertexShaderOut->begin(); it != _strVertexShaderOut->end(); ++it) fprintf(pShaderOut, it->c_str()); fclose(pShaderOut); pShaderOut = fopen("peel_frag.glsl", "wt"); for (GLSL::StringList::iterator it = _strFragmentShaderOut->begin(); it != _strFragmentShaderOut->end(); ++it) fprintf(pShaderOut, it->c_str()); fclose(pShaderOut); if (_flatShaded) { pShaderOut = fopen("peel_geom.glsl", "wt"); for (GLSL::StringList::iterator it = _strGeometryShaderOut->begin(); it != _strGeometryShaderOut->end(); ++it) fprintf(pShaderOut, it->c_str()); fclose(pShaderOut); } #endif } ////////////////////////////////////////////////////////////////////////// template bool DepthPeelingPlugin::traverseDrawApplyAction( BaseNode* _node, Action& _action, ACG::SceneGraph::DrawModes::DrawMode _globalDrawMode, int _pass, int _peelPass) { bool ret = true; // process_children flag returned // use nodes drawmode to grab a peeler program with correct shading ACG::SceneGraph::DrawModes::DrawMode dm, dmSave = _node->drawMode(); dm = dmSave; if (!dm) return ret; if (dm & ACG::SceneGraph::DrawModes::DEFAULT) dm = _globalDrawMode; /* if (dm & ACG::SceneGraph::DrawModes::HIDDENLINE) { // hiddenline is accomplished with wireframe in first peel layer only dm &= ~ACG::SceneGraph::DrawModes::HIDDENLINE; dm |= ACG::SceneGraph::DrawModes::WIREFRAME; if (_peelPass > 2) return ret; _node->drawMode(dm); } */ if (dm & ACG::SceneGraph::DrawModes::WIREFRAME || dm & ACG::SceneGraph::DrawModes::HIDDENLINE) { ACG::SceneGraph::DrawModes::DrawMode dmShaded = dm; dmShaded &= ~ACG::SceneGraph::DrawModes::WIREFRAME; dmShaded &= ~ACG::SceneGraph::DrawModes::HIDDENLINE; // polygon only draw if (dmShaded) { _node->drawMode(dmShaded); // evil method: change nodes drawmode here, restore it later ret &= traverseDrawApplyAction(_node, _action, _globalDrawMode, _pass, _peelPass); } // wireframe only follows dm &= (~dmShaded); _node->drawMode(dm); } unsigned int shaderIndex = getPeelShaderIndex(dm); // do hiddenline algorithmus manually here GLenum prev_depth = glStateTmp_->depthFunc(); if (dm & ACG::SceneGraph::DrawModes::HIDDENLINE) { // manual hiddenline // First: // Render all faces in background color to initialize z-buffer _node->drawMode(ACG::SceneGraph::DrawModes::SOLID_SMOOTH_SHADED); GLSL::Program* peelProg = peelProgs_[PEEL_SHADER_HIDDENLINE]; peelProg->use(); peelProg->setUniform("DepthBlenderTex", 4); peelProg->setUniform("FrontBlenderTex", 5); peelProg->setUniform("ObjectColor", glStateTmp_->clear_color()); ACG::GLState::depthRange(0.01, 1.0); ACG::GLState::lockDepthRange(); ACG::GLState::lockProgram(); ret &= _action(_node); ACG::GLState::unlockProgram(); ACG::GLState::unlockDepthRange(); ACG::GLState::depthRange(0.0, 1.0); // Second // Render the lines. All lines not on the front will be skipped in z-test _node->drawMode(ACG::SceneGraph::DrawModes::WIREFRAME); ACG::GLState::depthFunc(GL_LEQUAL); ACG::GLState::lockDepthFunc(); // use wireframe shader now shaderIndex = PEEL_SHADER_WIREFRAME; } GLSL::Program* peelProg = peelProgs_[shaderIndex]; peelProg->use(); peelProg->setUniform("DepthBlenderTex", 4); peelProg->setUniform("FrontBlenderTex", 5); if (shaderIndex & PEEL_SHADER_FLAT) { // set geomtry shader constants peelProg->setGeometryInputType(GL_TRIANGLES); peelProg->setGeometryOutputType(GL_TRIANGLE_STRIP); peelProg->setGeometryVertexCount(3); } peelProg->setUniform("DiffuseTex", 0); ACG::GLState::lockProgram(); ret &= _action(_node); ACG::GLState::unlockProgram(); // restore state _node->drawMode(dmSave); if (dm & ACG::SceneGraph::DrawModes::HIDDENLINE) { ACG::GLState::unlockDepthFunc(); //restore depth buffer comparison function ACG::GLState::depthFunc(prev_depth); } return ret; } ////////////////////////////////////////////////////////////////////////// template void DepthPeelingPlugin::traverseDraw( BaseNode* _node, Action& _action, ACG::SceneGraph::DrawModes::DrawMode _globalDrawMode, int _pass, int _peelPass) { // Process node if it exists if (_node) { BaseNode::StatusMode status(_node->status()); bool process_children(status != BaseNode::HideChildren); // If the subtree is hidden, ignore this node and its children while rendering if (status != BaseNode::HideSubtree) { // Executes this nodes enter function (if available and active in multipass) if ( _node->multipassStatusActive(_pass) ) { if_has_enter(_action, _node); } // If the node itself is hidden, don't call the action on it. // Additionally check if rendering order is node first. otherwise, we will call it after the children. // And check if it should be called in this rendering pass. if ( (_node->status() != BaseNode::HideNode ) && ( _node->traverseMode() & BaseNode::NodeFirst ) && _node->multipassNodeActive(_pass)) process_children &= traverseDrawApplyAction(_node, _action, _globalDrawMode, _pass, _peelPass); if (process_children) { BaseNode::ChildIter cIt, cEnd(_node->childrenEnd()); // Process all children for (cIt = _node->childrenBegin(); cIt != cEnd; ++cIt) if (~(*cIt)->traverseMode() & BaseNode::SecondPass) traverseDraw(*cIt, _action, _globalDrawMode, _pass, _peelPass); // Process all children which are second pass for (cIt = _node->childrenBegin(); cIt != cEnd; ++cIt) if ((*cIt)->traverseMode() & BaseNode::SecondPass) traverseDraw(*cIt, _action, _globalDrawMode, _pass, _peelPass); } // If we are in childrenfirst node, the children have been painted andwe now check, if we can draw this node. // If its hidden, ignore it. // If it should not be rendered in this pass, ignore it too. if ( (_node->traverseMode() & BaseNode::ChildrenFirst ) && (_node->status() != BaseNode::HideNode) && _node->multipassNodeActive(_pass) ) process_children &= traverseDrawApplyAction(_node, _action, _globalDrawMode, _pass, _peelPass); // Call the leave function of the node (if available and active in multipass). if ( _node->multipassStatusActive(_pass) ) if_has_leave(_action, _node); } // if (status != BaseNode::HideSubtree) } // if(node_) } void DepthPeelingPlugin::drawScenePeelPass(ACG::GLState* _glState, ACG::SceneGraph::DrawModes::DrawMode _drawMode, BaseNode* _sceneGraphRoot, int _peelPass) { ACG::SceneGraph::DrawAction action(_drawMode, *_glState, false); // traverseDraw(_sceneGraphRoot, action, *_glState, _drawMode); // Reset render pass counter _glState->reset_render_pass(); // Get max render passes unsigned int max_passes = _glState->max_render_passes(); // Render all passes for(unsigned int pass = BaseNode::PASS_1; pass <= (BaseNode::PASS_1 + max_passes); ++pass) { // Traverse scenegraph traverseDraw (_sceneGraphRoot, action, _drawMode, pass, _peelPass); // Increment render pass counter by 1 _glState->next_render_pass(); } // Reset render pass counter _glState->reset_render_pass(); } ////////////////////////////////////////////////////////////////////////// void DepthPeelingPlugin::drawScenePass(ACG::GLState* _glState, ACG::SceneGraph::DrawModes::DrawMode _drawMode, BaseNode* _sceneGraphRoot) { ACG::SceneGraph::DrawAction action(_drawMode, *_glState, false); traverse_multipass(_sceneGraphRoot, action, *_glState, _drawMode); } ////////////////////////////////////////////////////////////////////////// void DepthPeelingPlugin::updatePeelingShaderSet() { bool rebuildShaders = false; BaseNode* sceneGraphRoot = PluginFunctions::getSceneGraphRootNode(); // peel shader is rebuilt, if a new light is added or a light type changed LightType prevLights[8]; unsigned int prevIDs[8]; unsigned int prevNum = numLights_; memcpy(prevLights, lightTypes_, sizeof(LightType) * numLights_); memcpy(prevIDs, glLightIDs_, sizeof(unsigned int) * numLights_); numLights_ = 0; traverseLightNodes(sceneGraphRoot); // test for necessary shader recompilation if (numLights_ == prevNum) { // look for any light node changes if (memcmp(prevLights, lightTypes_, sizeof(LightType) * numLights_) || memcmp(prevIDs, glLightIDs_, sizeof(unsigned int) * numLights_)) rebuildShaders = true; } else rebuildShaders = true; if (rebuildShaders) { // delete old shaders and programs for (unsigned int i = 0; i < sizeof(peelShaders_) / sizeof(peelShaders_[0]); ++i) { delete peelShaders_[i]; peelShaders_[i] = 0; } for (unsigned int i = 0; i < sizeof(peelProgs_) / sizeof(peelProgs_[0]); ++i) { delete peelProgs_[i]; peelProgs_[i] = 0; } for (unsigned int i = 0; i < PEEL_NUM_COMBINATIONS; ++i) { GLuint texturedDrawMode = 0, flatDrawMode = 0, phongDrawMode = 0, vertexColorDrawMode = 0, gouraudDrawMode = 0; texturedDrawMode = i & PEEL_SHADER_TEXTURED; flatDrawMode = i & PEEL_SHADER_FLAT; phongDrawMode = i & PEEL_SHADER_PHONG; vertexColorDrawMode = i & PEEL_SHADER_VERTEXCOLORS; gouraudDrawMode = i & PEEL_SHADER_GOURAUD; if (i != PEEL_SHADER_WIREFRAME) { // filter nonsense if (flatDrawMode && phongDrawMode) continue; if (flatDrawMode && gouraudDrawMode) continue; if (phongDrawMode && gouraudDrawMode) continue; if (phongDrawMode + flatDrawMode + gouraudDrawMode == 0) continue; } if (flatDrawMode) { // replace flat shading with gouraud, if geometry shaders are not supported if (!ACG::checkExtensionSupported("GL_ARB_geometry_shader4") && !ACG::checkExtensionSupported("GL_EXT_geometry_shader4")) { flatDrawMode = 0; gouraudDrawMode = 1; } } GLSL::StringList strVertexShader, strFragmentShader, strGeometryShader; generatePeelingShaders(&strVertexShader, &strFragmentShader, &strGeometryShader, texturedDrawMode != 0, flatDrawMode != 0, phongDrawMode != 0, vertexColorDrawMode != 0, i == PEEL_SHADER_WIREFRAME); peelProgs_[i] = new GLSL::Program(); GLSL::VertexShader* pVertexSh = new GLSL::VertexShader(); pVertexSh->setSource(strVertexShader); pVertexSh->compile(); peelShaders_[i*3 + 0] = pVertexSh; peelProgs_[i]->attach(pVertexSh); if (flatDrawMode) { GLSL::GeometryShader* pGeomSh = new GLSL::GeometryShader(); pGeomSh->setSource(strGeometryShader); pGeomSh->compile(); peelShaders_[i*3 + 1] = pGeomSh; peelProgs_[i]->attach(pGeomSh); } GLSL::FragmentShader* pFragSh = new GLSL::FragmentShader(); pFragSh->setSource(strFragmentShader); pFragSh->compile(); peelShaders_[i*3 + 2] = pFragSh; peelProgs_[i]->attach(pFragSh); peelProgs_[i]->link(); ACG::glCheckErrors(); #ifdef DEPTHPEELING_SHADER_EXPORT char szFileName[256]; sprintf(szFileName, "peel_vertex_%02d.glsl", i); FILE* pShaderOut = fopen(szFileName, "wt"); for (GLSL::StringList::iterator it = strVertexShader.begin(); it != strVertexShader.end(); ++it) fprintf(pShaderOut, it->c_str()); fclose(pShaderOut); sprintf(szFileName, "peel_frag%02d.glsl", i); pShaderOut = fopen(szFileName, "wt"); for (GLSL::StringList::iterator it = strFragmentShader.begin(); it != strFragmentShader.end(); ++it) fprintf(pShaderOut, it->c_str()); fclose(pShaderOut); if (flatDrawMode) { sprintf(szFileName, "peel_geom%02d.glsl", i); pShaderOut = fopen(szFileName, "wt"); for (GLSL::StringList::iterator it = strGeometryShader.begin(); it != strGeometryShader.end(); ++it) fprintf(pShaderOut, it->c_str()); fclose(pShaderOut); } #endif } } // create a special shader for hiddenline // hiddenline = wireframe + early z cull // the special shader is needed for the z buffer pass const char* szVertexShader[] = { "void main(void)", "{", " gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;", "}"}; const char* szFragmentShader[] = { "uniform vec4 ObjectColor;", "uniform sampler2DRect DepthBlenderTex;", "uniform sampler2DRect FrontBlenderTex;", "", "#define MAX_DEPTH 1.0", "", "", "void main(void)", "{", " // window-space depth interpolated linearly in screen space", " float fragDepth = gl_FragCoord.z;", "", " vec2 depthBlender = texture2DRect(DepthBlenderTex, gl_FragCoord.xy).xy;", " vec4 forwardTemp = texture2DRect(FrontBlenderTex, gl_FragCoord.xy);", " ", " // Depths and 1.0-alphaMult always increase", " // so we can use pass-through by default with MAX blending", " gl_FragData[0].xy = depthBlender;", " ", " // Front colors always increase (DST += SRC*ALPHA_MULT)", " // so we can use pass-through by default with MAX blending", " gl_FragData[1] = forwardTemp;", " ", " // Because over blending makes color increase or decrease,", " // we cannot pass-through by default.", " // Each pass, only one fragment writes a color greater than 0", " gl_FragData[2] = vec4(0.0);", "", " float nearestDepth = -depthBlender.x;", " float farthestDepth = depthBlender.y;", " float alphaMultiplier = 1.0 - forwardTemp.w;", "", " if (fragDepth < nearestDepth || fragDepth > farthestDepth) {", " // Skip this depth in the peeling algorithm", " gl_FragData[0].xy = vec2(-MAX_DEPTH);", " return;", " }", " ", " if (fragDepth > nearestDepth && fragDepth < farthestDepth) {", " // This fragment needs to be peeled again", " gl_FragData[0].xy = vec2(-fragDepth, fragDepth);", " return;", " }", " ", " // If we made it here, this fragment is on the peeled layer from last pass", " // therefore, we need to shade it, and make sure it is not peeled any farther", " vec4 color = ObjectColor;", " gl_FragData[0].xy = vec2(-MAX_DEPTH);", " ", " if (fragDepth == nearestDepth) {", " gl_FragData[1].xyz += color.rgb * color.a * alphaMultiplier;", " gl_FragData[1].w = 1.0 - alphaMultiplier * (1.0 - color.a);", " } else {", " gl_FragData[2] += color;", " }", "}"}; GLSL::StringList strVertexShader, strFragmentShader; for (unsigned int i = 0; i < sizeof(szVertexShader) / sizeof(char*); ++i) { std::string str = szVertexShader[i]; str += "\n"; strVertexShader.push_back(str); } for (unsigned int i = 0; i < sizeof(szFragmentShader) / sizeof(char*); ++i) { std::string str = szFragmentShader[i]; str += "\n"; strFragmentShader.push_back(str); } GLSL::Program* peelHiddenLine = peelProgs_[PEEL_SHADER_HIDDENLINE] = new GLSL::Program(); GLSL::VertexShader* pVertexSh = new GLSL::VertexShader(); pVertexSh->setSource(strVertexShader); pVertexSh->compile(); peelShaders_[PEEL_SHADER_HIDDENLINE*3 + 0] = pVertexSh; peelHiddenLine->attach(pVertexSh); GLSL::FragmentShader* pFragSh = new GLSL::FragmentShader(); pFragSh->setSource(strFragmentShader); pFragSh->compile(); peelShaders_[PEEL_SHADER_HIDDENLINE*3 + 2] = pFragSh; peelHiddenLine->attach(pFragSh); peelHiddenLine->link(); ACG::glCheckErrors(); } ////////////////////////////////////////////////////////////////////////// unsigned int DepthPeelingPlugin::getPeelShaderIndex(ACG::SceneGraph::DrawModes::DrawMode _drawMode) { if (_drawMode & ACG::SceneGraph::DrawModes::WIREFRAME || _drawMode & ACG::SceneGraph::DrawModes::HIDDENLINE) return PEEL_SHADER_WIREFRAME; bool textured = _drawMode & ACG::SceneGraph::DrawModes::SOLID_TEXTURED || _drawMode & ACG::SceneGraph::DrawModes::SOLID_TEXTURED_SHADED; bool flat = _drawMode & ACG::SceneGraph::DrawModes::SOLID_FLAT_SHADED; bool phong = _drawMode & ACG::SceneGraph::DrawModes::SOLID_PHONG_SHADED; bool vertexColor = _drawMode & ACG::SceneGraph::DrawModes::SOLID_FACES_COLORED || _drawMode & ACG::SceneGraph::DrawModes::SOLID_FACES_COLORED_FLAT_SHADED || _drawMode & ACG::SceneGraph::DrawModes::SOLID_POINTS_COLORED || _drawMode & ACG::SceneGraph::DrawModes::POINTS_COLORED || _drawMode & ACG::SceneGraph::DrawModes::SOLID_FACES_COLORED_SMOOTH_SHADED; bool gouraud = _drawMode & ACG::SceneGraph::DrawModes::SOLID_SMOOTH_SHADED; // fix illegal combinations if (phong && flat) flat = false; if (flat && gouraud) gouraud = false; if (gouraud && phong) phong = false; // wireframe, point, etc use gouraud shading if ((!phong) && (!gouraud) && (!flat)) gouraud = true; unsigned int idx = 0; if (flat) idx |= PEEL_SHADER_FLAT; if (gouraud) idx |= PEEL_SHADER_GOURAUD; if (phong) idx |= PEEL_SHADER_PHONG; if (vertexColor) idx |= PEEL_SHADER_VERTEXCOLORS; if (textured) idx |= PEEL_SHADER_TEXTURED; return idx; } ////////////////////////////////////////////////////////////////////////// void DepthPeelingPlugin::render(ACG::GLState* _glState, Viewer::ViewerProperties& _properties) { glStateTmp_ = _glState; 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 (pViewer->glWidth_ != pViewer->rtWidth_ || pViewer->glHeight_ != pViewer->rtHeight_) reloadResources(viewerId); // updatePeelingShaderSet(viewerId, _properties.drawMode()); updatePeelingShaderSet(); BaseNode* sceneGraphRoot = PluginFunctions::getSceneGraphRootNode(); ACG::GLState::depthFunc(GL_LESS); ACG::GLState::disable(GL_CULL_FACE); ACG::GLState::disable(GL_LIGHTING); ACG::GLState::disable(GL_NORMALIZE); ACG::GLState::lockDepthFunc(); ACG::GLState::lockState(GL_CULL_FACE); ACG::GLState::lockState(GL_LIGHTING); ACG::GLState::lockState(GL_NORMALIZE); // from nvidia demo code: // needed some minor adjustments 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 = 1.0f; ACG::GLState::disable(GL_DEPTH_TEST); ACG::GLState::enable(GL_BLEND); ACG::GLState::lockState(GL_DEPTH_TEST); ACG::GLState::lockState(GL_BLEND); /* FIXED: VIEWPORT BUG log-window causes viewport shift by 16 units upward in window's y axis therefore the scene gets rendered only in the upper part of the viewport: -> glViewport(0, 16, glWidht, glHeight_) glHeight_() is NOT the height of the back buffer (i.e. glViewer window), but the height of scene target view -> glHeight_() is 16 units less than the back buffer -> all render targets are 16 units less in height than back buffer since the scene has to use the full render targets size, use glViewport(0, 0, glWidth_, glHeight_) for all offscreen rendering in the final pass, shift the viewport up by 16 units and use the shift amount in the shader (uniform g_Offset), to finally get the correct sampling coordinates note: shift amount is not hardcoded, but fetched from glGetIntegerv(GL_VIEWPORT) */ GLint old_viewport[4]; glGetIntegerv(GL_VIEWPORT, old_viewport); glViewport(0, 0, pViewer->glWidth_, pViewer->glHeight_); for (int i = 0; i < 6; ++i) { ACG::GLState::activeTexture(GL_TEXTURE0 + i); ACG::GLState::bindTexture(GL_TEXTURE_RECTANGLE_EXT, 0); } // --------------------------------------------------------------------- // 1. Initialize Min-Max Depth Buffer // --------------------------------------------------------------------- ACG::GLState::bindFramebuffer(GL_FRAMEBUFFER_EXT, pViewer->blendDualPeelFbo_); // Render targets 1 and 2 store the front and back colors // Clear to 0.0 and use MAX blending to filter written color // At most one front color and one back color can be written every pass ACG::GLState::drawBuffers(2, &drawBuffers[1]); glClearColor(0, 0, 0, 0); glClear(GL_COLOR_BUFFER_BIT); // Render target 0 stores (-minDepth, maxDepth, alphaMultiplier) ACG::GLState::drawBuffer(drawBuffers[0]); glClearColor(-maxDepth, -maxDepth, 0, 0); glClear(GL_COLOR_BUFFER_BIT); ACG::GLState::blendEquation(GL_MAX_EXT); ACG::GLState::lockBlendEquation(); blendDualPeelProg_[0]->use(); ACG::GLState::lockProgram(); drawScenePass(_glState, _properties.drawMode(), sceneGraphRoot); ACG::GLState::unlockProgram(); int currId = 0; // --------------------------------------------------------------------- // 2. Dual Depth Peeling + Blending // --------------------------------------------------------------------- // Since we cannot blend the back colors in the geometry passes, // we use another render target to do the alpha blending //glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, g_dualBackBlenderFboId); ACG::GLState::drawBuffer(drawBuffers[6]); glClearColor(_glState->clear_color()[0], _glState->clear_color()[1], _glState->clear_color()[2], 0); glClear(GL_COLOR_BUFFER_BIT); // Geometry layers are peeled until the sample query returns 0 GLuint sampleCount = 1; for (int pass = 1; sampleCount; ++pass) { currId = pass % 2; int prevId = 1 - currId; int bufId = currId * 3; ACG::GLState::drawBuffers(2, &drawBuffers[bufId+1]); glClearColor(0, 0, 0, 0); glClear(GL_COLOR_BUFFER_BIT); ACG::GLState::drawBuffer(drawBuffers[bufId+0]); glClearColor(-maxDepth, -maxDepth, 0, 0); glClear(GL_COLOR_BUFFER_BIT); // Render target 0: RG32F MAX blending // Render target 1: RGBA MAX blending // Render target 2: RGBA MAX blending ACG::GLState::drawBuffers(3, &drawBuffers[bufId+0]); ACG::GLState::unlockBlendEquation(); ACG::GLState::blendEquation(GL_MAX_EXT); ACG::GLState::lockBlendEquation(); ACG::GLState::activeTexture(GL_TEXTURE5); // front_blender_tex base_offset: 2 ACG::GLState::bindTexture(GL_TEXTURE_RECTANGLE_EXT, pViewer->blendDualPeelTexID_[2 + prevId]); ACG::GLState::activeTexture(GL_TEXTURE4); // depth_tex base_offset: 0 ACG::GLState::bindTexture(GL_TEXTURE_RECTANGLE_EXT, pViewer->blendDualPeelTexID_[0 + prevId]); // scene geometry peeling pass // note that the peel shader is set right before rendering in the traverser, based on a node's drawmode ACG::GLState::activeTexture(GL_TEXTURE0); ACG::GLState::shadeModel(GL_SMOOTH); // flat shading is emulated in Geometry Shader, which only works with interpolated vertex shader output ACG::GLState::lockShadeModel(); drawScenePeelPass(_glState, _properties.drawMode(), sceneGraphRoot, pass); ACG::GLState::unlockShadeModel(); // Full screen pass to alpha-blend the back color ACG::GLState::drawBuffer(drawBuffers[6]); ACG::GLState::unlockBlendEquation(); ACG::GLState::blendEquation(GL_FUNC_ADD); ACG::GLState::lockBlendEquation(); ACG::GLState::blendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // start samples counter query glBeginQuery(GL_SAMPLES_PASSED_ARB, blendQueryID_); blendDualPeelProg_[2]->use(); blendDualPeelProg_[2]->setUniform("TempTex", 4); ACG::GLState::activeTexture(GL_TEXTURE4); // back_temp_tex base_offset: 4 ACG::GLState::bindTexture(GL_TEXTURE_RECTANGLE_EXT, pViewer->blendDualPeelTexID_[4 + currId]); drawQuadProj(); // full screen quad, already projected glEndQuery(GL_SAMPLES_PASSED_ARB); glGetQueryObjectuiv(blendQueryID_, GL_QUERY_RESULT_ARB, &sampleCount); } ACG::GLState::unlockBlendEquation(); ACG::GLState::unlockState(GL_BLEND); ACG::GLState::disable(GL_BLEND); // --------------------------------------------------------------------- // 3. Final Pass // operates on screen pixels only // --------------------------------------------------------------------- // enable back buffer ACG::GLState::bindFramebuffer(GL_FRAMEBUFFER_EXT, targetFbo); ACG::GLState::drawBuffer(GL_BACK); // program id 3 blendDualPeelProg_[3]->use(); blendDualPeelProg_[3]->setUniform("FrontBlenderTex", 4); blendDualPeelProg_[3]->setUniform("BackBlenderTex", 5); // bugfix for multiple viewports: // gl_FragCoord represents the screen space coordinate of a pixel into the back buffer // this has to be back-shifted by the upper left viewport coordinate to get correct texture coordinates blendDualPeelProg_[3]->setUniform("ViewportOffset", ACG::Vec2f(old_viewport[0], old_viewport[1])); ACG::GLState::activeTexture(GL_TEXTURE5); // back_blender: offset 6 ACG::GLState::bindTexture(GL_TEXTURE_RECTANGLE_EXT, pViewer->blendDualPeelTexID_[6]); ACG::GLState::activeTexture(GL_TEXTURE4); // front_blender_tex base_offset: 2 ACG::GLState::bindTexture(GL_TEXTURE_RECTANGLE_EXT, pViewer->blendDualPeelTexID_[2 + currId]); glViewport(old_viewport[0], old_viewport[1], old_viewport[2], old_viewport[3]); drawQuadProj(-1.0f, 1.0f, 2.0f, 2.0f); blendDualPeelProg_[3]->disable(); ACG::glCheckErrors(); // unlock states ACG::GLState::unlockDepthFunc(); ACG::GLState::unlockState(GL_CULL_FACE); ACG::GLState::unlockState(GL_LIGHTING); ACG::GLState::unlockState(GL_NORMALIZE); ACG::GLState::unlockState(GL_DEPTH_TEST); ACG::GLState::unlockState(GL_BLEND); glPopAttrib(); } Q_EXPORT_PLUGIN2( depthpeelingplugin , DepthPeelingPlugin );