Hi Mathias --
OK, final flip-flop: I really did encounter a NULL viewport problem. It
occurs on a single GPU dual-head Win32 configuration. The viewport comes
back NULL in this case, and the app crashes. Oddly, if I just pass in
zero for the width and height, the app works for the most part. Seems to
have some flicker in this case though. I've attached my code, which
might still require some modification in the NULL viewport case.
The attached code also contains the compile fix for Windows math routines.
And I also changed the clear color to white, so you might want to change
that back. I noticed the clear color is hard-coded, so I assume this is
unfinished code that you're going to modify anyway?
Thanks for posting this and letting me look at it, very informative.
-Paul
Mathias Fröhlich wrote:
Hi Robert,
I have now put together what I have for the order independent transparency or
short oit. This rendering technique is also known as depth peeling.
Attached is the example that makes depth peeling work with the fixed function
pipeline. Ok, this is 'old fashioned' but required for our use case that
still has to work on older UNIX OpenGL implementations as well as together
with a whole existing application making use of the fixed function pipeline.
I can imagine to add support for shaders when we have that shader composition
framework where we can add a second depth test in a generic way.
This does *not* implement the dual depth peeling described in a paper from the
ETH Zürich.
This example could serve as a test case for the feature that you can on the
fly remove pre render cameras that you made work a few time ago.
It is also a test case for the new TraversalOrderBin that is used to composite
the depth layers in the correct blend order.
This example also stresses your new texture object cache since you can change
some parameters for the oit implementation at runtime.
You can just load any model with osgoit and see how it works.
Use the usual help key to see what you can change.
There is already an osgdepthpeeling example that I could not really make sense
of up to now. So I just made something new without touching what I do not
understand.
Can you add this small example to the examples directory please?
Thanks!
Mathias
------------------------------------------------------------------------
_______________________________________________
osg-submissions mailing list
[email protected]
http://lists.openscenegraph.org/listinfo.cgi/osg-submissions-openscenegraph.org
#include <osg/Array>
#include <osg/AlphaFunc>
#include <osg/BlendFunc>
#include <osg/Depth>
#include <osg/Geode>
#include <osg/Geometry>
#include <osg/Vec3>
#include <osg/MatrixTransform>
#include <osg/Texture2D>
#include <osg/TextureRectangle>
#include <osg/TexGen>
#include <osg/TexEnv>
#include <osg/TexMat>
#include <osg/TexGenNode>
#include <osgDB/ReadFile>
#include <osgViewer/Viewer>
#include <osgViewer/ViewerEventHandlers>
#include <osg/Math>
#include <limits>
#include <iostream>
// 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<typename T>
inline T
nextPowerOfTwo(T k)
{
if (k == T(0))
return 1;
k--;
for (int i = 1; i < std::numeric_limits<T>::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),
_stateSet(new osg::StateSet),
_texMat(new osg::TexMat)
{
_stateSet->setTextureAttribute(_texUnit, _texMat.get());
}
virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
{
osgUtil::CullVisitor* cullVisitor =
static_cast<osgUtil::CullVisitor*>(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)));
_texMat->setMatrix(m);
cullVisitor->pushStateSet(_stateSet.get());
traverse(node, nv);
cullVisitor->popStateSet();
}
private:
unsigned _texUnit;
unsigned _texWidth;
unsigned _texHeight;
unsigned _offsetValue;
osg::ref_ptr<osg::StateSet> _stateSet;
osg::ref_ptr<osg::TexMat> _texMat;
};
void
createPeeling()
{
int numTiles = ceil(sqrt((double)_numPasses));
_root->removeChildren(0, _root->getNumChildren());
_colorTextures.clear();
_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->setClearColor(osg::Vec4( 1,1,1,1 ) );//0.2, 0.2, 0.4,
1));
_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<osg::Texture> depthTexture = _depthTextures[i%2];
osg::ref_ptr<osg::Texture> 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<osg::TextureRectangle> colorTexture = new
osg::TextureRectangle;
#else
osg::ref_ptr<osg::Texture2D> 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),
_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 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;
unsigned _offsetValue;
// The root node that is handed over to the viewer
osg::ref_ptr<osg::Group> _root;
// The scene that is displayed
osg::ref_ptr<osg::Group> _scene;
// The final camera that composites the pre rendered textures to the final
picture
osg::ref_ptr<osg::Camera> _compositeCamera;
#ifdef USE_TEXTURE_RECTANGLE
osg::ref_ptr<osg::TextureRectangle> _depthTextures[2];
std::vector<osg::ref_ptr<osg::TextureRectangle> > _colorTextures;
#else
osg::ref_ptr<osg::Texture2D> _depthTextures[2];
std::vector<osg::ref_ptr<osg::Texture2D> > _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 '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> _depthPeeling;
};
int main(int argc, char** argv)
{
// use an ArgumentParser object to manage the program arguments.
osg::ArgumentParser arguments(&argc, argv);
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");
arguments.getApplicationUsage()->addKeyboardMouseBinding("p", "Increase the
layer offset");
arguments.getApplicationUsage()->addKeyboardMouseBinding("o", "Decrease the
layer offset");
// Have the usual viewer
osgViewer::Viewer viewer(arguments);
osg::DisplaySettings* displaySettings = new osg::DisplaySettings;
viewer.setDisplaySettings(displaySettings);
// Add the stats handler
viewer.addEventHandler(new osgViewer::StatsHandler);
// add the help handler
viewer.addEventHandler(new
osgViewer::HelpHandler(arguments.getApplicationUsage()));
// load the data
osg::ref_ptr<osg::Node> 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;
}
// We need the initial size ...
viewer.realize();
int viewWidth( 0 ), viewHeight( 0 );
if( viewer.getNumSlaves() == 0 )
{
osg::Viewport* viewport = viewer.getCamera()->getViewport();
viewWidth = viewport->width();
viewHeight = viewport->height();
}
// Now that the viewport contains the initial size, set up depthpeeling ...
DepthPeeling* depthPeeling = new DepthPeeling(viewWidth, viewHeight);
depthPeeling->setScene(loadedModel.get());
viewer.setSceneData(depthPeeling->getRoot());
// Add the event handler for the depth peeling stuff
viewer.addEventHandler(new EventHandler(depthPeeling));
return viewer.run();
}
_______________________________________________
osg-submissions mailing list
[email protected]
http://lists.openscenegraph.org/listinfo.cgi/osg-submissions-openscenegraph.org