Added osgdepthpeeling example

This commit is contained in:
Robert Osfield
2007-09-03 12:27:37 +00:00
parent de19fb3737
commit 7b73a58728
9 changed files with 1497 additions and 0 deletions

View File

@@ -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)

View File

@@ -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)

View File

@@ -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 <osg/GLExtensions>
#include <osg/Node>
#include <osg/MatrixTransform>
#include <osg/Projection>
#include <osg/Geode>
#include "Utility.h"
#include <osg/ShapeDrawable>
#include <osg/Geometry>
#include <assert.h>
#include <iostream>
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;i<geode->getNumDrawables();++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<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() ) );
//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<osg::StateSet> 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<osg::StateSet> 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;
}

View File

@@ -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 <osg/Node>
#include <osg/Camera>
#include <osg/Group>
#include <osg/Texture2D>
#include <osgText/Text>
#include <string>
#include <stack>
#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<osg::Group> _parent;
osg::ref_ptr<osg::Group> _subgraph;
osg::ref_ptr<osg::Texture2D> _noiseMap;
osg::ref_ptr<osg::Texture2D> _normalDepthMap0;
osg::ref_ptr<osg::Texture2D> _normalDepthMap1;
osg::ref_ptr<osg::Texture2D> _edgeMap;
osg::ref_ptr<osg::Texture2D> _colorMap;
osg::ref_ptr<osg::Geode> _quadGeode;
osgText::Text* _hudText;
double* _fps;
std::vector<DePeePass*> _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<osg::Program> _normalDepthMapProgram;
osg::ref_ptr<osg::Program> _colorMapProgram;
osg::ref_ptr<osg::Program> _edgeMapProgram;
bool _renderToFirst;
};
#endif

View File

@@ -0,0 +1,49 @@
/*
Steffen Frey
Fachpraktikum Graphik-Programmierung 2007
Institut fuer Visualisierung und Interaktive Systeme
Universitaet Stuttgart
*/
#include "DePeePass.h"
#include <iostream>
#include <assert.h>
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));
}

View File

@@ -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 <map>
#include <osg/Node>
#include <osg/Camera>
#include <osg/Group>
/*!
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<osg::Group> root;
std::map<MapMode, osg::ref_ptr<osg::Camera> > Cameras;
std::map<MapMode, osg::ref_ptr<osg::Group> > settingNodes;
};
#endif

View File

@@ -0,0 +1,152 @@
/*
Steffen Frey
Fachpraktikum Graphik-Programmierung 2007
Institut fuer Visualisierung und Interaktive Systeme
Universitaet Stuttgart
*/
#include "Utility.h"
#include <assert.h>
#include <iostream>
#include <fstream>
#include <osg/Geometry>
#include <osg/Geode>
#include <osgDB/FileUtils>
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;
}

View File

@@ -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 <string>
#include <sstream>
#include <osg/Program>
#include <osg/Texture2D>
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

View File

@@ -0,0 +1,328 @@
/*
Steffen Frey
Fachpraktikum Graphik-Programmierung 2007
Institut fuer Visualisierung und Interaktive Systeme
Universitaet Stuttgart
*/
#include <osg/GLExtensions>
#include <osg/Node>
#include <osg/Geometry>
#include <osg/Notify>
#include <osg/MatrixTransform>
#include <osg/AnimationPath>
#include <osgDB/ReadFile>
#include <osgViewer/Viewer>
#include <osgGA/TrackballManipulator>
#include <iostream>
#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> 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;
}