From ceb97fe230905c581785ba7667dca6579250f795 Mon Sep 17 00:00:00 2001 From: Robert Osfield Date: Tue, 25 Jun 2013 11:13:50 +0000 Subject: [PATCH] =?UTF-8?q?From=20Christian=20Buchner,=20"Here=20is=20a=20?= =?UTF-8?q?strongly=20overhauled=20version=20of=20the=20original=20osgoit?= =?UTF-8?q?=20("order=20independent=20transparency")=20by=20Mathias=20Fr?= =?UTF-8?q?=C3=B6hlich.=20I=20called=20this=20version=20myosgoit.=20It=20l?= =?UTF-8?q?ooks=20very=20nice,=20just=20build=20and=20run=20it!?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This version adds: - an encapsulation of the entire Depth Peeling procedure into a class (not currently a scene graph node) for easier integration in other projects. - compositing with opaque (solid) geometry is possible and the opaque model is only rendered once. This needs to performs some depth buffer blitting between FBOs. - mix and match with GLSL shaders in the transparent objects is possible, as demonstrated with a 3D heat map intersecting an opaque truck model. Some Drawbacks: - the display framebuffer does not receive any depth information from the compositing camera. This could be fixed by compositing with a GLSL shader and writing to FragDepth." From Robert Osfield, ported the code to work under Linux and without the automatic ref_ptr to C* conversion. --- examples/osgoit/CMakeLists.txt | 8 +- examples/osgoit/DepthPeeling.cpp | 574 +++++++++++++++++++++++++++++++ examples/osgoit/DepthPeeling.h | 102 ++++++ examples/osgoit/HeatMap.cpp | 232 +++++++++++++ examples/osgoit/HeatMap.h | 33 ++ examples/osgoit/osgoit.cpp | 545 +++++------------------------ 6 files changed, 1032 insertions(+), 462 deletions(-) create mode 100644 examples/osgoit/DepthPeeling.cpp create mode 100644 examples/osgoit/DepthPeeling.h create mode 100644 examples/osgoit/HeatMap.cpp create mode 100644 examples/osgoit/HeatMap.h diff --git a/examples/osgoit/CMakeLists.txt b/examples/osgoit/CMakeLists.txt index 40e84f52a..49089945e 100644 --- a/examples/osgoit/CMakeLists.txt +++ b/examples/osgoit/CMakeLists.txt @@ -1,2 +1,8 @@ -SET(TARGET_SRC osgoit.cpp) +SET(TARGET_SRC + osgoit.cpp + DepthPeeling.h + DepthPeeling.cpp + HeatMap.h + HeatMap.cpp) + SETUP_EXAMPLE(osgoit) diff --git a/examples/osgoit/DepthPeeling.cpp b/examples/osgoit/DepthPeeling.cpp new file mode 100644 index 000000000..285996f94 --- /dev/null +++ b/examples/osgoit/DepthPeeling.cpp @@ -0,0 +1,574 @@ + +#include "DepthPeeling.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include +#include + +const char *DepthPeeling::PeelingShader = +{ + "#version 120\n" +#ifdef USE_TEXTURE_RECTANGLE + "#extension GL_ARB_texture_rectangle : enable\n" + "uniform sampler2DRectShadow depthtex;\n" +#else + "uniform sampler2DShadow depthtex;\n" +#endif + "uniform bool depthtest;\n" // depth test enable flag + "uniform float invWidth;\n" // 1.0/width (shadow texture size) + "uniform float invHeight;\n" // 1.0/height (shadow texture size) + "uniform float offsetX;\n" // viewport lower left corner (int) + "uniform float offsetY;\n" // viewport lower left corner (int) + "\n" + "bool depthpeeling()\n" + "{\n" + " if( depthtest ) {\n" + " vec3 r0 = vec3((gl_FragCoord.x-offsetX)*invWidth,\n" + " (gl_FragCoord.y-offsetY)*invHeight,\n" + " gl_FragCoord.z);\n" +#ifdef USE_TEXTURE_RECTANGLE + " return shadow2DRect(depthtex, r0).r < 0.5;\n" +#else + " return shadow2D(depthtex, r0).r < 0.5;\n" +#endif + " }\n" + " return false;\n" + "}\n" +}; + +class PreDrawFBOCallback : public osg::Camera::DrawCallback +{ +public: + PreDrawFBOCallback( osg::FrameBufferObject* fbo, osg::FrameBufferObject* source_fbo, unsigned int width, unsigned int height, osg::Texture *dt, osg::Texture *ct ) : + _fbo(fbo), _source_fbo(source_fbo), _depthTexture(dt), _colorTexture(ct), _width(width), _height(height) {} + + virtual void operator () (osg::RenderInfo& renderInfo) const + { + // switching only the frame buffer attachments is actually faster than switching the framebuffer +#ifdef USE_PACKED_DEPTH_STENCIL +#ifdef USE_TEXTURE_RECTANGLE + _fbo->setAttachment(osg::Camera::PACKED_DEPTH_STENCIL_BUFFER, osg::FrameBufferAttachment((osg::TextureRectangle*)(_depthTexture.get()))); +#else + _fbo->setAttachment(osg::Camera::PACKED_DEPTH_STENCIL_BUFFER, osg::FrameBufferAttachment((osg::Texture2D*)(_depthTexture.get()))); +#endif +#else +#ifdef USE_TEXTURE_RECTANGLE + _fbo->setAttachment(osg::Camera::DEPTH_BUFFER, osg::FrameBufferAttachment((osg::TextureRectangle*)(_depthTexture.get()))); +#else + _fbo->setAttachment(osg::Camera::DEPTH_BUFFER, osg::FrameBufferAttachment((osg::Texture2D*)(_depthTexture.get()))); +#endif +#endif +#ifdef USE_TEXTURE_RECTANGLE + _fbo->setAttachment(osg::Camera::COLOR_BUFFER0, osg::FrameBufferAttachment((osg::TextureRectangle*)(_colorTexture.get()))); +#else + _fbo->setAttachment(osg::Camera::COLOR_BUFFER0, osg::FrameBufferAttachment((osg::Texture2D*)(_colorTexture.get()))); +#endif + + // check if we need to do some depth buffer copying from a source FBO into the current FBO + if (_source_fbo.get() != NULL) + { + osg::FBOExtensions* fbo_ext = osg::FBOExtensions::instance(renderInfo.getContextID(),true); + bool fbo_supported = fbo_ext && fbo_ext->isSupported(); + if (fbo_supported && fbo_ext->glBlitFramebuffer) + { + // blit the depth buffer from the solid geometry fbo into the current transparency fbo + (_fbo.get())->apply(*renderInfo.getState(), osg::FrameBufferObject::DRAW_FRAMEBUFFER); + (_source_fbo.get())->apply(*renderInfo.getState(), osg::FrameBufferObject::READ_FRAMEBUFFER); + +// glReadBuffer(GL_COLOR_ATTACHMENT0_EXT); // only needed to blit the color buffer +// glDrawBuffer(GL_COLOR_ATTACHMENT0_EXT); // only needed to blit the color buffer + fbo_ext->glBlitFramebuffer( + 0, 0, static_cast(_width), static_cast(_height), + 0, 0, static_cast(_width), static_cast(_height), +#ifdef USE_PACKED_DEPTH_STENCIL + GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT, GL_NEAREST); +#else + GL_DEPTH_BUFFER_BIT, GL_NEAREST); +#endif + (_fbo.get())->apply(*renderInfo.getState(), osg::FrameBufferObject::READ_FRAMEBUFFER); + (_fbo.get())->apply(*renderInfo.getState(), osg::FrameBufferObject::DRAW_FRAMEBUFFER); + } + } + // switch to this fbo, if it isn't already bound + (_fbo.get())->apply( *renderInfo.getState() ); + } +protected: + osg::ref_ptr _fbo; + osg::ref_ptr _source_fbo; + osg::ref_ptr _depthTexture; + osg::ref_ptr _colorTexture; + unsigned int _width; + unsigned int _height; +}; + + +class PostDrawFBOCallback : public osg::Camera::DrawCallback +{ +public: + PostDrawFBOCallback(bool restore) : _restore(restore) {} + + virtual void operator () (osg::RenderInfo& renderInfo) const + { + // only unbind the fbo if this is the last transparency pass + if (_restore) + osg::FBOExtensions::instance( renderInfo.getState()->getContextID(), false )->glBindFramebuffer( GL_FRAMEBUFFER_EXT, 0 ); + } +protected: + bool _restore; +}; + + +DepthPeeling::CullCallback::CullCallback(unsigned int texUnit, unsigned int texWidth, unsigned int texHeight, unsigned int offsetValue) : + _texUnit(texUnit), + _texWidth(texWidth), + _texHeight(texHeight), + _offsetValue(offsetValue) +{ +} + +void DepthPeeling::CullCallback::operator()(osg::Node* node, osg::NodeVisitor* nv) +{ + osgUtil::CullVisitor* cullVisitor = static_cast(nv); + osgUtil::RenderStage* renderStage = cullVisitor->getCurrentRenderStage(); + const osg::Viewport* viewport = renderStage->getViewport(); + + osg::Matrixd m(*cullVisitor->getProjectionMatrix()); + m.postMultTranslate(osg::Vec3d(1, 1, 1)); + m.postMultScale(osg::Vec3d(0.5, 0.5, 0.5)); + + // scale the texture coordinates to the viewport +#ifdef USE_TEXTURE_RECTANGLE + m.postMultScale(osg::Vec3d(viewport->width(), viewport->height(), 1)); +#else +#ifndef USE_NON_POWER_OF_TWO_TEXTURE + m.postMultScale(osg::Vec3d(viewport->width()/double(_texWidth), viewport->height()/double(_texHeight), 1)); +#endif +#endif + + if (_texUnit != 0 && _offsetValue) + { + // Kind of polygon offset: note this way, we can also offset lines and points. + // Whereas with the polygon offset we could only handle surface primitives. + m.postMultTranslate(osg::Vec3d(0, 0, -ldexp(double(_offsetValue), -24))); + } + + osg::TexMat* texMat = new osg::TexMat(m); + osg::StateSet* stateSet = new osg::StateSet; + stateSet->setTextureAttribute(_texUnit, texMat); + + if (_texUnit != 0) + { + // + // GLSL pipeline support + // + +#ifdef USE_TEXTURE_RECTANGLE + // osg::Uniform::SAMPLER_2D_RECT_SHADOW not yet available in OSG 3.0.1 + // osg::Uniform* depthUniform = new osg::Uniform(osg::Uniform::SAMPLER_2D_RECT_SHADOW, "depthtex"); + // depthUniform->set((int)_texUnit); + osg::Uniform* depthUniform = new osg::Uniform("depthtex", (int)_texUnit); + osg::Uniform *invWidthUniform = new osg::Uniform("invWidth", (float)1.0f); + osg::Uniform *invHeightUniform = new osg::Uniform("invHeight", (float)1.0f); +#else + osg::Uniform* depthUniform = new osg::Uniform(osg::Uniform::SAMPLER_2D_SHADOW, "depthtex"); + depthUniform->set((int)_texUnit); + osg::Uniform *invWidthUniform = new osg::Uniform("invWidth", (float)1.0f / _texWidth); + osg::Uniform *invHeightUniform = new osg::Uniform("invHeight", (float)1.0f / _texHeight); +#endif + osg::Uniform *offsetXUniform = new osg::Uniform("offsetX", (float)viewport->x()); + osg::Uniform *offsetYUniform = new osg::Uniform("offsetY", (float)viewport->y()); + + // uniforms required for any any GLSL implementation in the rendered geometry + stateSet->addUniform(depthUniform); + stateSet->addUniform(invWidthUniform); + stateSet->addUniform(invHeightUniform); + stateSet->addUniform(offsetXUniform); + stateSet->addUniform(offsetYUniform); + } + + cullVisitor->pushStateSet(stateSet); + traverse(node, nv); + cullVisitor->popStateSet(); +} + + +osg::Node* DepthPeeling::createQuad(unsigned int layerNumber, unsigned int numTiles) +{ + float tileSpan = 1; + float tileOffsetX = 0; + float tileOffsetY = 0; + if (_showAllLayers) { + tileSpan /= numTiles; + tileOffsetX = tileSpan * (layerNumber%numTiles); + tileOffsetY = 1 - tileSpan * (1 + layerNumber/numTiles); + } + + osg::Vec3Array* vertices = new osg::Vec3Array; + + vertices->push_back(osg::Vec3f(tileOffsetX , tileOffsetY , 0)); + vertices->push_back(osg::Vec3f(tileOffsetX , tileOffsetY + tileSpan, 0)); + vertices->push_back(osg::Vec3f(tileOffsetX + tileSpan, tileOffsetY + tileSpan, 0)); + vertices->push_back(osg::Vec3f(tileOffsetX + tileSpan, tileOffsetY , 0)); + + osg::Vec3Array* colors = new osg::Vec3Array; + colors->push_back(osg::Vec3(1, 1, 1)); + + osg::Vec2Array* texcoords = new osg::Vec2Array; + texcoords->push_back(osg::Vec2f(0, 0)); + texcoords->push_back(osg::Vec2f(0, 1)); + texcoords->push_back(osg::Vec2f(1, 1)); + texcoords->push_back(osg::Vec2f(1, 0)); + + osg::Geometry* geometry = new osg::Geometry; + geometry->setVertexArray(vertices); + geometry->setTexCoordArray(0, texcoords); + + geometry->setColorArray(colors); + geometry->setColorBinding(osg::Geometry::BIND_OVERALL); + + geometry->addPrimitiveSet(new osg::DrawArrays(GL_QUADS, 0, 4)); + + osg::Geode* geode = new osg::Geode; + geode->addDrawable(geometry); + + return geode; +} + +#include + +void DepthPeeling::createPeeling() +{ + int numTiles = ceil(sqrt(double(_numPasses))); + + // cleanup any previous scene data + _root->removeChildren(0, _root->getNumChildren()); + + // create depth textures + _depthTextures.clear(); + _depthTextures.resize(3); + for (unsigned int i = 0; i < 3; ++i) { +#ifdef USE_TEXTURE_RECTANGLE + _depthTextures[i] = new osg::TextureRectangle; +#else + _depthTextures[i] = new osg::Texture2D; +#endif + _depthTextures[i]->setTextureSize(_texWidth, _texHeight); + + _depthTextures[i]->setFilter(osg::Texture::MIN_FILTER, osg::Texture::NEAREST); + _depthTextures[i]->setFilter(osg::Texture::MAG_FILTER, osg::Texture::NEAREST); + _depthTextures[i]->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_BORDER); + _depthTextures[i]->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_BORDER); + +#ifdef USE_PACKED_DEPTH_STENCIL + _depthTextures[i]->setInternalFormat(GL_DEPTH24_STENCIL8_EXT); + _depthTextures[i]->setSourceFormat(GL_DEPTH_STENCIL_EXT); + _depthTextures[i]->setSourceType(GL_UNSIGNED_INT_24_8_EXT); +#else + _depthTextures[i]->setInternalFormat(GL_DEPTH_COMPONENT); +#endif + + _depthTextures[i]->setShadowComparison(true); + _depthTextures[i]->setShadowAmbient(0.0); // The r value if the test fails + _depthTextures[i]->setShadowCompareFunc(osg::Texture::GREATER); + _depthTextures[i]->setShadowTextureMode(osg::Texture::INTENSITY); + } + + // create the cameras for the individual depth peel layers + _colorTextures.clear(); + _colorTextures.resize(_numPasses); + for (unsigned int i = 0; i < _numPasses; ++i) { + + // create textures for the color buffers +#ifdef USE_TEXTURE_RECTANGLE + osg::ref_ptr colorTexture = new osg::TextureRectangle; +#else + osg::ref_ptr colorTexture = new osg::Texture2D; +#endif + colorTexture->setTextureSize(_texWidth, _texHeight); + colorTexture->setFilter(osg::Texture::MIN_FILTER, osg::Texture::NEAREST); + colorTexture->setFilter(osg::Texture::MAG_FILTER, osg::Texture::NEAREST); + colorTexture->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_BORDER); + colorTexture->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_BORDER); + colorTexture->setInternalFormat(GL_RGBA); + + _colorTextures[i] = colorTexture; + } + + // create some uniform and cull callback objects + osg::Uniform *depthOff = new osg::Uniform("depthtest", (bool)false); + osg::Uniform *depthOn = new osg::Uniform("depthtest", (bool)true); + CullCallback *ccb = new CullCallback(_texUnit, _texWidth, _texHeight, _offsetValue); + + // create a node for solid model rendering + osg::Group *pre_solidNode = new osg::Group; + pre_solidNode->addChild(_solidscene.get()); + + // create a node for non depth peeled transparent rendering (topmost layer) + osg::Group *transparentNodeNoPeel = new osg::Group; + transparentNodeNoPeel->addChild(_transparentscene.get()); + transparentNodeNoPeel->getOrCreateStateSet()->addUniform(depthOff); + transparentNodeNoPeel->getOrCreateStateSet()->setRenderBinDetails(99, "RenderBin", osg::StateSet::OVERRIDE_RENDERBIN_DETAILS); + + // create a node for depth peeled transparent rendering (any layers below). + osg::TexGenNode* transparentNodePeel = new osg::TexGenNode; + transparentNodePeel->setReferenceFrame(osg::TexGenNode::ABSOLUTE_RF); + transparentNodePeel->setTextureUnit(_texUnit); + transparentNodePeel->getTexGen()->setMode(osg::TexGen::EYE_LINEAR); + transparentNodePeel->addChild(_transparentscene.get()); + transparentNodePeel->getOrCreateStateSet()->addUniform(depthOn); + transparentNodePeel->getOrCreateStateSet()->setRenderBinDetails(99, "RenderBin", osg::StateSet::OVERRIDE_RENDERBIN_DETAILS); + + // only render fragments that are not completely transparent + transparentNodePeel->getOrCreateStateSet()->setAttributeAndModes(new osg::AlphaFunc(osg::AlphaFunc::GREATER, 0.01), + osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE); + + // generate texcoords for the depth texture, supporting the fixed function pipeline + transparentNodePeel->getOrCreateStateSet()->setTextureMode(_texUnit, GL_TEXTURE_GEN_S, osg::StateAttribute::ON); + transparentNodePeel->getOrCreateStateSet()->setTextureMode(_texUnit, GL_TEXTURE_GEN_T, osg::StateAttribute::ON); + transparentNodePeel->getOrCreateStateSet()->setTextureMode(_texUnit, GL_TEXTURE_GEN_R, osg::StateAttribute::ON); + transparentNodePeel->getOrCreateStateSet()->setTextureMode(_texUnit, GL_TEXTURE_GEN_Q, osg::StateAttribute::ON); + + // use two FBOs, one for solid geometry - the other one for the transparency passes + // depth and color attachments will be switched as needed. + osg::ref_ptr fbos[2] = {new osg::FrameBufferObject(), new osg::FrameBufferObject()}; + + // create the cameras for the individual depth peel layers + for (unsigned int i = 0; i < _numPasses; ++i) { + + // get the pointers to the required fbo, color and depth textures for each camera instance + // we perform ping ponging between two depth textures + osg::FrameBufferObject *fbo0 = (i >= 1) ? fbos[0].get() : NULL; + osg::FrameBufferObject *fbo = (i >= 1) ? fbos[1].get() : fbos[0].get(); + osg::Texture *colorTexture = _colorTextures[i].get(); + osg::Texture *depthTexture = (i >= 1) ? _depthTextures[1+(i-1)%2].get() : _depthTextures[i].get(); + osg::Texture *prevDepthTexture = (i >= 2) ? _depthTextures[1+(i-2)%2].get() : NULL; + + // all our peeling layer cameras are post render + osg::Camera* camera = new osg::Camera; + camera->setDataVariance(osg::Object::DYNAMIC); + camera->setInheritanceMask(osg::Camera::ALL_VARIABLES); + camera->setRenderOrder(osg::Camera::POST_RENDER, i); + camera->setClearMask(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT); + camera->setClearColor(osg::Vec4f(0, 0, 0, 0)); + camera->setComputeNearFarMode(osg::Camera::DO_NOT_COMPUTE_NEAR_FAR); + camera->setPreDrawCallback(new PreDrawFBOCallback(fbo, fbo0, _texWidth, _texHeight, depthTexture, colorTexture)); + camera->setPostDrawCallback(new PostDrawFBOCallback(i == _numPasses - 1)); + camera->setDrawBuffer(GL_COLOR_ATTACHMENT0_EXT); + camera->setReadBuffer(GL_COLOR_ATTACHMENT0_EXT); + camera->setAllowEventFocus(false); + + // the peeled layers are rendered with blending forced off + // and the depth buffer is directly taken from camera 0 via framebuffer blit + if (i > 0) { + camera->getOrCreateStateSet()->setMode(GL_BLEND, osg::StateAttribute::OFF | osg::StateAttribute::OVERRIDE); + camera->setClearMask(GL_COLOR_BUFFER_BIT); + } else { + // camera 0 has to clear both the depth and color buffers + camera->setClearMask(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT); + } + + // add the correct geometry for each pass. + // the peeling passes also need read access to prevDepthTexture and a cull callback + if (0 == i) { // solid geometry + camera->addChild(pre_solidNode); + } else if (1 == i) { // topmost layer peeling pass + camera->addChild(transparentNodeNoPeel); + } else { // behind layers peeling passes + camera->addChild(transparentNodePeel); + // set depth (shadow) texture for depth peeling and add a cull callback + camera->getOrCreateStateSet()->setTextureAttributeAndModes(_texUnit, prevDepthTexture); + camera->addCullCallback(ccb); + } + _root->addChild(camera); + } + + // create the composite camera that blends the peeled layers into the final scene + _compositeCamera = new osg::Camera; + _compositeCamera->setDataVariance(osg::Object::DYNAMIC); + _compositeCamera->setInheritanceMask(osg::Camera::READ_BUFFER | osg::Camera::DRAW_BUFFER); + _compositeCamera->setRenderOrder(osg::Camera::POST_RENDER, _numPasses); + _compositeCamera->setComputeNearFarMode(osg::Camera::COMPUTE_NEAR_FAR_USING_PRIMITIVES); + _compositeCamera->setClearMask(0); + _compositeCamera->setReferenceFrame(osg::Transform::ABSOLUTE_RF); + _compositeCamera->setViewMatrix(osg::Matrix()); + _compositeCamera->setProjectionMatrix(osg::Matrix::ortho2D(0, 1, 0, 1)); + _compositeCamera->setCullCallback(new CullCallback(0, _texWidth, _texHeight, 0)); + osg::StateSet* stateSet = _compositeCamera->getOrCreateStateSet(); + stateSet->setRenderBinDetails(100, "TraversalOrderBin", osg::StateSet::OVERRIDE_RENDERBIN_DETAILS); + _root->addChild(_compositeCamera.get()); + + // solid geometry is blended first, transparency layers are blended in back to front order. + // this order is achieved by rendering using a TraversalOrderBin (see camera stateset). + for (unsigned int i = _numPasses; i > 0; --i) { + osg::Node* geode = createQuad(i%_numPasses, numTiles); + osg::StateSet *stateSet = geode->getOrCreateStateSet(); + stateSet->setTextureAttributeAndModes(0, _colorTextures[i%_numPasses].get(), osg::StateAttribute::ON); + stateSet->setAttribute(new osg::BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA), osg::StateAttribute::ON); + stateSet->setMode(GL_BLEND, osg::StateAttribute::ON); + stateSet->setMode(GL_LIGHTING, osg::StateAttribute::OFF); + osg::Depth* depth = new osg::Depth; + depth->setWriteMask( false ); + stateSet->setAttributeAndModes( depth, osg::StateAttribute::ON ); + stateSet->setMode(GL_DEPTH_TEST, osg::StateAttribute::OFF); + _compositeCamera->addChild(geode); + } +} + +DepthPeeling::DepthPeeling(unsigned int width, unsigned int height) : + _numPasses(9), + _texUnit(2), + _texWidth(width), + _texHeight(height), + _showAllLayers(false), + _offsetValue(8), + _root(new osg::Group), + _solidscene(new osg::Group), + _transparentscene(new osg::Group) +{ + createPeeling(); +} + +void DepthPeeling::setSolidScene(osg::Node* scene) +{ + _solidscene->removeChildren(0, _solidscene->getNumChildren()); + _solidscene->addChild(scene); +} + +void DepthPeeling::setTransparentScene(osg::Node* scene) +{ + _transparentscene->removeChildren(0, _transparentscene->getNumChildren()); + _transparentscene->addChild(scene); +} + +osg::Node* DepthPeeling::getRoot() +{ + return _root.get(); +} + +void DepthPeeling::resize(int width, int height) +{ +#ifdef USE_TEXTURE_RECTANGLE + for (unsigned int i = 0; i < 3; ++i) + _depthTextures[i]->setTextureSize(width, height); + for (unsigned int i = 0; i < _colorTextures.size(); ++i) + _colorTextures[i]->setTextureSize(width, height); + _texWidth = width; + _texHeight = height; +#else +#ifndef USE_NON_POWER_OF_TWO_TEXTURE + width = nextPowerOfTwo(width); + height = nextPowerOfTwo(height); +#endif + _depthTextures[0]->setTextureSize(width, height); + _depthTextures[1]->setTextureSize(width, height); + for (unsigned int i = 0; i < _colorTextures.size(); ++i) + _colorTextures[i]->setTextureSize(width, height); + _texWidth = width; + _texHeight = height; +#endif + createPeeling(); +} + +void DepthPeeling::setNumPasses(unsigned int numPasses) +{ + if (numPasses == _numPasses) + return; + if (numPasses == unsigned(-1)) + return; + _numPasses = numPasses; + createPeeling(); +} +unsigned int DepthPeeling::getNumPasses() const +{ + return _numPasses; +} + +void DepthPeeling::setTexUnit(unsigned int texUnit) +{ + if (texUnit == _texUnit) + return; + _texUnit = texUnit; + createPeeling(); +} + +void DepthPeeling::setShowAllLayers(bool showAllLayers) +{ + if (showAllLayers == _showAllLayers) + return; + _showAllLayers = showAllLayers; + createPeeling(); +} +bool DepthPeeling::getShowAllLayers() const +{ + return _showAllLayers; +} + +void DepthPeeling::setOffsetValue(unsigned int offsetValue) +{ + if (offsetValue == _offsetValue) + return; + _offsetValue = offsetValue; + createPeeling(); +} +unsigned int DepthPeeling::getOffsetValue() const +{ + return _offsetValue; +} + + +DepthPeeling::EventHandler::EventHandler(DepthPeeling* depthPeeling) : + _depthPeeling(depthPeeling) +{ } + + +/** Handle events, return true if handled, false otherwise. */ +bool DepthPeeling::EventHandler::handle(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter&, osg::Object*, osg::NodeVisitor*) +{ + if (ea.getEventType() == osgGA::GUIEventAdapter::RESIZE) { + _depthPeeling->resize(ea.getWindowWidth(), ea.getWindowHeight()); + return true; + } + + if (ea.getEventType() == osgGA::GUIEventAdapter::KEYDOWN) { + switch (ea.getKey()) { + case 'm': + _depthPeeling->setNumPasses(_depthPeeling->getNumPasses() + 1); + return true; + case 'n': + _depthPeeling->setNumPasses(_depthPeeling->getNumPasses() - 1); + return true; + case 'p': + _depthPeeling->setOffsetValue(_depthPeeling->getOffsetValue() + 1); + return true; + case 'o': + _depthPeeling->setOffsetValue(_depthPeeling->getOffsetValue() - 1); + return true; + case 'l': + _depthPeeling->setShowAllLayers(!_depthPeeling->getShowAllLayers()); + return true; + default: + return false; + }; + } + + return false; +} diff --git a/examples/osgoit/DepthPeeling.h b/examples/osgoit/DepthPeeling.h new file mode 100644 index 000000000..9598c7abf --- /dev/null +++ b/examples/osgoit/DepthPeeling.h @@ -0,0 +1,102 @@ + +#include +#include +#include +#include +#include +#include + +#include + +#ifndef DEPTHPEELING_H +#define DEPTHPEELING_H + +// Some choices for the kind of textures we can use ... +#define USE_TEXTURE_RECTANGLE +//#define USE_NON_POWER_OF_TWO_TEXTURE +#define USE_PACKED_DEPTH_STENCIL + +template +inline T +nextPowerOfTwo(T k) +{ + if (k == T(0)) + return 1; + k--; + for (int i = 1; i < std::numeric_limits::digits; i <<= 1) + k = k | k >> i; + return k + 1; +} + +class DepthPeeling : public osg::Referenced { +public: + + DepthPeeling(unsigned int width, unsigned int height); + void setSolidScene(osg::Node* scene); + void setTransparentScene(osg::Node* scene); + osg::Node* getRoot(); + void resize(int width, int height); + void setNumPasses(unsigned int numPasses); + unsigned int getNumPasses() const; + void setTexUnit(unsigned int texUnit); + void setShowAllLayers(bool showAllLayers); + bool getShowAllLayers() const; + void setOffsetValue(unsigned int offsetValue); + unsigned int getOffsetValue() const; + + static const char *PeelingShader; /* use this to support depth peeling in GLSL shaders in transparent objects */ + + class EventHandler : public osgGA::GUIEventHandler { + public: + EventHandler(DepthPeeling* depthPeeling); + + /** Handle events, return true if handled, false otherwise. */ + virtual bool handle(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter&, osg::Object*, osg::NodeVisitor*); + + protected: + osg::ref_ptr _depthPeeling; + }; + +protected: + osg::Node *createQuad(unsigned int layerNumber, unsigned int numTiles); + void createPeeling(); + + class CullCallback : public osg::NodeCallback { + public: + CullCallback(unsigned int texUnit, unsigned int texWidth, unsigned int texHeight, unsigned int offsetValue); + virtual void operator()(osg::Node* node, osg::NodeVisitor* nv); + + private: + unsigned int _texUnit; + unsigned int _texWidth; + unsigned int _texHeight; + unsigned int _offsetValue; + }; + + unsigned int _numPasses; + unsigned int _texUnit; + unsigned int _texWidth; + unsigned int _texHeight; + bool _showAllLayers; + unsigned int _offsetValue; + + // The root node that is handed over to the viewer + osg::ref_ptr _root; + + // The scene that is displayed + osg::ref_ptr _solidscene; + osg::ref_ptr _transparentscene; + + // The final camera that composites the pre rendered textures to the final picture + osg::ref_ptr _compositeCamera; + +#ifdef USE_TEXTURE_RECTANGLE + std::vector > _depthTextures; + std::vector > _colorTextures; +#else + std::vector > _depthTextures; + std::vector > _colorTextures; +#endif +}; + +#endif // #ifndef DEPTHPEELING_H diff --git a/examples/osgoit/HeatMap.cpp b/examples/osgoit/HeatMap.cpp new file mode 100644 index 000000000..a5fdbb2a1 --- /dev/null +++ b/examples/osgoit/HeatMap.cpp @@ -0,0 +1,232 @@ +/* + * 3D Heat map using vertex displacement mapping + * Rendered using depth peeling in fragment shader. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define _USE_MATH_DEFINES +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "HeatMap.h" +#include "DepthPeeling.h" + +/////////////////////////////////////////////////////////////////////////// +// in-line GLSL source code + +static const char *VertexShader = { + "#version 120\n" + "uniform float maximum;\n" + "uniform float maxheight;\n" + "uniform float transparency;\n" + "uniform sampler1D colortex;\n" + "uniform sampler2D datatex;\n" + "in vec2 xypos;\n" + "void main(void)\n" + "{\n" + " float foo;\n" + " float tmp = min(texture2D(datatex, xypos).x / maximum, 1.0);\n" + " gl_Position = gl_ModelViewProjectionMatrix * (gl_Vertex + vec4(0.0, 0.0, maxheight * tmp, 0.0));\n" + " vec4 color = texture1D(colortex, tmp);\n" + " color.w = color.w * transparency;\n" + " gl_FrontColor = color;\n" + " gl_BackColor = color;\n" + "}\n" +}; + +static const char *FragmentShader = +{ + "#version 120\n" + "bool depthpeeling();\n" + "void main(void)\n" + "{\n" + " if( depthpeeling() ) discard;\n" + " gl_FragColor = gl_Color;\n" + "}\n" +}; + +/** + * Overloaded Geometry class to return predefined bounds + */ +class MyGeometry : public osg::Geometry +{ +public: + MyGeometry(osg::BoundingBox bounds) + { + m_bounds = bounds; + m_bsphere = osg::BoundingSphere(bounds); + } + + // an attempt to return a reasonable bounding box. Still does not prevent clipping of the heat map. + virtual const osg::BoundingBox& getBoundingBox() const {return m_bounds;} + virtual osg::BoundingBox computeBound() const {return m_bounds;} + virtual const osg::BoundingSphere& getBound() const {return m_bsphere;} + +protected: + osg::BoundingBox m_bounds; + osg::BoundingSphere m_bsphere; +}; + +Heatmap::Heatmap(float width, float depth, float maxheight, unsigned int K, unsigned int N, float maximum, float transparency) +{ + m_K = K; + m_N = N; + const int O = 4; + + // create Geometry object to store all the vertices primitives. + osg::Geometry *meshGeom = new MyGeometry(osg::BoundingBox(osg::Vec3(-width/2, -depth/2, 0), osg::Vec3(width/2, depth/2, maxheight))); + + // we use a float attribute array storing texcoords + osg::Vec2Array* xypositions = new osg::Vec2Array(); + xypositions->setName("xypos"); + + // create vertex coordinates + osg::Vec3Array* vertices = new osg::Vec3Array(); + osg::Vec3 off(-width/2, -depth/2, 0); + for (unsigned int y=0; y < O*N; y++) { + if (y % 2 == 0) + { + for (unsigned int x=0; x < O*K; x++) { + vertices->push_back(osg::Vec3(width*x/(O*K-1), depth*y/(O*N-1), 0.0)+off); + xypositions->push_back(osg::Vec2(((float)x+0.5f)/(O*K),((float)y+0.5f)/(O*N))); + } + } + else + { + vertices->push_back(osg::Vec3(0, depth*y/(O*N-1), 0.0)+off); + xypositions->push_back(osg::Vec2(0.5f/(O*K),((float)y+0.5f)/(O*N))); + for (unsigned int x=0; x < O*K-1; x++) { + vertices->push_back(osg::Vec3(width*(0.5+x)/(O*K-1), depth*y/(O*N-1), 0.0)+off); + xypositions->push_back(osg::Vec2(((float)(x+0.5f)+0.5f)/(O*K),((float)y+0.5f)/(O*N))); + } + vertices->push_back(osg::Vec3(width, depth*y/(O*N-1), 0.0)+off); + xypositions->push_back(osg::Vec2(1.0f-0.5f/(O*K),((float)y+0.5f)/(O*N))); + } + } + + meshGeom->setVertexAttribArray(6, xypositions); + meshGeom->setVertexAttribNormalize(6, false); + meshGeom->setVertexAttribBinding(6, osg::Geometry::BIND_PER_VERTEX); + meshGeom->setVertexArray(vertices); + + // generate several tri strips to form a mesh + GLuint *indices = new GLuint[4*O*K]; + for (unsigned int y=0; y < O*N-1; y++) { + if (y % 2 == 0) + { + int base = (y/2) * (O*K+O*K+1); + int base2 = (y/2) * (O*K+O*K+1) + O*K; + int i=0; for (unsigned int x=0; x < O*K; x++) { indices[i++] = base2+x; indices[i++] = base+x;} + indices[i++] = base2+O*K; + meshGeom->addPrimitiveSet(new osg::DrawElementsUInt(osg::PrimitiveSet::TRIANGLE_STRIP, i, indices)); + } + else + { + int base = (y/2) * (O*K+O*K+1) + O*K; + int base2 = (y/2) * (O*K+O*K+1) + O*K + O*K+1; + int i=0; for (unsigned int x=0; x < O*K; x++) { indices[i++] = base+x; indices[i++] = base2+x;} + indices[i++] = base+O*K; + meshGeom->addPrimitiveSet(new osg::DrawElementsUInt(osg::PrimitiveSet::TRIANGLE_STRIP, i, indices)); + } + } + delete[] indices; + + // create vertex and fragment shader + osg::Program* program = new osg::Program; + program->setName( "mesh" ); + program->addBindAttribLocation("xypos", 6); + program->addShader( new osg::Shader( osg::Shader::VERTEX, VertexShader ) ); + program->addShader( new osg::Shader( osg::Shader::FRAGMENT, DepthPeeling::PeelingShader ) ); + program->addShader( new osg::Shader( osg::Shader::FRAGMENT, FragmentShader ) ); + + // create a 1D texture for color lookups + colorimg = new osg::Image(); + colorimg->allocateImage(5, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE); + unsigned char *data = colorimg->data(); + *data++ = 0; *data++ = 0; *data++ = 255; *data++ = 0; // fully transparent blue + *data++ = 0; *data++ = 255; *data++ = 255; *data++ = 255; // turquoise + *data++ = 0; *data++ = 255; *data++ = 0; *data++ = 255; // green + *data++ = 255; *data++ = 255; *data++ = 0; *data++ = 255; // yellow + *data++ = 255; *data++ = 0; *data++ = 0; *data++ = 255; // red + colortex = new osg::Texture1D(colorimg.get()); + colortex->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR); + colortex->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR); + colortex->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE); + colortex->setResizeNonPowerOfTwoHint(false); + + // create a 2D texture for data lookups + m_img2 = new osg::Image(); + m_img2->allocateImage(K, N, 1, GL_LUMINANCE, GL_FLOAT); + m_img2->setInternalTextureFormat(GL_RGB32F_ARB); + m_data = (float*)m_img2.get()->data(); + m_tex2 = new osg::Texture2D(m_img2.get()); + m_tex2.get()->setResizeNonPowerOfTwoHint(false); + m_tex2.get()->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR); + m_tex2.get()->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR); + m_tex2.get()->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE); + m_tex2.get()->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE); + + // set render states + osg::StateSet *meshstate = meshGeom->getOrCreateStateSet(); + meshstate->setMode(GL_BLEND, osg::StateAttribute::ON); + meshstate->setMode(GL_LIGHTING, osg::StateAttribute::OFF); + meshstate->setAttributeAndModes(program, osg::StateAttribute::ON); + meshstate->setTextureAttributeAndModes(0,colortex.get(),osg::StateAttribute::ON); + meshstate->setTextureAttributeAndModes(1,m_tex2.get(),osg::StateAttribute::ON); + + // uniforms for height and color scaling + maximumUniform = new osg::Uniform( "maximum", (float)maximum ); + maxheightUniform = new osg::Uniform( "maxheight", (float)maxheight ); + transparencyUniform = new osg::Uniform( "transparency", (float)transparency); + + osg::Uniform* texUniform = new osg::Uniform(osg::Uniform::SAMPLER_1D, "colortex"); + texUniform->set(0); + osg::Uniform* texUniform2 = new osg::Uniform(osg::Uniform::SAMPLER_2D, "datatex"); + texUniform2->set(1); + meshstate->addUniform(texUniform); + meshstate->addUniform(texUniform2); + meshstate->addUniform(maximumUniform); + meshstate->addUniform(maxheightUniform); + meshstate->addUniform(transparencyUniform); + + // add the geometries to the geode. + meshGeom->setUseDisplayList(false); + addDrawable(meshGeom); +} + +void Heatmap::setData(float *buffer, float maxheight, float maximum, float transparency) +{ + memcpy(m_data, buffer, m_N*m_K*sizeof(float)); + + maximumUniform->set( maximum ); + maxheightUniform->set( maxheight ); + transparencyUniform->set ( transparency ); + + m_img2.get()->dirty(); +} + +Heatmap::~Heatmap() +{ +} diff --git a/examples/osgoit/HeatMap.h b/examples/osgoit/HeatMap.h new file mode 100644 index 000000000..b77a0179f --- /dev/null +++ b/examples/osgoit/HeatMap.h @@ -0,0 +1,33 @@ + +#ifndef HEATMAP_H +#define HEATMAP_H + +#include +#include +#include +#include + +class Heatmap : public osg::Geode +{ +public: + Heatmap(float width, float depth, float maxheight, unsigned int K, unsigned int N, float maximum, float transparency); + ~Heatmap(); + + void setData(float *buffer, float maxheight, float maximum, float transparency); + +protected: + unsigned int m_K; + unsigned int m_N; + float *m_data; + osg::ref_ptr m_img2; + osg::ref_ptr m_tex2; + + osg::ref_ptr colorimg; + osg::ref_ptr colortex; + + osg::Uniform *maximumUniform; + osg::Uniform *maxheightUniform; + osg::Uniform *transparencyUniform; +}; + +#endif // #ifndef HEATMAP_H diff --git a/examples/osgoit/osgoit.cpp b/examples/osgoit/osgoit.cpp index d69c2aeda..cf9b19706 100644 --- a/examples/osgoit/osgoit.cpp +++ b/examples/osgoit/osgoit.cpp @@ -1,460 +1,49 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +/* OpenSceneGraph example, myosgoit. +* +* Author: Christian Buchner, based on original osgoit by Mathias Frhlich +* +* This demo provides a DepthPeeling object that can correctly compose +* solid and transparent geometry within the same scene. The transparent +* geometry can also use GLSL shaders, as demonstrated in the 3D HeatMap. +* The solid geometry is only rendered once, and its depth buffer blitted +* into the cameras rendering the transparency layers. +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +* THE SOFTWARE. +*/ -#include +#include "DepthPeeling.h" +#include "HeatMap.h" #include #include #include +#include +#include +#include +#include +#include #include #include -// Some choices for the kind of textures we can use ... -#define USE_TEXTURE_RECTANGLE -// #define USE_NON_POWER_OF_TWO_TEXTURE -#define USE_PACKED_DEPTH_STENCIL - -template -inline T -nextPowerOfTwo(T k) -{ - if (k == T(0)) - return 1; - k--; - for (int i = 1; i < std::numeric_limits::digits; i <<= 1) - k = k | k >> i; - return k + 1; -} - -class DepthPeeling : public osg::Referenced { -public: - osg::Node* - createQuad(unsigned layerNumber, unsigned numTiles) - { - float tileSpan = 1; - float tileOffsetX = 0; - float tileOffsetY = 0; - if (_showAllLayers) { - tileSpan /= numTiles; - tileOffsetX = tileSpan * (layerNumber%numTiles); - tileOffsetY = 1 - tileSpan * (1 + layerNumber/numTiles); - } - - osg::Vec3Array* vertices = new osg::Vec3Array; - - vertices->push_back(osg::Vec3f(tileOffsetX , tileOffsetY , 0)); - vertices->push_back(osg::Vec3f(tileOffsetX , tileOffsetY + tileSpan, 0)); - vertices->push_back(osg::Vec3f(tileOffsetX + tileSpan, tileOffsetY + tileSpan, 0)); - vertices->push_back(osg::Vec3f(tileOffsetX + tileSpan, tileOffsetY , 0)); - - osg::Vec3Array* colors = new osg::Vec3Array; - colors->push_back(osg::Vec3(1, 1, 1)); - - osg::Vec2Array* texcoords = new osg::Vec2Array; - texcoords->push_back(osg::Vec2f(0, 0)); - texcoords->push_back(osg::Vec2f(0, 1)); - texcoords->push_back(osg::Vec2f(1, 1)); - texcoords->push_back(osg::Vec2f(1, 0)); - - osg::Geometry* geometry = new osg::Geometry; - geometry->setVertexArray(vertices); - geometry->setTexCoordArray(0, texcoords); - - geometry->setColorArray(colors); - geometry->setColorBinding(osg::Geometry::BIND_OVERALL); - - geometry->addPrimitiveSet(new osg::DrawArrays(GL_QUADS, 0, 4)); - - osg::Geode* geode = new osg::Geode; - geode->addDrawable(geometry); - - return geode; - } - - class CullCallback : public osg::NodeCallback { - public: - CullCallback(unsigned texUnit, unsigned texWidth, unsigned texHeight, unsigned offsetValue) : - _texUnit(texUnit), - _texWidth(texWidth), - _texHeight(texHeight), - _offsetValue(offsetValue) - { - } - - virtual void operator()(osg::Node* node, osg::NodeVisitor* nv) - { - osgUtil::CullVisitor* cullVisitor = static_cast(nv); - osgUtil::RenderStage* renderStage = cullVisitor->getCurrentRenderStage(); - const osg::Viewport* viewport = renderStage->getViewport(); - - osg::Matrixd m(*cullVisitor->getProjectionMatrix()); - m.postMultTranslate(osg::Vec3d(1, 1, 1)); - m.postMultScale(osg::Vec3d(0.5, 0.5, 0.5)); - - // scale the texture coordinates to the viewport -#ifdef USE_TEXTURE_RECTANGLE - m.postMultScale(osg::Vec3d(viewport->width(), viewport->height(), 1)); -#else -#ifndef USE_NON_POWER_OF_TWO_TEXTURE - m.postMultScale(osg::Vec3d(viewport->width()/double(_texWidth), viewport->height()/double(_texHeight), 1)); -#endif -#endif - - // Kind of polygon offset: note this way, we can also offset lines and points. - // Whereas with the polygon offset we could only handle surface primitives. - m.postMultTranslate(osg::Vec3d(0, 0, -ldexp(double(_offsetValue), -24))); - - osg::TexMat* texMat = new osg::TexMat(m); - osg::StateSet* stateSet = new osg::StateSet; - stateSet->setTextureAttribute(_texUnit, texMat); - cullVisitor->pushStateSet(stateSet); - traverse(node, nv); - cullVisitor->popStateSet(); - } - - private: - unsigned _texUnit; - unsigned _texWidth; - unsigned _texHeight; - unsigned _offsetValue; - }; - - void - createPeeling() - { - int numTiles = ceil(sqrt(double(_numPasses))); - - _root->removeChildren(0, _root->getNumChildren()); - _colorTextures.clear(); - - // If not enabled, just use the top level camera - if (!_depthPeelingEnabled) { - _root->addChild(_scene.get()); - return; - } - - _compositeCamera = new osg::Camera; - _compositeCamera->setDataVariance(osg::Object::DYNAMIC); - _compositeCamera->setInheritanceMask(osg::Camera::READ_BUFFER | osg::Camera::DRAW_BUFFER); - _compositeCamera->setRenderOrder(osg::Camera::POST_RENDER); - _compositeCamera->setComputeNearFarMode(osg::Camera::COMPUTE_NEAR_FAR_USING_PRIMITIVES); - _compositeCamera->setClearMask(0); - - _compositeCamera->setReferenceFrame(osg::Transform::ABSOLUTE_RF); - _compositeCamera->setViewMatrix(osg::Matrix()); - _compositeCamera->setProjectionMatrix(osg::Matrix::ortho2D(0, 1, 0, 1)); - - _compositeCamera->setCullCallback(new CullCallback(0, _texWidth, _texHeight, 0)); - - osg::StateSet* stateSet = _compositeCamera->getOrCreateStateSet(); - stateSet->setBinName("TraversalOrderBin"); - stateSet->setRenderBinMode(osg::StateSet::USE_RENDERBIN_DETAILS); - - _root->addChild(_compositeCamera.get()); - - for (unsigned i = 0; i < 2; ++i) { -#ifdef USE_TEXTURE_RECTANGLE - _depthTextures[i] = new osg::TextureRectangle; -#else - _depthTextures[i] = new osg::Texture2D; -#endif - _depthTextures[i]->setTextureSize(_texWidth, _texHeight); - - _depthTextures[i]->setFilter(osg::Texture::MIN_FILTER, osg::Texture::NEAREST); - _depthTextures[i]->setFilter(osg::Texture::MAG_FILTER, osg::Texture::NEAREST); - _depthTextures[i]->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_BORDER); - _depthTextures[i]->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_BORDER); - -#ifdef USE_PACKED_DEPTH_STENCIL - _depthTextures[i]->setInternalFormat(GL_DEPTH24_STENCIL8_EXT); - _depthTextures[i]->setSourceFormat(GL_DEPTH_STENCIL_EXT); - _depthTextures[i]->setSourceType(GL_UNSIGNED_INT_24_8_EXT); -#else - _depthTextures[i]->setInternalFormat(GL_DEPTH_COMPONENT); - _depthTextures[i]->setInternalFormat(GL_DEPTH_COMPONENT24); -#endif - - _depthTextures[i]->setShadowComparison(true); - _depthTextures[i]->setShadowAmbient(0); // The r value if the test fails - _depthTextures[i]->setShadowCompareFunc(osg::Texture::GREATER); - _depthTextures[i]->setShadowTextureMode(osg::Texture::INTENSITY); - } - - // Then, the other ones - for (unsigned i = 0; i < _numPasses; ++i) { - osg::Camera* camera = new osg::Camera; - camera->setDataVariance(osg::Object::DYNAMIC); - - camera->setInheritanceMask(osg::Camera::ALL_VARIABLES); - camera->setRenderTargetImplementation(osg::Camera::FRAME_BUFFER_OBJECT); - camera->setRenderOrder(osg::Camera::PRE_RENDER, i); - camera->setClearMask(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT); - camera->setClearColor(osg::Vec4f(0, 0, 0, 0)); - - camera->setComputeNearFarMode(osg::Camera::DO_NOT_COMPUTE_NEAR_FAR); - - osg::ref_ptr depthTexture = _depthTextures[i%2]; - osg::ref_ptr prevDepthTexture = _depthTextures[(i+1)%2]; - -#ifdef USE_PACKED_DEPTH_STENCIL - camera->attach(osg::Camera::PACKED_DEPTH_STENCIL_BUFFER, depthTexture.get()); -#else - camera->attach(osg::Camera::DEPTH_BUFFER, depthTexture.get()); -#endif - -#ifdef USE_TEXTURE_RECTANGLE - osg::ref_ptr colorTexture = new osg::TextureRectangle; -#else - osg::ref_ptr colorTexture = new osg::Texture2D; -#endif - _colorTextures.push_back(colorTexture); - - colorTexture->setTextureSize(_texWidth, _texHeight); - colorTexture->setFilter(osg::Texture::MIN_FILTER, osg::Texture::NEAREST); - colorTexture->setFilter(osg::Texture::MAG_FILTER, osg::Texture::NEAREST); - colorTexture->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_BORDER); - colorTexture->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_BORDER); - colorTexture->setInternalFormat(GL_RGBA); - camera->attach(osg::Camera::COLOR_BUFFER, colorTexture.get()); - - camera->getOrCreateStateSet()->setMode(GL_BLEND, osg::StateAttribute::OFF | osg::StateAttribute::OVERRIDE); - if (0 == i) { - camera->addChild(_scene.get()); - } else { - osg::StateSet* stateSet = camera->getOrCreateStateSet(); - - stateSet->setAttributeAndModes(new osg::AlphaFunc(osg::AlphaFunc::GREATER, 0.01), - osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE); - - stateSet->setTextureAttributeAndModes(_texUnit, prevDepthTexture.get()); - - // Is the default ... - // stateSet->setTextureAttributeAndModes(_texUnit, new osg::TexEnv(osg::TexEnv::MODULATE)); - stateSet->setTextureMode(_texUnit, GL_TEXTURE_GEN_S, osg::StateAttribute::ON); - stateSet->setTextureMode(_texUnit, GL_TEXTURE_GEN_T, osg::StateAttribute::ON); - stateSet->setTextureMode(_texUnit, GL_TEXTURE_GEN_R, osg::StateAttribute::ON); - stateSet->setTextureMode(_texUnit, GL_TEXTURE_GEN_Q, osg::StateAttribute::ON); - - osg::TexGenNode* texGenNode = new osg::TexGenNode; - texGenNode->setReferenceFrame(osg::TexGenNode::ABSOLUTE_RF); - texGenNode->setTextureUnit(_texUnit); - texGenNode->getTexGen()->setMode(osg::TexGen::EYE_LINEAR); - camera->addChild(texGenNode); - camera->addCullCallback(new CullCallback(_texUnit, _texWidth, _texHeight, _offsetValue)); - - texGenNode->addChild(_scene.get()); - } - - _root->addChild(camera); - - osg::Node* geode = createQuad(i, numTiles); - osg::StateSet* stateSet = geode->getOrCreateStateSet(); - stateSet->setTextureAttributeAndModes(0, colorTexture.get(), osg::StateAttribute::ON); - stateSet->setAttribute(new osg::BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA), osg::StateAttribute::ON); - stateSet->setMode(GL_BLEND, osg::StateAttribute::ON); - stateSet->setMode(GL_LIGHTING, osg::StateAttribute::OFF); - stateSet->setMode(GL_DEPTH_TEST, osg::StateAttribute::OFF); - _compositeCamera->insertChild(0, geode); - } - } - - DepthPeeling(unsigned width, unsigned height) : - _numPasses(8), - _texUnit(1), - _texWidth(width), - _texHeight(height), - _showAllLayers(false), - _depthPeelingEnabled(true), - _offsetValue(8), - _root(new osg::Group), - _scene(new osg::Group) - { - createPeeling(); - } - - void setScene(osg::Node* scene) - { - _scene->removeChildren(0, _scene->getNumChildren()); - _scene->addChild(scene); - } - - osg::Node* getRoot() - { - return _root.get(); - } - - void resize(int width, int height) - { -#ifdef USE_TEXTURE_RECTANGLE - _depthTextures[0]->setTextureSize(width, height); - _depthTextures[1]->setTextureSize(width, height); - for (unsigned i = 0; i < _colorTextures.size(); ++i) - _colorTextures[i]->setTextureSize(width, height); - _texWidth = width; - _texHeight = height; -#else -#ifndef USE_NON_POWER_OF_TWO_TEXTURE - width = nextPowerOfTwo(width); - height = nextPowerOfTwo(height); -#endif - _depthTextures[0]->setTextureSize(width, height); - _depthTextures[1]->setTextureSize(width, height); - for (unsigned i = 0; i < _colorTextures.size(); ++i) - _colorTextures[i]->setTextureSize(width, height); - _texWidth = width; - _texHeight = height; -#endif - createPeeling(); - } - - void setNumPasses(unsigned numPasses) - { - if (numPasses == _numPasses) - return; - if (numPasses == unsigned(-1)) - return; - _numPasses = numPasses; - createPeeling(); - } - unsigned getNumPasses() const - { - return _numPasses; - } - - void setTexUnit(unsigned texUnit) - { - if (texUnit == _texUnit) - return; - _texUnit = texUnit; - createPeeling(); - } - - void setShowAllLayers(bool showAllLayers) - { - if (showAllLayers == _showAllLayers) - return; - _showAllLayers = showAllLayers; - createPeeling(); - } - bool getShowAllLayers() const - { - return _showAllLayers; - } - - void setDepthPeelingEnabled(bool depthPeelingEnabled) - { - if (depthPeelingEnabled == _depthPeelingEnabled) - return; - _depthPeelingEnabled = depthPeelingEnabled; - createPeeling(); - } - bool getDepthPeelingEnabled() const - { - return _depthPeelingEnabled; - } - - void setOffsetValue(unsigned offsetValue) - { - if (offsetValue == _offsetValue) - return; - _offsetValue = offsetValue; - createPeeling(); - } - unsigned getOffsetValue() const - { - return _offsetValue; - } - - unsigned _numPasses; - unsigned _texUnit; - unsigned _texWidth; - unsigned _texHeight; - bool _showAllLayers; - bool _depthPeelingEnabled; - unsigned _offsetValue; - - // The root node that is handed over to the viewer - osg::ref_ptr _root; - - // The scene that is displayed - osg::ref_ptr _scene; - - // The final camera that composites the pre rendered textures to the final picture - osg::ref_ptr _compositeCamera; - -#ifdef USE_TEXTURE_RECTANGLE - osg::ref_ptr _depthTextures[2]; - std::vector > _colorTextures; -#else - osg::ref_ptr _depthTextures[2]; - std::vector > _colorTextures; -#endif -}; - -class EventHandler : public osgGA::GUIEventHandler { -public: - EventHandler(DepthPeeling* depthPeeling) : - _depthPeeling(depthPeeling) - { } - - /** Handle events, return true if handled, false otherwise. */ - virtual bool handle(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter&, osg::Object*, osg::NodeVisitor*) - { - if (ea.getEventType() == osgGA::GUIEventAdapter::RESIZE) { - _depthPeeling->resize(ea.getWindowWidth(), ea.getWindowHeight()); - return true; - } - - if (ea.getEventType() == osgGA::GUIEventAdapter::KEYDOWN) { - switch (ea.getKey()) { - case 'd': - _depthPeeling->setDepthPeelingEnabled(!_depthPeeling->getDepthPeelingEnabled()); - return true; - case 'm': - _depthPeeling->setNumPasses(_depthPeeling->getNumPasses() + 1); - return true; - case 'n': - _depthPeeling->setNumPasses(_depthPeeling->getNumPasses() - 1); - return true; - case 'p': - _depthPeeling->setOffsetValue(_depthPeeling->getOffsetValue() + 1); - return true; - case 'o': - _depthPeeling->setOffsetValue(_depthPeeling->getOffsetValue() - 1); - return true; - case 'l': - _depthPeeling->setShowAllLayers(!_depthPeeling->getShowAllLayers()); - return true; - default: - return false; - }; - } - - return false; - } - - osg::ref_ptr _depthPeeling; -}; - int main(int argc, char** argv) { // use an ArgumentParser object to manage the program arguments. osg::ArgumentParser arguments(&argc, argv); - arguments.getApplicationUsage()->addKeyboardMouseBinding("d", "Toggle depth peeling enabled"); arguments.getApplicationUsage()->addKeyboardMouseBinding("m", "Increase the number of depth peeling layers"); arguments.getApplicationUsage()->addKeyboardMouseBinding("n", "Decrease the number of depth peeling layers"); arguments.getApplicationUsage()->addKeyboardMouseBinding("l", "Toggle display of the individual or composed layer textures"); @@ -473,31 +62,65 @@ int main(int argc, char** argv) // add the help handler viewer.addEventHandler(new osgViewer::HelpHandler(arguments.getApplicationUsage())); - // load the data - osg::ref_ptr loadedModel = osgDB::readNodeFiles(arguments); - if (!loadedModel) - { - std::cout << arguments.getApplicationName() <<": No data loaded" << std::endl; - return 1; - } - // any option left unread are converted into errors to write out later. arguments.reportRemainingOptionsAsUnrecognized(); - // report any errors if they have occurred when parsing the program arguments. - if (arguments.errors()) - { - arguments.writeErrorMessages(std::cout); - return 1; - } + // read the dump truck, we will need it twice + osg::ref_ptr dt = osgDB::readNodeFile("dumptruck.osg"); - // The initial size sez to 0, 0. We get a resize event for the right size ... + // display a solid version of the dump truck + osg::ref_ptr solidModel = new osg::PositionAttitudeTransform; + solidModel->setPosition(osg::Vec3f(7.0f, -2.0f, 7.0f)); + solidModel->addChild(dt.get()); + + // generate the 3D heatmap surface to display + osg::ref_ptr hm = new Heatmap(30, 30, 10, 30, 30, 1.0, 0.25); + float data[30][30]; + for (int x=0; x < 30; ++x) + for (int y=0; y < 30; ++y) + data[y][x] = (double)rand() / RAND_MAX; + hm->setData((float*)data, 10.0, 1.0, 0.25); + + // add a transparent version of the truck to the scene also + osg::ref_ptr transparentTruck = new osg::PositionAttitudeTransform; + transparentTruck->setPosition(osg::Vec3f(7.0f, -25.0f, 7.0f)); + + // set the states of the truck so that it actually appears transparently and nicely lit. + osg::StateSet *state = transparentTruck->getOrCreateStateSet(); + state->setMode(GL_BLEND, osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE); + state->setAttribute(new osg::BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA), osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE); + osg::Material* material = new osg::Material; + material->setAmbient(osg::Material::FRONT_AND_BACK,osg::Vec4(0.2f,0.2f,0.2f,0.3f)); + material->setDiffuse(osg::Material::FRONT_AND_BACK,osg::Vec4(0.8f,0.8f,0.8f,0.3f)); + state->setAttribute(material,osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE); + osg::LightModel *lm = new osg::LightModel(); + lm->setTwoSided(true); + state->setAttribute(lm, osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE); + (transparentTruck.get())->addChild(dt.get()); + + // place the heatmap and a transparent dump truck in the transparent geometry group + osg::ref_ptr transparentModel = new osg::Group; + (transparentModel.get())->addChild(hm.get()); + (transparentModel.get())->addChild(transparentTruck.get()); + + // The initial size set to 0, 0. We get a resize event for the right size... DepthPeeling* depthPeeling = new DepthPeeling(0, 0); - depthPeeling->setScene(loadedModel.get()); + // the heat map already uses two textures bound to unit 0 and 1, so we can use TexUnit 2 for the peeling + depthPeeling->setTexUnit(2); + depthPeeling->setSolidScene(solidModel.get()); + depthPeeling->setTransparentScene(transparentModel.get()); viewer.setSceneData(depthPeeling->getRoot()); // Add the event handler for the depth peeling stuff - viewer.addEventHandler(new EventHandler(depthPeeling)); - + viewer.addEventHandler(new DepthPeeling::EventHandler(depthPeeling)); + + // force a resize event, so the DepthPeeling object updates _texWidth and _texHeight + viewer.realize(); + int x, y, width, height; + osgViewer::ViewerBase::Windows windows; + viewer.getWindows(windows); + windows.front()->getWindowRectangle(x,y,width,height); + viewer.getEventQueue()->windowResize(x,y,width,height); + return viewer.run(); }