From 7b73a58728525f5e22b0c0c3ca8e9c22eb5bcf9a Mon Sep 17 00:00:00 2001 From: Robert Osfield Date: Mon, 3 Sep 2007 12:27:37 +0000 Subject: [PATCH] Added osgdepthpeeling example --- examples/CMakeLists.txt | 1 + examples/osgdepthpeeling/CMakeLists.txt | 15 + examples/osgdepthpeeling/DePee.cpp | 669 +++++++++++++++++++ examples/osgdepthpeeling/DePee.h | 170 +++++ examples/osgdepthpeeling/DePeePass.cpp | 49 ++ examples/osgdepthpeeling/DePeePass.h | 56 ++ examples/osgdepthpeeling/Utility.cpp | 152 +++++ examples/osgdepthpeeling/Utility.h | 57 ++ examples/osgdepthpeeling/osgdepthpeeling.cpp | 328 +++++++++ 9 files changed, 1497 insertions(+) create mode 100644 examples/osgdepthpeeling/CMakeLists.txt create mode 100644 examples/osgdepthpeeling/DePee.cpp create mode 100644 examples/osgdepthpeeling/DePee.h create mode 100644 examples/osgdepthpeeling/DePeePass.cpp create mode 100644 examples/osgdepthpeeling/DePeePass.h create mode 100644 examples/osgdepthpeeling/Utility.cpp create mode 100644 examples/osgdepthpeeling/Utility.h create mode 100644 examples/osgdepthpeeling/osgdepthpeeling.cpp diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index ccfa2c15f..da039cebe 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -29,6 +29,7 @@ IF(DYNAMIC_OPENSCENEGRAPH) ADD_SUBDIRECTORY(osgcubemap) ADD_SUBDIRECTORY(osgdelaunay) ADD_SUBDIRECTORY(osgdepthpartition) + ADD_SUBDIRECTORY(osgdepthpeeling) ADD_SUBDIRECTORY(osgdistortion) ADD_SUBDIRECTORY(osgfadetext) ADD_SUBDIRECTORY(osgforest) diff --git a/examples/osgdepthpeeling/CMakeLists.txt b/examples/osgdepthpeeling/CMakeLists.txt new file mode 100644 index 000000000..d87ddd890 --- /dev/null +++ b/examples/osgdepthpeeling/CMakeLists.txt @@ -0,0 +1,15 @@ +SET(TARGET_SRC + DePee.cpp + DePeePass.cpp + Utility.cpp + osgdepthpeeling.cpp +) + +SET(TARGET_H + DePee.h + DePeePass.h + Utility.h +) + +#### end var setup ### +SETUP_EXAMPLE(osgdepthpeeling) diff --git a/examples/osgdepthpeeling/DePee.cpp b/examples/osgdepthpeeling/DePee.cpp new file mode 100644 index 000000000..075720d3f --- /dev/null +++ b/examples/osgdepthpeeling/DePee.cpp @@ -0,0 +1,669 @@ +/* + Steffen Frey + Fachpraktikum Graphik-Programmierung 2007 + Institut fuer Visualisierung und Interaktive Systeme + Universitaet Stuttgart + */ + +//export OSG_NOTIFY_LEVEL=DEBUG_INFO + +#include "DePee.h" + +#include +#include +#include +#include +#include + +#include "Utility.h" + +#include +#include +#include +#include + +DePee::DePee(osg::Group* parent, osg::Group* subgraph, unsigned width, unsigned height) +{ + _renderToFirst = false; + + _isSketchy =false; + _isColored = false; + _isEdgy = true; + _isCrayon = false; + + _normalDepthMapProgram = Utility::createProgram("shaders/depthpeel_normaldepthmap.vert","shaders/depthpeel_normaldepthmap.frag"); + _colorMapProgram = Utility::createProgram("shaders/depthpeel_colormap.vert","shaders/depthpeel_colormap.frag" ); + _edgeMapProgram = Utility::createProgram("shaders/depthpeel_edgemap.vert", "shaders/depthpeel_edgemap.frag"); + + _parent = new osg::Group; + parent->addChild(_parent.get()); + _subgraph = subgraph; + + _width = width; + _height = height; + _texWidth = width; + _texHeight = height; + + assert(parent); + assert(subgraph); + + _fps = 0; + _colorCamera = 0; + + _sketchy = new osg::Uniform("sketchy", false); + _colored = new osg::Uniform("colored", false); + _edgy = new osg::Uniform("edgy", true); + _sketchiness = new osg::Uniform("sketchiness", (float) 1.0); + + _normalDepthMap0 = Utility::newColorTexture2D(_texWidth, _texHeight, 32); + _normalDepthMap1 = Utility::newColorTexture2D(_texWidth, _texHeight, 32); + _edgeMap = Utility::newColorTexture2D(_texWidth, _texHeight, 8); + _colorMap = Utility::newColorTexture2D(_texWidth, _texHeight, 8); + + //create a noise map...this doesn't end up in a new rendering pass + (void) createMap(NOISE_MAP); + + //the viewport aligned quad + _quadGeode = Utility::getCanvasQuad(_width, _height); + + + //!!!Getting problems if assigning unit to texture in depth peeling subraph and removing depth peeling steps!!! + //That's why it is done here + osg::StateSet* stateset = _parent->getOrCreateStateSet(); + stateset->setTextureAttributeAndModes(1, _normalDepthMap0.get(), osg::StateAttribute::ON); + stateset->setTextureAttributeAndModes(2, _normalDepthMap1.get(), osg::StateAttribute::ON); + stateset->setTextureAttributeAndModes(3, _edgeMap.get(), osg::StateAttribute::ON); + stateset->setTextureAttributeAndModes(4, _colorMap.get(), osg::StateAttribute::ON); + stateset->setTextureAttributeAndModes(5, _noiseMap.get(), osg::StateAttribute::ON); + + // render the final thing + (void) createFinal(); + + //take one step initially + addDePeePass(); + + //render head up display + (void) createHUD(); +} + +DePee::~DePee() +{ +} + +void DePee::setSketchy(bool sketchy) + { + _sketchy->set(sketchy); + _isSketchy = sketchy; + } + +void DePee::setCrayon(bool crayon) +{ + if(_isCrayon != crayon) + { + _isCrayon = crayon; + createMap(NOISE_MAP); + } +} + +void DePee::setSketchiness(double sketchiness) + { + _sketchiness->set((float)sketchiness); + } + +void DePee::setColored(bool colored) +{ + if(colored == !_isColored) + { + if(colored) + { + (void) createMap(COLOR_MAP, false); + } + else + { + _dePeePasses.back()->remRenderPass(COLOR_MAP); + } + _colored->set(colored); + _isColored = colored; + } +} + +void DePee::setEdgy(bool edgy) +{ + + if(edgy != _isEdgy) + { + + _isEdgy = edgy; + unsigned int n = 0; + while(remDePeePass()) + { + ++n; + } + + if(edgy) + { + (void) createMap(EDGE_MAP,_dePeePasses.size() == 1); + } + else + { + _dePeePasses.back()->remRenderPass(EDGE_MAP); + } + + for(int i=0; i < n; i++) + { + addDePeePass(); + } + } + _edgy->set(edgy); +} + + + +void DePee::setFPS(double* fps) +{ + _fps = fps; +} + +unsigned int DePee::getNumberOfRenderPasses() +{ + unsigned int n = 0; + for(int i=0; i < _dePeePasses.size();i++) + n += _dePeePasses.at(i)->Cameras.size(); + // add one pass for final rendering pass and one for hud + return n+2; +} + +bool DePee::addDePeePass() +{ + + if(_isColored) + { + //remove previous color pass + _dePeePasses.back()->remRenderPass(COLOR_MAP); + } + + _dePeePasses.push_back(new DePeePass()); + _parent->addChild(_dePeePasses.back()->root.get()); + + //need to create a depth map in every case + (void) createMap(NORMAL_DEPTH_MAP, _dePeePasses.size() == 1); + + if(_isEdgy) + { + (void) createMap(EDGE_MAP,_dePeePasses.size() == 1); + } + + if(_isColored) + { + (void) createMap(COLOR_MAP, false); + } + + return true; +} + +bool DePee::remDePeePass() +{ + if(_dePeePasses.size() < 2) + return false; + + _parent->removeChild(_dePeePasses.back()->root.get()); + delete _dePeePasses.back(); + _dePeePasses.pop_back(); + + _renderToFirst = !_renderToFirst; + + if(_isColored) + { + (void) createMap(COLOR_MAP, false); + } + + return true; +} + + +//create noise map with values ranging from 0 to 255 +bool DePee::createNoiseMap() +{ + { + osg::StateSet* stateset = _parent->getOrCreateStateSet(); + _noiseMap = new osg::Texture2D; + _noiseMap->setTextureSize(_width, _height); + _noiseMap->setFilter(osg::Texture2D::MIN_FILTER,osg::Texture2D::LINEAR); + _noiseMap->setFilter(osg::Texture2D::MAG_FILTER,osg::Texture2D::LINEAR); + stateset->setTextureAttributeAndModes(5, _noiseMap.get(), osg::StateAttribute::ON); + } + + osg::Image* image = new osg::Image; + unsigned char* data = new unsigned char[_width*_height]; + unsigned char* tmpData = new unsigned char[_width*_height]; + + int random=rand() % 5000; + for(unsigned y=0; y < _height; y++) + for(unsigned x=0; x < _width; x++) + data[y*_width + x] = (unsigned char) (0.5 * 255.0 + Utility::getNoise(x, y, random) * 0.5 * 255.0); + + //if style isn't crayon style, smooth the noise map + if(!_isCrayon) + { + for(unsigned i=0; i < 4; i++) + { + for(unsigned y=0; y < _height; y++) + for(unsigned x=0; x < _width; x++) + tmpData[y*_width + x] = (unsigned char)Utility::smoothNoise(_width, _height,x,y, data); + + for(unsigned y=0; y < _height; y++) + for(unsigned x=0; x < _width; x++) + data[y*_width + x] = (unsigned char)Utility::smoothNoise(_width, _height, x,y, tmpData); + } + } + + image->setImage(_width, _height, 1, + 1, GL_LUMINANCE, GL_UNSIGNED_BYTE, + data, + osg::Image::USE_NEW_DELETE); + _noiseMap->setImage(image); + return true; +} + +bool DePee::createHUD() +{ + osg::Geode* geode = new osg::Geode(); + + std::string timesFont("fonts/arial.ttf"); + + // turn lighting off for the text and disable depth test to ensure its always ontop. + osg::StateSet* stateset = geode->getOrCreateStateSet(); + stateset->setMode(GL_LIGHTING,osg::StateAttribute::OFF); + + stateset->setTextureAttributeAndModes(1, _normalDepthMap0.get(), osg::StateAttribute::OFF); + stateset->setTextureAttributeAndModes(2, _normalDepthMap1.get(), osg::StateAttribute::OFF); + stateset->setTextureAttributeAndModes(3, _edgeMap.get(), osg::StateAttribute::OFF); + stateset->setTextureAttributeAndModes(4, _colorMap.get(), osg::StateAttribute::OFF); + stateset->setTextureAttributeAndModes(5, _noiseMap.get(), osg::StateAttribute::OFF); + + osg::Vec3 position(5.0f,7.0f,0.0f); + osg::Vec3 delta(0.0f,-120.0f,0.0f); + + _hudText = new osgText::Text; + + { + geode->addDrawable( _hudText ); + + _hudText->setFont(timesFont); + _hudText->setPosition(position); + _hudText->setText("Head Up Display"); + _hudText->setColor(osg::Vec4(0.5, 0.5, 0.5, 1.0)); + _hudText->setCharacterSize(20.0); + position += delta; + } + + { + osg::BoundingBox bb; + for(unsigned int i=0;igetNumDrawables();++i) + { + bb.expandBy(geode->getDrawable(i)->getBound()); + } + + osg::Geometry* geom = new osg::Geometry; + + osg::Vec3Array* vertices = new osg::Vec3Array; + float depth = bb.zMin()-0.1; + vertices->push_back(osg::Vec3(bb.xMin(),bb.yMax(),depth)); + vertices->push_back(osg::Vec3(bb.xMin(),bb.yMin(),depth)); + vertices->push_back(osg::Vec3(bb.xMax(),bb.yMin(),depth)); + vertices->push_back(osg::Vec3(bb.xMax(),bb.yMax(),depth)); + geom->setVertexArray(vertices); + + osg::Vec3Array* normals = new osg::Vec3Array; + normals->push_back(osg::Vec3(0.0f,0.0f,1.0f)); + geom->setNormalArray(normals); + geom->setNormalBinding(osg::Geometry::BIND_OVERALL); + + osg::Vec4Array* colors = new osg::Vec4Array; + colors->push_back(osg::Vec4(0.0f,0.0,0.0f,0.3f)); + geom->setColorArray(colors); + geom->setColorBinding(osg::Geometry::BIND_OVERALL); + + geom->addPrimitiveSet(new osg::DrawArrays(GL_QUADS,0,4)); + + osg::StateSet* stateset = geom->getOrCreateStateSet(); + stateset->setMode(GL_BLEND,osg::StateAttribute::ON); + + stateset->setRenderingHint(osg::StateSet::TRANSPARENT_BIN); + + //geode->addDrawable(geom); + } + + osg::Camera* camera = new osg::Camera; + + // set the projection matrix + camera->setProjectionMatrix(osg::Matrix::ortho2D(0,1280,0,1024)); + + // set the view matrix + camera->setReferenceFrame(osg::Transform::ABSOLUTE_RF); + camera->setViewMatrix(osg::Matrix::identity()); + + // only clear the depth buffer + camera->setClearMask(GL_DEPTH_BUFFER_BIT); + + // draw subgraph after main camera view. + camera->setRenderOrder(osg::Camera::POST_RENDER); + + camera->addChild(geode); + + _parent->addChild(camera); + + return true; + +} + + +// then create the first camera node to do the render to texture +// render normal and depth map color map + +bool DePee::createMap(MapMode mapMode, bool first) + { + switch(mapMode) + { + case EDGE_MAP: + return createEdgeMap(first); + case NOISE_MAP: + return createNoiseMap(); + case NORMAL_DEPTH_MAP: + case COLOR_MAP: + return createNormalDepthColorMap(mapMode, first); + default: + std::cerr << "mapMode not recognized!!!\n"; + return false; + } + } + +bool DePee::createFinal() +{ + osg::Projection* screenAlignedProjectionMatrix = new osg::Projection; + + screenAlignedProjectionMatrix->setMatrix(osg::Matrix::ortho2D(0,_width,0,_height)); + screenAlignedProjectionMatrix->setCullingActive(false); + + osg::MatrixTransform* screenAlignedModelViewMatrix = new osg::MatrixTransform; + screenAlignedModelViewMatrix->setMatrix(osg::Matrix::identity()); + + // Make sure the model view matrix is not affected by any transforms + // above it in the scene graph: + screenAlignedModelViewMatrix->setReferenceFrame(osg::Transform::ABSOLUTE_RF); + + + // new we need to add the texture to the Drawable, we do so by creating a + // StateSet to contain the Texture StateAttribute. + osg::StateSet* stateset = new osg::StateSet; + + stateset->setMode(GL_DEPTH_TEST,osg::StateAttribute::OFF); + + _quadGeode->setStateSet(stateset); + + _parent->addChild(screenAlignedProjectionMatrix); + screenAlignedProjectionMatrix->addChild(screenAlignedModelViewMatrix); + screenAlignedModelViewMatrix->addChild(_quadGeode.get()); + + //setup shader + std::string vertSource; + if(!Utility::readFile("shaders/depthpeel_final.vert", vertSource)) + { + printf("shader source not found\n"); + return false; + } + + std::string fragSource; + if(!Utility::readFile("shaders/depthpeel_final.frag", fragSource)) + { + printf("shader source not found\n"); + return false; + } + + osg::ref_ptr program = new osg::Program; + program->addShader( new osg::Shader( osg::Shader::VERTEX, vertSource.c_str() ) ); + program->addShader( new osg::Shader( osg::Shader::FRAGMENT, fragSource.c_str() ) ); + + //choose map to display + stateset->addUniform( new osg::Uniform("normalDepthMap0", 1)); + stateset->addUniform( new osg::Uniform("normalDepthMap1", 2)); + stateset->addUniform(new osg::Uniform("edgeMap", 3)); + stateset->addUniform( new osg::Uniform("colorMap", 4)); + stateset->addUniform( new osg::Uniform("noiseMap", 5)); + + stateset->addUniform(_sketchy); + stateset->addUniform(_colored); + stateset->addUniform(_edgy); + stateset->addUniform(_sketchiness); + + stateset->setAttributeAndModes( program.get(), osg::StateAttribute::OVERRIDE | osg::StateAttribute::ON); + + //switch lighting off + stateset->setMode(GL_LIGHTING,osg::StateAttribute::OVERRIDE | osg::StateAttribute::OFF); + return true; +} + +bool DePee::createEdgeMap(bool first) +//create the edge map of the first normal and depth map +{ + _dePeePasses.back()->newRenderPass(EDGE_MAP); + + + // set up the background color and clear mask. + _dePeePasses.back()->Cameras[EDGE_MAP]->setClearColor(osg::Vec4(0.3,0.3f,0.3f,1.0f)); + _dePeePasses.back()->Cameras[EDGE_MAP]->setClearMask(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + const osg::BoundingSphere& bs = _quadGeode->getBound(); + if (!bs.valid()) + { + return false; + } + + float znear = 1.0f*bs.radius(); + float zfar = 3.0f*bs.radius(); + + znear *= 0.9f; + zfar *= 1.1f; + + + // set up projection. + //_dePeePasses.back()->Cameras.top()->setProjectionMatrixAsFrustum(-proj_right,proj_right,-proj_top,proj_top,znear,zfar); + _dePeePasses.back()->Cameras[EDGE_MAP]->setProjectionMatrixAsOrtho(0,_width,0,_height,znear,zfar); + + //set view + _dePeePasses.back()->Cameras[EDGE_MAP]->setReferenceFrame(osg::Transform::ABSOLUTE_RF); + + _dePeePasses.back()->Cameras[EDGE_MAP]->setViewMatrixAsLookAt(osg::Vec3(0.0f,0.0f,2.0f)*bs.radius(), osg::Vec3(0.0,0.0,0.0),osg::Vec3(0.0f,1.0f,0.0f)); + + // set viewport + _dePeePasses.back()->Cameras[EDGE_MAP]->setViewport(0,0,_texWidth,_texHeight); + + // set the camera to render before the main camera. + _dePeePasses.back()->Cameras[EDGE_MAP]->setRenderOrder(osg::Camera::PRE_RENDER); + + // tell the camera to use OpenGL frame buffer object + _dePeePasses.back()->Cameras[EDGE_MAP]->setRenderTargetImplementation(osg::Camera::FRAME_BUFFER); + + //switch lighting off + osg::ref_ptr stateset = new osg::StateSet; + + + if(_renderToFirst) + { + stateset->addUniform(new osg::Uniform("normalDepthMap", 1)); + } + else + { + stateset->addUniform(new osg::Uniform("normalDepthMap", 2)); + } + + _dePeePasses.back()->Cameras[EDGE_MAP]->attach(osg::Camera::COLOR_BUFFER, _edgeMap.get()); + stateset->addUniform( new osg::Uniform("edgeMap", 3)); + + stateset->setMode(GL_LIGHTING,osg::StateAttribute::OVERRIDE | + osg::StateAttribute::OFF); + //setup shader + stateset->setAttributeAndModes(_edgeMapProgram.get(), osg::StateAttribute::OVERRIDE | osg::StateAttribute::ON); + stateset->addUniform(new osg::Uniform("width", (float) _width)); + stateset->addUniform(new osg::Uniform("height", (float) _height)); + + if(first) + { + stateset->addUniform(new osg::Uniform("first", (float)1.0)); + } + else + { + stateset->addUniform(new osg::Uniform("first", (float)0.0)); + } + _dePeePasses.back()->settingNodes[EDGE_MAP]->setStateSet(stateset.get()); + + // add subgraph to render + assert(_dePeePasses.size() > 0); + + _dePeePasses.back()->settingNodes[EDGE_MAP]->addChild(_quadGeode.get()); + + return true; +} + + +bool DePee::createNormalDepthColorMap(MapMode mapMode, bool first) +{ + DePeePass* pass; + + pass = _dePeePasses.back(); + + pass->newRenderPass(mapMode); + + // + // setup camera + // + + // set up the background color and clear mask + pass->Cameras[mapMode]->setClearColor(osg::Vec4(0.f,0.f,1.f,1.f)); + pass->Cameras[mapMode]->setClearMask(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + const osg::BoundingSphere& bs = _subgraph->getBound(); + if (!bs.valid()) + { + return false; + } + + float znear = 1.0f*bs.radius(); + float zfar = 3.0f*bs.radius(); + + // 2:1 aspect ratio as per flag geomtry below. + float projTop = 0.25f*znear; + float projRight = projTop * ((double)_width/(double)_height); + + znear *= 0.9f; + zfar *= 1.1f; + + // set up projection. + pass->Cameras[mapMode]->setProjectionMatrixAsFrustum(-projRight,projRight,-projTop,projTop, znear,zfar); + + // setup view + pass->Cameras[mapMode]->setReferenceFrame(osg::Transform::ABSOLUTE_RF); + + pass->Cameras[mapMode]->setViewMatrixAsLookAt(bs.center()-osg::Vec3(0.0f,2.0f,0.0f)*bs.radius(), + bs.center(), + osg::Vec3(0.0f,0.0f,1.0f)); + // set viewport + pass->Cameras[mapMode]->setViewport(0,0,_texWidth,_texHeight); + + // set the camera to render before the main camera. + pass->Cameras[mapMode]->setRenderOrder(osg::Camera::PRE_RENDER); + + pass->Cameras[mapMode]->setRenderTargetImplementation(osg::Camera::FRAME_BUFFER_OBJECT); + + // + // setup stateset + // + //switch lighting off + osg::ref_ptr stateset = new osg::StateSet; + + stateset->setMode(GL_LIGHTING,osg::StateAttribute::OVERRIDE | + osg::StateAttribute::OFF); + + switch(mapMode) + { + case NORMAL_DEPTH_MAP: + + _renderToFirst = !_renderToFirst; + + if(_renderToFirst) + { + pass->Cameras[mapMode]->attach(osg::Camera::COLOR_BUFFER, _normalDepthMap0.get()); + stateset->addUniform(new osg::Uniform("normalDepthMap", 2)); + } + else + { + pass->Cameras[mapMode]->attach(osg::Camera::COLOR_BUFFER, _normalDepthMap1.get()); + stateset->addUniform(new osg::Uniform("normalDepthMap", 1)); + } + + stateset->setMode(GL_LIGHTING,osg::StateAttribute::OVERRIDE | osg::StateAttribute::OFF); + stateset->setAttributeAndModes(_normalDepthMapProgram.get(), osg::StateAttribute::OVERRIDE | osg::StateAttribute::ON); + break; + + case COLOR_MAP: + + assert(pass == _dePeePasses.back()); + pass->Cameras[mapMode]->attach(osg::Camera::COLOR_BUFFER, _colorMap.get()); + + + if(_renderToFirst) + { + stateset->addUniform(new osg::Uniform("normalDepthMap", 1)); + } + else + { + stateset->addUniform(new osg::Uniform("normalDepthMap", 2)); + } + pass->Cameras[mapMode]->setClearColor(osg::Vec4(1.f,1.f,1.f,1.f)); + stateset->setMode(GL_LIGHTING,osg::StateAttribute::OVERRIDE | osg::StateAttribute::OFF); + stateset->setMode(GL_BLEND, osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE); + stateset->setAttributeAndModes(_colorMapProgram.get(), osg::StateAttribute::OVERRIDE | osg::StateAttribute::ON); + stateset->addUniform(new osg::Uniform("tex", 0)); + + break; + default: + return false; + }; + + // add subgraph to render + + pass->settingNodes[mapMode]->addChild(_subgraph.get()); + + stateset->addUniform(new osg::Uniform("first", first)); + + stateset->addUniform(new osg::Uniform("width", (float) _width)); + stateset->addUniform(new osg::Uniform("height", (float) _height)); + stateset->addUniform(new osg::Uniform("znear", znear)); + stateset->addUniform(new osg::Uniform("zfar", zfar)); + + + pass->settingNodes[mapMode]->setStateSet(stateset.get()); + + return true; +} + + +bool DePee::updateHUDText() +{ + if(!_fps) + return false; + std::string str; + std::string tmp = Utility::toString(*_fps); + unsigned i = tmp.find_first_of('.'); + tmp = tmp.substr(0, i + 3); + _hudText->setText(Utility::toString(_dePeePasses.size()) + + " Depth Peeling Pass" + (_dePeePasses.size() == 1 ? " " : "es ") + + "((a)dd; (r)emove) " + + (_isEdgy ? "+" : "-") + "(E)dgy " + + + (_isSketchy ? "+" : "-") + "(S)ketchy " + + + (_isColored ? "+" : "-") + "(C)olored " + + + "-> "+Utility::toString(getNumberOfRenderPasses())+ " Rendering Passes " + + "@ " + + tmp + " fps"); + return true; +} diff --git a/examples/osgdepthpeeling/DePee.h b/examples/osgdepthpeeling/DePee.h new file mode 100644 index 000000000..ce46c9850 --- /dev/null +++ b/examples/osgdepthpeeling/DePee.h @@ -0,0 +1,170 @@ +/* + Steffen Frey + Fachpraktikum Graphik-Programmierung 2007 + Institut fuer Visualisierung und Interaktive Systeme + Universitaet Stuttgart + */ + +#ifndef _DEPEE_H_ +#define _DEPEE_H_ + +#include +#include +#include +#include +#include +#include +#include + +#include "DePeePass.h" + +/*! + The DePee class is main class for setting up and managing depth peeling. + A DePee object can be seen as a virtual node, that has one parent and one child. The rendering of every child and subchil of this child is managed by the the DePee node. Besides that, it handles a head up display. + */ +class DePee : public osg::Referenced +{ +public: + /*! + The constructor is initialized by giving it a parent and child node (subgraph), as well as the width and height in pixels of the output window. Additionally a subgraph can be added whose children aren't depth peeled but combined with de depth peeled scene + */ + DePee(osg::Group* parent, osg::Group* subgraph, unsigned width, unsigned height); + /*! + Takes care of clean removal of DePee + */ + ~DePee(); + + /*! + The head up display shows information like internal status and current frames per second. This function needs to be called in the rendering loop to keep the information updated. + */ + bool updateHUDText(); + + /*! + Sets whether sketchiness is activated or deactivated + */ + void setSketchy(bool sketchy); + + /*! + If sketchiness is enabled, sets whether a crayon should be used + */ + void setCrayon(bool crayon); + + /*! + Sets whether color display is activated or deactivated + */ + void setColored(bool colored); + + /*! + Sets whether edges are displayed or not + */ + void setEdgy(bool edgy); + + /*! + Sets how sketchy lines and colors should be displayed (standard is 1.0) + */ + void setSketchiness(double sketchiness); + + /*! + Set the pointer to the double variable containing the current fps for displaying it on the head up display + */ + void setFPS(double* fps); + + /*! + Add a depth peeling pass and adjust the render passes accordingly + */ + bool addDePeePass(); + + /*! + Remove a depth peeling pass and adjust the render passes accordingly + */ + bool remDePeePass(); + +private: + /*! + Create a map. This is a function for convenience and calls either + createNoiseMap(), createEdgeMap() or createNormalDepthColorMap(). + Apart from NOISE_MAP, for every texture generation + one rendering pass is needed. + The boolean first is used to indicate whether this rendering pass + belongs to the first depth peeling pass. + */ + bool createMap(MapMode mapMode, bool first=false); + + /*! + Creates a two dimensional noise map and initalizes _noiseMap with it + */ + bool createNoiseMap(); + + /*! + Depending on the chosen MapMode, it either creates a new rendering + pass for creaeting a normal, depth or color map. The created rendering + pass is added to the current depth peeling pass. + */ + bool createNormalDepthColorMap(MapMode mapMode, bool first); + + /*! + Create an edge map. A previous depth and normal rendering pass in this + depth peeling pass is required for that. + */ + bool createEdgeMap(bool first); + + /*! + Creates the final rendering pass for depth peeling. Color and edge map are + added up here and sketchiness is applied. + */ + bool createFinal(); + + /*! + Create the rendering pass for the head up display + */ + bool createHUD(); + + /* + Returns the number of rendering passes of the depth peeling object + */ + unsigned int getNumberOfRenderPasses(); + + + unsigned _texWidth; + unsigned _texHeight; + unsigned _width; + unsigned _height; + + osg::ref_ptr _parent; + osg::ref_ptr _subgraph; + osg::ref_ptr _noiseMap; + osg::ref_ptr _normalDepthMap0; + osg::ref_ptr _normalDepthMap1; + + osg::ref_ptr _edgeMap; + + osg::ref_ptr _colorMap; + + osg::ref_ptr _quadGeode; + + osgText::Text* _hudText; + double* _fps; + + std::vector _dePeePasses; + + osg::Uniform* _sketchy; + osg::Uniform* _colored; + osg::Uniform* _edgy; + osg::Uniform* _sketchiness; + + bool _isSketchy; + bool _isColored; + bool _isEdgy; + bool _isCrayon; + + osg::Camera* _colorCamera; + + //shader programs + osg::ref_ptr _normalDepthMapProgram; + osg::ref_ptr _colorMapProgram; + osg::ref_ptr _edgeMapProgram; + + bool _renderToFirst; +}; + +#endif diff --git a/examples/osgdepthpeeling/DePeePass.cpp b/examples/osgdepthpeeling/DePeePass.cpp new file mode 100644 index 000000000..a3e9a4c98 --- /dev/null +++ b/examples/osgdepthpeeling/DePeePass.cpp @@ -0,0 +1,49 @@ +/* + Steffen Frey + Fachpraktikum Graphik-Programmierung 2007 + Institut fuer Visualisierung und Interaktive Systeme + Universitaet Stuttgart + */ + +#include "DePeePass.h" + +#include +#include + +DePeePass::DePeePass() +{ + root = new osg::Group; +} + +DePeePass::~DePeePass() +{ + root->releaseGLObjects(); + assert(Cameras.size() == settingNodes.size()); + while(!Cameras.empty()) + { + remRenderPass((*Cameras.begin()).first); + } +} + +void DePeePass::newRenderPass(MapMode mapMode) +{ + Cameras[mapMode] = new osg::Camera; + settingNodes[mapMode] = new osg::Group; + root->addChild(Cameras[mapMode].get()); + Cameras[mapMode]->addChild(settingNodes[mapMode].get()); +} + +void DePeePass::remRenderPass(MapMode mapMode) +{ + assert(Cameras.find(mapMode) != Cameras.end()); + Cameras[mapMode]->releaseGLObjects(); + settingNodes[mapMode]->releaseGLObjects(); + + Cameras[mapMode]->removeChild(settingNodes[mapMode].get()); + //setting Nodes have exactly one child + assert(settingNodes[mapMode]->getNumChildren() == 1); + settingNodes[mapMode]->removeChild(0,1); + + Cameras.erase(Cameras.find(mapMode)); + settingNodes.erase(settingNodes.find(mapMode)); +} diff --git a/examples/osgdepthpeeling/DePeePass.h b/examples/osgdepthpeeling/DePeePass.h new file mode 100644 index 000000000..d80c3c168 --- /dev/null +++ b/examples/osgdepthpeeling/DePeePass.h @@ -0,0 +1,56 @@ +/* + Steffen Frey + Fachpraktikum Graphik-Programmierung 2007 + Institut fuer Visualisierung und Interaktive Systeme + Universitaet Stuttgart + */ + +#ifndef _DEPEEPASS_H_ +#define _DEPEEPASS_H_ + +#include +#include +#include +#include + +/*! + MapMode specifies the kind of texture maps that can be generated for later + usage + */ +enum MapMode {NORMAL_DEPTH_MAP, COLOR_MAP, EDGE_MAP, NOISE_MAP}; + +/*! + DePeePass can be seen as a mera data structure and typically used by + the class DePee. It represents one depth peeling pass and is initialized + by functions in the DePee class, but cleans itself up. + Please note, that no texture generation mode is allowed to appear twice +*/ +class DePeePass +{ + public: + /*! + Constructor + */ + DePeePass(); + + /*! + Desctructor cleans the whole depth peeling pass + */ + ~DePeePass(); + + /*! + Make data structure ready for incorporating a new rendering pass + */ + void newRenderPass(MapMode mapMode); + + /*! + Clean up the specified rendering pass + */ + void remRenderPass(MapMode mapMode); + + osg::ref_ptr root; + std::map > Cameras; + std::map > settingNodes; +}; + +#endif diff --git a/examples/osgdepthpeeling/Utility.cpp b/examples/osgdepthpeeling/Utility.cpp new file mode 100644 index 000000000..c84e7d88f --- /dev/null +++ b/examples/osgdepthpeeling/Utility.cpp @@ -0,0 +1,152 @@ +/* + Steffen Frey + Fachpraktikum Graphik-Programmierung 2007 + Institut fuer Visualisierung und Interaktive Systeme + Universitaet Stuttgart + */ + +#include "Utility.h" + +#include +#include +#include +#include +#include +#include + +bool Utility::readFile(char* fName, std::string& s) +{ + std::string foundFile = osgDB::findDataFile(fName); + if (foundFile.empty()) return false; + + std::ifstream is;//(fName); + is.open(foundFile.c_str()); + if (is.fail()) + { + std::cerr << "Could not open " << fName << " for reading.\n"; + return false; + } + char ch = is.get(); + while (!is.eof()) + { + s += ch; + ch = is.get(); + } + is.close(); + return true; +} + +std::string Utility::toString(double d) +{ + std::stringstream ostr; + ostr << d; + return ostr.str(); +} + +osg::Program* Utility::createProgram(std::string vs, std::string fs) +{ + //setup shader + std::string vertSource; + if(!readFile((char*)vs.c_str(), vertSource)) + { + printf("shader source not found\n"); + return false; + } + + std::string fragSource; + if(!readFile((char*)fs.c_str(), fragSource)) + { + printf("shader source not found\n"); + return false; + } + + + osg::Program* program = new osg::Program; + program->addShader( new osg::Shader( osg::Shader::VERTEX, vertSource.c_str() ) ); + program->addShader( new osg::Shader( osg::Shader::FRAGMENT, fragSource.c_str() ) ); + return program; +} + +double Utility::getNoise(unsigned x, unsigned y, unsigned random) +{ + int n = x + y * 57 + random * 131; + n = (n<<13) ^ n; + double noise = (1.0f - ( (n * (n * n * 15731 + 789221) + + 1376312589)&0x7fffffff)* 0.000000000931322574615478515625f); + return noise; +} + +double Utility::smoothNoise(unsigned width, unsigned height, unsigned x, unsigned y, unsigned char* noise) +{ + assert(noise); + + if(x==0 || x > width -2 + || y==0 || y > height -2) + return noise[x + y*width]; + + double corners = (noise[x-1 + (y-1) *width] + +noise[x+1 + (y-1)*width] + +noise[x-1 + (y+1) * width] + +noise[x+1 + (y+1) * width]) / 16.0; + double sides = (noise[x-1 + y*width] + +noise[x+1 + y*width] + +noise[x + (y-1)*width] + +noise[x + (y+1)*width]) / 8.0; + double center = noise[x + y*width] / 4.0; + + return corners + sides + center; +} + +osg::Texture2D* Utility::newColorTexture2D(unsigned width, unsigned height, unsigned accuracy) +{ + osg::Texture2D* texture2D = new osg::Texture2D; + + texture2D->setTextureSize(width, height); + if(accuracy == 32) + { + texture2D->setInternalFormat(GL_RGBA32F_ARB); + texture2D->setSourceFormat(GL_RGBA); + } + else if(accuracy == 8) + { + texture2D->setInternalFormat(GL_RGBA); + } + texture2D->setSourceType(GL_FLOAT); + texture2D->setFilter(osg::Texture2D::MIN_FILTER,osg::Texture2D::LINEAR); + texture2D->setFilter(osg::Texture2D::MAG_FILTER,osg::Texture2D::LINEAR); + return texture2D; +} + +osg::Geode* Utility::getCanvasQuad(unsigned width, unsigned height, double depth) +{ + osg::Vec3Array* vertices = new osg::Vec3Array; + osg::Vec2Array* texCoords = new osg::Vec2Array; + vertices->push_back(osg::Vec3(0,0,depth)); + texCoords->push_back(osg::Vec2(0,0)); + + vertices->push_back(osg::Vec3(width,0,depth)); + texCoords->push_back(osg::Vec2(1,0)); + + vertices->push_back(osg::Vec3(0,height,depth)); + texCoords->push_back(osg::Vec2(0,1)); + + vertices->push_back(osg::Vec3(width,height,depth)); + texCoords->push_back(osg::Vec2(1,1)); + + osg::Geometry* quad = new osg::Geometry; + quad->setVertexArray(vertices); + quad->setTexCoordArray(1,texCoords); + + quad->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::QUAD_STRIP,0,vertices->size())); + + osg::Vec4Array* colors = new osg::Vec4Array; + colors->push_back(osg::Vec4(1.0f,1.0f,1.0f,1.0f)); + quad->setColorArray(colors); + quad->setColorBinding(osg::Geometry::BIND_OVERALL); + + osg::Geode* geode = new osg::Geode(); + geode->addDrawable(quad); + + return geode; + +} diff --git a/examples/osgdepthpeeling/Utility.h b/examples/osgdepthpeeling/Utility.h new file mode 100644 index 000000000..6efcdc3c7 --- /dev/null +++ b/examples/osgdepthpeeling/Utility.h @@ -0,0 +1,57 @@ +/* + Steffen Frey + Fachpraktikum Graphik-Programmierung 2007 + Institut fuer Visualisierung und Interaktive Systeme + Universitaet Stuttgart + */ + + +#ifndef _UTILITY_H_ +#define _UTILITY_H_ + +#include +#include +#include +#include + +namespace Utility +{ +/*! + Reads a file and returns a string + */ +bool readFile(char* fName, std::string& s); + +/*! + Converts a number to a string + */ +std::string toString(double d); + +/*! + Create a osg shader program consisting of a vertex shader and a + fragment shader + */ +osg::Program* createProgram(std::string vs, std::string fs); + +/*! +This is a random generator to generate noise patterns. +The returned values range from -1 to 1 +*/ +double getNoise(unsigned x, unsigned y, unsigned random); + +/*! + Returns a smoothed noise version of the value that is read from the noise + texture + */ +double smoothNoise(unsigned width, unsigned height, unsigned x, unsigned y, unsigned char* noise); + +/*! + Creates a two dimensional color texture and apply some standard settings + */ + osg::Texture2D* newColorTexture2D(unsigned width, unsigned height, unsigned accuracy); + +/*! + Get a quad with screen size in order to show a texture full screen + */ +osg::Geode* getCanvasQuad(unsigned width, unsigned height, double depth=-1); +}; +#endif diff --git a/examples/osgdepthpeeling/osgdepthpeeling.cpp b/examples/osgdepthpeeling/osgdepthpeeling.cpp new file mode 100644 index 000000000..bce7f69b5 --- /dev/null +++ b/examples/osgdepthpeeling/osgdepthpeeling.cpp @@ -0,0 +1,328 @@ +/* + Steffen Frey + Fachpraktikum Graphik-Programmierung 2007 + Institut fuer Visualisierung und Interaktive Systeme + Universitaet Stuttgart + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include "DePee.h" + +/*! + Handles keyboard events. + Maintains a copy of the DePee object and part of its internal state + Used for example to set sketchiness, color, add or remove a depth peeling pass + */ +class KeyboardEventHandler : public osgGA::GUIEventHandler +{ +public: + + KeyboardEventHandler(DePee* dePee) + { + _apc = 0; + _dePee = dePee; + _sketchy = false; + _sketchiness = 1.0; + _colored = false; + _edgy = true; + _crayon = false; + _dePee->setSketchy(_sketchy); + _dePee->setColored(_colored); + } + + virtual bool handle(const osgGA::GUIEventAdapter& ea,osgGA::GUIActionAdapter&) + { + switch(ea.getEventType()) + { + + case(osgGA::GUIEventAdapter::KEYDOWN): + { + if (ea.getKey()==osgGA::GUIEventAdapter::KEY_Space) + { + if(_apc) + _apc->setPause(!_apc->getPause()); + return true; + } + else if (ea.getKey() == 'a') + { + _dePee->addDePeePass(); + return true; + } + else if (ea.getKey() == 'r') + { + _dePee->remDePeePass(); + return true; + } + else if (ea.getKey() == 'c') + { + _colored = !_colored; + _dePee->setColored(_colored); + return true; + } + else if (ea.getKey() == 's') + { + _sketchy = !_sketchy; + _dePee->setSketchy(_sketchy); + return true; + } + + else if (ea.getKey() == 'e') + { + _edgy = !_edgy; + _dePee->setEdgy(_edgy); + return true; + } + else if (ea.getKey() == 'f') + { + return true; + } + else if (ea.getKey() == '+') + { + _sketchiness += 0.5; + _dePee->setSketchiness(_sketchiness); + } + else if (ea.getKey() == '-') + { + _sketchiness -= 0.5; + if(_sketchiness < 0.0) + _sketchiness = 0.0; + _dePee->setSketchiness(_sketchiness); + } + + else if (ea.getKey() == 'y') + { + _crayon = !_crayon; + _dePee->setCrayon(_crayon); + } + + break; + } + + default: + break; + + } + return false; + } + void registerAnimationPathCallback(osg::AnimationPathCallback* apc) + { + _apc = apc; + } +private: + DePee* _dePee; + bool _sketchy; + bool _colored; + bool _edgy; + bool _crayon; + double _sketchiness; + osg::AnimationPathCallback* _apc; +}; + + +/*! + Handles mouse events. + Maintains a copy of the DePee object and part of its internal state + Used to rotate the object + */ +class MouseEventHandler : public osgGA::GUIEventHandler +{ +public: + + MouseEventHandler(DePee* dePee) + { + _dePee = dePee; + } + + virtual bool handle(const osgGA::GUIEventAdapter& ea,osgGA::GUIActionAdapter&) + { + switch(ea.getEventType()) + { + //mouse + case(osgGA::GUIEventAdapter::DRAG): + { + rotate(ea.getXnormalized(), ea.getYnormalized()); + break; + } + case(osgGA::GUIEventAdapter::MOVE): + _prevX = ea.getXnormalized(); + _prevY = ea.getYnormalized(); + break; + + default: + break; + + } + return false; + } + void registerModelGroupTransform(osg::MatrixTransform* modelGroupTransform) + { + _modelGroupTransform = modelGroupTransform; + _rotCenter = modelGroupTransform->getBound().center(); + } +private: + void rotate(float x, float y) + { + osg::Matrixd baseMatrix = _modelGroupTransform->getMatrix(); + + osg::Matrixd preTransMatrix; + osg::Matrixd postTransMatrix; + osg::Matrixd rotMatrixX; + osg::Matrixd rotMatrixZ; + + preTransMatrix.makeTranslate(_rotCenter); + postTransMatrix.makeTranslate(-_rotCenter); + + rotMatrixZ.makeRotate((x - _prevX) * 3., osg::Vec3d(0.0, 0.0,1.0)); + + baseMatrix.preMult(preTransMatrix); + baseMatrix.preMult(rotMatrixZ); + baseMatrix.preMult(postTransMatrix); + + rotMatrixX.makeRotate(-(y - _prevY) * 3., (baseMatrix * osg::Vec3d(1.0, 0.0,0.0))); + + baseMatrix.preMult(preTransMatrix); + baseMatrix.preMult(rotMatrixX); + baseMatrix.preMult(postTransMatrix); + + _modelGroupTransform->setMatrix(baseMatrix); + + _prevX = x; + _prevY = y; + }; + + DePee* _dePee; + + float _prevX; + float _prevY; + + osg::Vec3 _rotCenter; + osg::MatrixTransform* _modelGroupTransform; +}; + + + +int main( int argc, char **argv ) +{ + // use an ArgumentParser object to manage the program arguments. + osg::ArgumentParser arguments(&argc,argv); + + // set up the usage document, in case we need to print out how to use this program. + arguments.getApplicationUsage()->setDescription(arguments.getApplicationName()+" is the example which demonstrates Depth Peeling"); + arguments.getApplicationUsage()->setCommandLineUsage(arguments.getApplicationName()+" filename"); + + + // construct the viewer + osgViewer::Viewer viewer(arguments); + + // any option left unread are converted into errors to write out later. + arguments.reportRemainingOptionsAsUnrecognized(); + + // report any errors if they have occured when parsing the program aguments. + if (arguments.errors()) + { + arguments.writeErrorMessages(std::cout); + return 1; + } + + if (arguments.argc()<=1 || arguments.argc() > 3) + { + arguments.getApplicationUsage()->write(std::cout,osg::ApplicationUsage::COMMAND_LINE_OPTION); + return 1; + } + + + //only displays a textured quad + viewer.getCamera()->setComputeNearFarMode(osg::CullSettings::DO_NOT_COMPUTE_NEAR_FAR); + + // read the model to do depth peeling with + osg::Node* loadedModel = osgDB::readNodeFile(arguments.argv()[1]); + + if (!loadedModel) + return 1; + + // create a transform to spin the model. + osg::MatrixTransform* modelGroupTransform = new osg::MatrixTransform; + osg::Group* modelGroup = new osg::Group; + modelGroupTransform->addChild(modelGroup); + modelGroup->addChild(loadedModel); + + osg::Group* rootNode = new osg::Group(); + + // add model to the viewer. + viewer.setSceneData(rootNode); + + // Depth peel example only works on a single graphics context right now + // so open up viewer on single screen to prevent problems + viewer.setUpViewOnSingleScreen(0); + + // create the windows and run the threads. + viewer.realize(); + + unsigned int width = 1280; + unsigned int height = 1280; + osgViewer::Viewer::Windows windows; + viewer.getWindows(windows); + if (!windows.empty()) + { + width = windows.front()->getTraits()->width; + height = windows.front()->getTraits()->height; + } + + + osg::ref_ptr dePee = new DePee(rootNode, + modelGroupTransform, + width, + height); + + //create event handlers + KeyboardEventHandler* keyboardEventHandler = new KeyboardEventHandler(dePee.get()); + MouseEventHandler* mouseEventHandler = new MouseEventHandler(dePee.get()); + viewer.addEventHandler(keyboardEventHandler); + viewer.addEventHandler(mouseEventHandler); + + //viewer.setCameraManipulator(new osgGA::TrackballManipulator); + + osg::StateSet* stateset = modelGroupTransform->getOrCreateStateSet(); + + stateset->setMode(GL_BLEND, osg::StateAttribute::OFF); + + //create new animation callback for autmatic object rotation + osg::AnimationPathCallback* apc = new osg::AnimationPathCallback(modelGroupTransform->getBound().center(),osg::Vec3(0.0f,0.0f,1.0f),osg::inDegrees(45.0f)); + apc->setPause(true); + modelGroupTransform->setUpdateCallback(apc); + + keyboardEventHandler->registerAnimationPathCallback(apc); + mouseEventHandler->registerModelGroupTransform(modelGroupTransform); + + //setup stuff that is necessary for measuring fps + osg::Timer_t current_tick, previous_tick = 1; + double* fps = new double; + dePee->setFPS(fps); + + while(!viewer.done()) + { + osg::Timer_t current_tick = osg::Timer::instance()->tick(); + + *fps = 1.0/osg::Timer::instance()->delta_s(previous_tick,current_tick); + dePee->updateHUDText(); + + previous_tick = current_tick; + + // fire off the cull and draw traversals of the scene. + viewer.frame(); + } + + return 0; +}