OpenSceneGraph/src/osgVolume/MultipassTechnique.cpp
2016-06-08 09:06:42 +01:00

1188 lines
47 KiB
C++

/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2009 Robert Osfield
*
* This library is open source and may be redistributed and/or modified under
* the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or
* (at your option) any later version. The full license is in LICENSE file
* included with this distribution, and on the openscenegraph.org website.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* OpenSceneGraph Public License for more details.
*/
#include <osgVolume/MultipassTechnique>
#include <osgVolume/VolumeTile>
#include <osgVolume/VolumeScene>
#include <osgVolume/VolumeSettings>
#include <osg/Geometry>
#include <osg/ValueObject>
#include <osg/io_utils>
#include <osg/Program>
#include <osg/Material>
#include <osg/CullFace>
#include <osg/Texture1D>
#include <osg/Texture2D>
#include <osg/Texture3D>
#include <osg/TexEnvFilter>
#include <osg/TransferFunction>
#include <osgDB/ReadFile>
#include <osgDB/WriteFile>
namespace osgVolume
{
osg::Image* createDownsampledImage(osg::Image* sourceImage)
{
osg::ref_ptr<osg::Image> targetImage = new osg::Image;
int s_base = sourceImage->s()/2;
int s_odd = (sourceImage->s()%2);
int s = s_base + s_odd;
int t_base = sourceImage->t()/2;
int t_odd = (sourceImage->t()%2);
int t = t_base + t_odd;
int r_base = sourceImage->r()/2;
int r_odd = (sourceImage->r()%2);
int r = r_base + r_odd;
OSG_NOTICE<<"createDownsampledImage("<<sourceImage->s()<<", "<<sourceImage->t()<<", "<<sourceImage->r()<<")"<<std::endl;
OSG_NOTICE<<" s_base = "<<s_base<<", s_odd = "<<s_odd<<", s="<<s<<std::endl;
OSG_NOTICE<<" t_base = "<<t_base<<", t_odd = "<<t_odd<<", t="<<t<<std::endl;
OSG_NOTICE<<" r_base = "<<r_base<<", r_odd = "<<r_odd<<", r="<<r<<std::endl;
targetImage->allocateImage(s, t, r, sourceImage->getPixelFormat(), sourceImage->getDataType());
int numComponents = 1;
for(int plane=0; plane<r_base; ++plane)
{
for(int row=0; row<t_base; ++row)
{
#if 0
typedef unsigned char T;
typedef unsigned short TL;
#else
typedef unsigned short T;
typedef unsigned int TL;
#endif
T* ptr_source_1 = reinterpret_cast<T*>(sourceImage->data(0,row*2, plane*2));
T* ptr_source_2 = reinterpret_cast<T*>(sourceImage->data(1,row*2, plane*2));
T* ptr_source_3 = reinterpret_cast<T*>(sourceImage->data(0,row*2+1, plane*2));
T* ptr_source_4 = reinterpret_cast<T*>(sourceImage->data(1,row*2+1, plane*2));
T* ptr_source_5 = reinterpret_cast<T*>(sourceImage->data(0,row*2, plane*2+1));
T* ptr_source_6 = reinterpret_cast<T*>(sourceImage->data(1,row*2, plane*2+1));
T* ptr_source_7 = reinterpret_cast<T*>(sourceImage->data(0,row*2+1, plane*2+1));
T* ptr_source_8 = reinterpret_cast<T*>(sourceImage->data(1,row*2+1, plane*2+1));
T* ptr_target = reinterpret_cast<T*>(targetImage->data(0,row,plane));
for(int column=0; column<s_base; ++column)
{
// average and copy across the source data.
for(int i=0; i<numComponents; ++i)
{
#if 1
TL value = static_cast<TL>(*ptr_source_1)+
static_cast<TL>(*ptr_source_2)+
static_cast<TL>(*ptr_source_3)+
static_cast<TL>(*ptr_source_4)+
static_cast<TL>(*ptr_source_5)+
static_cast<TL>(*ptr_source_6)+
static_cast<TL>(*ptr_source_7)+
static_cast<TL>(*ptr_source_8);
value /= 8;
#else
TL value = 0;
value = osg::maximum(static_cast<TL>(*ptr_source_1),value);
value = osg::maximum(static_cast<TL>(*ptr_source_2),value);
value = osg::maximum(static_cast<TL>(*ptr_source_3),value);
value = osg::maximum(static_cast<TL>(*ptr_source_4),value);
value = osg::maximum(static_cast<TL>(*ptr_source_5),value);
value = osg::maximum(static_cast<TL>(*ptr_source_6),value);
value = osg::maximum(static_cast<TL>(*ptr_source_7),value);
value = osg::maximum(static_cast<TL>(*ptr_source_8),value);
#endif
*ptr_target = static_cast<T>(value);
++ptr_target;
++ptr_source_1;
++ptr_source_2;
++ptr_source_3;
++ptr_source_4;
++ptr_source_5;
++ptr_source_6;
++ptr_source_7;
++ptr_source_8;
}
// skip to the next set of source texels
ptr_source_1 += numComponents;
ptr_source_2 += numComponents;
ptr_source_3 += numComponents;
ptr_source_4 += numComponents;
ptr_source_5 += numComponents;
ptr_source_6 += numComponents;
ptr_source_7 += numComponents;
ptr_source_8 += numComponents;
}
if (s_odd)
{
}
}
if (t_odd)
{
OSG_NOTICE<<"Need to handle odd image row"<<std::endl;
}
}
if (r_odd)
{
OSG_NOTICE<<"Need to handle odd image plane"<<std::endl;
}
return targetImage.release();
}
////////////////////////////////////////////////////////////////////////
//
// MultipassTileData
//
class RTTCameraCullCallback : public osg::NodeCallback
{
public:
RTTCameraCullCallback(MultipassTechnique::MultipassTileData* tileData, MultipassTechnique* mt):
_tileData(tileData),
_mt(mt) {}
virtual void operator()(osg::Node* /*node*/, osg::NodeVisitor* nv)
{
osgUtil::CullVisitor* cv = nv->asCullVisitor();
cv->pushProjectionMatrix(_tileData->projectionMatrix.get());
// OSG_NOTICE<<"Traversing tile subgraph for pre-render "<<_mt->getVolumeTile()->getNumChildren()<<std::endl;
_mt->getVolumeTile()->osg::Group::traverse(*cv);
cv->popProjectionMatrix();
}
protected:
virtual ~RTTCameraCullCallback() {}
osg::observer_ptr<osgVolume::MultipassTechnique::MultipassTileData> _tileData;
osg::observer_ptr<osgVolume::MultipassTechnique> _mt;
};
MultipassTechnique::MultipassTileData::MultipassTileData(osgUtil::CullVisitor* cv, MultipassTechnique* mpt):
TileData(),
multipassTechnique(mpt)
{
currentRenderingMode = mpt->computeRenderingMode();
int width = 512;
int height = 512;
osg::Viewport* viewport = cv->getCurrentRenderStage()->getViewport();
if (viewport)
{
width = static_cast<int>(viewport->width());
height = static_cast<int>(viewport->height());
}
stateset = new osg::StateSet;
eyeToTileUniform = new osg::Uniform("eyeToTile",osg::Matrixf());
stateset->addUniform(eyeToTileUniform.get());
tileToImageUniform = new osg::Uniform("tileToImage",osg::Matrixf());
stateset->addUniform(tileToImageUniform.get());
switch(currentRenderingMode)
{
case(MultipassTechnique::CUBE):
{
// no need to set up RTT Cameras;
OSG_NOTICE<<"Setting up MultipassTileData for CUBE rendering"<<std::endl;
break;
}
case(MultipassTechnique::HULL):
{
OSG_NOTICE<<"Setting up MultipassTileData for HULL rendering"<<std::endl;
setUp(frontFaceRttCamera, frontFaceDepthTexture, width, height);
frontFaceRttCamera->setName("frontFaceRttCamera");
frontFaceRttCamera->setCullCallback(new RTTCameraCullCallback(this, mpt));
frontFaceRttCamera->getOrCreateStateSet()->setAttributeAndModes(new osg::CullFace(osg::CullFace::BACK), osg::StateAttribute::ON|osg::StateAttribute::OVERRIDE);
stateset->setTextureAttribute(2, frontFaceDepthTexture.get(), osg::StateAttribute::ON|osg::StateAttribute::OVERRIDE);
break;
}
case(MultipassTechnique::CUBE_AND_HULL):
{
OSG_NOTICE<<"Setting up MultipassTileData for CUBE_AND_HULL rendering"<<std::endl;
setUp(frontFaceRttCamera, frontFaceDepthTexture, width, height);
frontFaceRttCamera->setName("frontFaceRttCamera");
frontFaceRttCamera->setCullCallback(new RTTCameraCullCallback(this, mpt));
frontFaceRttCamera->getOrCreateStateSet()->setAttributeAndModes(new osg::CullFace(osg::CullFace::BACK), osg::StateAttribute::ON|osg::StateAttribute::OVERRIDE);
setUp(backFaceRttCamera, backFaceDepthTexture, width, height);
backFaceRttCamera->setName("backFaceRttCamera");
backFaceRttCamera->setCullCallback(new RTTCameraCullCallback(this, mpt));
backFaceRttCamera->getOrCreateStateSet()->setAttributeAndModes(new osg::CullFace(osg::CullFace::FRONT), osg::StateAttribute::ON|osg::StateAttribute::OVERRIDE);
stateset->setTextureAttribute(2, frontFaceDepthTexture.get(), osg::StateAttribute::ON|osg::StateAttribute::OVERRIDE);
stateset->setTextureAttribute(3, backFaceDepthTexture.get(), osg::StateAttribute::ON|osg::StateAttribute::OVERRIDE);
break;
}
}
}
void MultipassTechnique::MultipassTileData::setUp(osg::ref_ptr<osg::Camera>& camera, osg::ref_ptr<osg::Texture2D>& depthTexture, int width, int height)
{
depthTexture = new osg::Texture2D;
depthTexture->setTextureSize(width, height);
depthTexture->setInternalFormat(GL_DEPTH_COMPONENT);
depthTexture->setFilter(osg::Texture2D::MIN_FILTER,osg::Texture2D::LINEAR);
depthTexture->setFilter(osg::Texture2D::MAG_FILTER,osg::Texture2D::LINEAR);
depthTexture->setWrap(osg::Texture2D::WRAP_S,osg::Texture2D::CLAMP_TO_BORDER);
depthTexture->setWrap(osg::Texture2D::WRAP_T,osg::Texture2D::CLAMP_TO_BORDER);
depthTexture->setBorderColor(osg::Vec4(1.0f,1.0f,1.0f,1.0f));
camera = new osg::Camera;
camera->attach(osg::Camera::DEPTH_BUFFER, depthTexture.get());
camera->setViewport(0, 0, width, height);
// clear the depth and colour bufferson each clear.
camera->setClearMask(GL_DEPTH_BUFFER_BIT);
// set the camera to render before the main camera.
camera->setRenderOrder(osg::Camera::PRE_RENDER);
// tell the camera to use OpenGL frame buffer object where supported.
camera->setRenderTargetImplementation(osg::Camera::FRAME_BUFFER_OBJECT);
camera->setReferenceFrame(osg::Transform::RELATIVE_RF);
camera->setProjectionMatrix(osg::Matrixd::identity());
camera->setViewMatrix(osg::Matrixd::identity());
}
void MultipassTechnique::MultipassTileData::update(osgUtil::CullVisitor* cv)
{
if (currentRenderingMode != multipassTechnique->computeRenderingMode())
{
OSG_NOTICE<<"Warning: need to re-structure MP setup."<<std::endl;
}
active = true;
nodePath = cv->getNodePath();
projectionMatrix = cv->getProjectionMatrix();
modelviewMatrix = cv->getModelViewMatrix();
int width = 512;
int height = 512;
osg::Viewport* viewport = cv->getCurrentRenderStage()->getViewport();
if (viewport)
{
width = static_cast<int>(viewport->width());
height = static_cast<int>(viewport->height());
}
if (frontFaceDepthTexture.valid())
{
if (frontFaceDepthTexture->getTextureWidth()!=width || frontFaceDepthTexture->getTextureHeight()!=height)
{
OSG_NOTICE<<"Need to change texture size to "<<width<<", "<<height<<std::endl;
frontFaceDepthTexture->setTextureSize(width, height);
frontFaceRttCamera->setViewport(0, 0, width, height);
if (frontFaceRttCamera->getRenderingCache())
{
frontFaceRttCamera->getRenderingCache()->releaseGLObjects(0);
}
}
}
if (backFaceDepthTexture.valid())
{
if (backFaceDepthTexture->getTextureWidth()!=width || backFaceDepthTexture->getTextureHeight()!=height)
{
OSG_NOTICE<<"Need to change texture size to "<<width<<", "<<height<<std::endl;
backFaceDepthTexture->setTextureSize(width, height);
backFaceRttCamera->setViewport(0, 0, width, height);
if (backFaceRttCamera->getRenderingCache())
{
backFaceRttCamera->getRenderingCache()->releaseGLObjects(0);
}
}
}
}
/////////////////////////////////////////////////////////////////////////////////////////
//
// MultipassTechnique
//
MultipassTechnique::MultipassTechnique()
{
}
MultipassTechnique::MultipassTechnique(const MultipassTechnique& fft,const osg::CopyOp& copyop):
VolumeTechnique(fft,copyop)
{
}
MultipassTechnique::~MultipassTechnique()
{
}
osg::StateSet* MultipassTechnique::createStateSet(osg::StateSet* statesetPrototype, osg::Program* programPrototype, osg::Shader* shaderToAdd1, osg::Shader* shaderToAdd2)
{
osg::ref_ptr<osg::StateSet> stateset = osg::clone(statesetPrototype, osg::CopyOp::SHALLOW_COPY);
osg::ref_ptr<osg::Program> program = osg::clone(programPrototype, osg::CopyOp::SHALLOW_COPY);
stateset->setAttribute(program.get());
if (shaderToAdd1) program->addShader(shaderToAdd1);
if (shaderToAdd2) program->addShader(shaderToAdd2);
return stateset.release();
}
MultipassTechnique::RenderingMode MultipassTechnique::computeRenderingMode()
{
bool hasHull = (_volumeTile->getNumChildren()>0);
if (!hasHull)
{
return CUBE;
}
CollectPropertiesVisitor cpv(false);
if (_volumeTile->getLayer()->getProperty())
{
_volumeTile->getLayer()->getProperty()->accept(cpv);
}
double etfValue = cpv._exteriorTransparencyFactorProperty.valid() ? cpv._exteriorTransparencyFactorProperty->getValue() : 0.0;
if (etfValue<=0.0)
{
return HULL;
}
else if (etfValue<1.0)
{
return CUBE_AND_HULL;
}
else
{
return CUBE;
}
}
void MultipassTechnique::init()
{
OSG_INFO<<"MultipassTechnique::init()"<<std::endl;
if (!_volumeTile)
{
OSG_NOTICE<<"MultipassTechnique::init(), error no volume tile assigned."<<std::endl;
return;
}
if (_volumeTile->getLayer()==0)
{
OSG_NOTICE<<"MultipassTechnique::init(), error no layer assigend to volume tile."<<std::endl;
return;
}
if (_volumeTile->getLayer()->getImage()==0)
{
OSG_NOTICE<<"MultipassTechnique::init(), error no image assigned to layer."<<std::endl;
return;
}
OSG_NOTICE<<"MultipassTechnique::init() Need to set up"<<std::endl;
CollectPropertiesVisitor cpv(false);
if (_volumeTile->getLayer()->getProperty())
{
_volumeTile->getLayer()->getProperty()->accept(cpv);
}
osg::ref_ptr<osg::Geode> geode = new osg::Geode;
{
osg::Geometry* geom = new osg::Geometry;
osg::Vec3Array* coords = new osg::Vec3Array(8);
(*coords)[0] = osg::Vec3d(0.0,0.0,0.0);
(*coords)[1] = osg::Vec3d(1.0,0.0,0.0);
(*coords)[2] = osg::Vec3d(1.0,1.0,0.0);
(*coords)[3] = osg::Vec3d(0.0,1.0,0.0);
(*coords)[4] = osg::Vec3d(0.0,0.0,1.0);
(*coords)[5] = osg::Vec3d(1.0,0.0,1.0);
(*coords)[6] = osg::Vec3d(1.0,1.0,1.0);
(*coords)[7] = osg::Vec3d(0.0,1.0,1.0);
geom->setVertexArray(coords);
osg::Vec4Array* colours = new osg::Vec4Array(1);
(*colours)[0].set(1.0f,1.0f,1.0,1.0f);
geom->setColorArray(colours, osg::Array::BIND_OVERALL);
osg::DrawElementsUShort* drawElements = new osg::DrawElementsUShort(GL_QUADS);
// bottom
drawElements->push_back(3);
drawElements->push_back(2);
drawElements->push_back(1);
drawElements->push_back(0);
// bottom
drawElements->push_back(7);//7623
drawElements->push_back(6);
drawElements->push_back(2);
drawElements->push_back(3);
// left
drawElements->push_back(4);//4730
drawElements->push_back(7);
drawElements->push_back(3);
drawElements->push_back(0);
// right
drawElements->push_back(1);//1265
drawElements->push_back(2);
drawElements->push_back(6);
drawElements->push_back(5);
// front
drawElements->push_back(5);//5401
drawElements->push_back(4);
drawElements->push_back(0);
drawElements->push_back(1);
// top
drawElements->push_back(4);//4567
drawElements->push_back(5);
drawElements->push_back(6);
drawElements->push_back(7);
geom->addPrimitiveSet(drawElements);
geode->addDrawable(geom);
}
_transform = new osg::MatrixTransform;
_transform->addChild(geode.get());
// handle locators
Locator* masterLocator = _volumeTile->getLocator();
Locator* layerLocator = _volumeTile->getLayer()->getLocator();
osg::TransferFunction1D* tf = 0;
if (!masterLocator && layerLocator) masterLocator = layerLocator;
if (!layerLocator && masterLocator) layerLocator = masterLocator;
osg::Matrix geometryMatrix;
if (masterLocator)
{
geometryMatrix = masterLocator->getTransform();
_transform->setMatrix(geometryMatrix);
masterLocator->addCallback(new TransformLocatorCallback(_transform.get()));
}
osg::Matrix imageMatrix;
if (layerLocator)
{
imageMatrix = layerLocator->getTransform();
}
OSG_NOTICE<<"MultipassTechnique::init() : geometryMatrix = "<<geometryMatrix<<std::endl;
OSG_NOTICE<<"MultipassTechnique::init() : imageMatrix = "<<imageMatrix<<std::endl;
osg::ref_ptr<osg::StateSet> stateset = new osg::StateSet;
_volumeRenderStateSet = stateset;
// texture unit 0 is for main scene colour buffer
// texture unit 1 is for main scene depth buffer
// texture unit 2 is for front depth buffer of hull when required
// texture unit 3 is for back depth depth of hull when required
unsigned int volumeTextureUnit = 4;
unsigned int transferFunctionTextureUnit = 5;
// set up uniforms
{
stateset->addUniform(new osg::Uniform("colorTexture",0));
stateset->addUniform(new osg::Uniform("depthTexture",1));
stateset->setMode(GL_ALPHA_TEST,osg::StateAttribute::ON);
//stateset->setMode(GL_DEPTH_TEST,osg::StateAttribute::OFF);
float alphaFuncValue = 0.1;
if (cpv._isoProperty.valid())
{
alphaFuncValue = cpv._isoProperty->getValue();
}
if (cpv._sampleRatioProperty.valid())
stateset->addUniform(cpv._sampleRatioProperty->getUniform());
else
stateset->addUniform(new osg::Uniform("SampleRatioValue",1.0f));
if (cpv._transparencyProperty.valid())
stateset->addUniform(cpv._transparencyProperty->getUniform());
else
stateset->addUniform(new osg::Uniform("TransparencyValue",1.0f));
if (cpv._exteriorTransparencyFactorProperty.valid())
stateset->addUniform(cpv._exteriorTransparencyFactorProperty->getUniform());
else
stateset->addUniform(new osg::Uniform("ExteriorTransparencyFactorValue",0.0f));
if (cpv._afProperty.valid())
stateset->addUniform(cpv._afProperty->getUniform());
else
stateset->addUniform(new osg::Uniform("AlphaFuncValue",alphaFuncValue));
if (cpv._isoProperty.valid())
stateset->addUniform(cpv._isoProperty->getUniform());
else
stateset->addUniform(new osg::Uniform("IsoSurfaceValue",alphaFuncValue));
if (cpv._tfProperty.valid())
{
tf = dynamic_cast<osg::TransferFunction1D*>(cpv._tfProperty->getTransferFunction());
}
}
// set up 3D texture
osg::ref_ptr<osg::Image> image_3d = _volumeTile->getLayer()->getImage();
// create a downsampled image to use when rendering at a lower quality.
// osg::ref_ptr<osg::Image> downsampled_image_3d = createDownsampledImage(image_3d.get());
//image_3d = createDownsampledImage(downsampled_image_3d.get());
// image_3d = downsampled_image_3d;
osg::ref_ptr<osg::Texture3D> texture3D = new osg::Texture3D;
{
osg::Texture::InternalFormatMode internalFormatMode = osg::Texture::USE_IMAGE_DATA_FORMAT;
#define VOLUME_TYPE 2
#if VOLUME_TYPE==1
osg::Texture::FilterMode minFilter = osg::Texture::LINEAR_MIPMAP_LINEAR;
osg::Texture::FilterMode magFilter = osg::Texture::LINEAR;
#elif VOLUME_TYPE==2
osg::Texture::FilterMode minFilter = osg::Texture::LINEAR;
osg::Texture::FilterMode magFilter = osg::Texture::LINEAR;
#else
osg::Texture::FilterMode minFilter = osg::Texture::NEAREST;
osg::Texture::FilterMode magFilter = osg::Texture::NEAREST;
#endif
// set up the 3d texture itself,
// note, well set the filtering up so that mip mapping is disabled,
// gluBuild3DMipsmaps doesn't do a very good job of handled the
// imbalanced dimensions of the 256x256x4 texture.
texture3D->setResizeNonPowerOfTwoHint(false);
texture3D->setFilter(osg::Texture3D::MIN_FILTER,minFilter);
texture3D->setFilter(osg::Texture3D::MAG_FILTER, magFilter);
texture3D->setWrap(osg::Texture3D::WRAP_R,osg::Texture3D::CLAMP_TO_BORDER);
texture3D->setWrap(osg::Texture3D::WRAP_S,osg::Texture3D::CLAMP_TO_BORDER);
texture3D->setWrap(osg::Texture3D::WRAP_T,osg::Texture3D::CLAMP_TO_BORDER);
//texture3D->setMaxAnisotropy(16.0f);
texture3D->setBorderColor(osg::Vec4(0.0,0.0,0.0,0.0));
if (image_3d->getPixelFormat()==GL_ALPHA ||
image_3d->getPixelFormat()==GL_LUMINANCE)
{
texture3D->setInternalFormatMode(osg::Texture3D::USE_USER_DEFINED_FORMAT);
texture3D->setInternalFormat(GL_INTENSITY);
}
else
{
texture3D->setInternalFormatMode(internalFormatMode);
}
texture3D->setImage(image_3d.get());
stateset->setTextureAttributeAndModes(volumeTextureUnit, texture3D.get(), osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE);
osg::ref_ptr<osg::Uniform> baseTextureSampler = new osg::Uniform("volumeTexture", int(volumeTextureUnit));
stateset->addUniform(baseTextureSampler.get());
osg::ref_ptr<osg::Uniform> volumeCellSize = new osg::Uniform("volumeCellSize", osg::Vec3(1.0f/static_cast<float>(image_3d->s()),1.0f/static_cast<float>(image_3d->t()),1.0f/static_cast<float>(image_3d->r())));
stateset->addUniform(volumeCellSize.get());
OSG_NOTICE<<"Texture Dimensions "<<image_3d->s()<<", "<<image_3d->t()<<", "<<image_3d->r()<<std::endl;
}
if (tf)
{
OSG_NOTICE<<"Setting up TransferFunction"<<std::endl;
float tfScale = 1.0f;
float tfOffset = 0.0f;
ImageLayer* imageLayer = dynamic_cast<ImageLayer*>(_volumeTile->getLayer());
if (imageLayer)
{
tfOffset = (imageLayer->getTexelOffset()[3] - tf->getMinimum()) / (tf->getMaximum() - tf->getMinimum());
tfScale = imageLayer->getTexelScale()[3] / (tf->getMaximum() - tf->getMinimum());
}
else
{
tfOffset = -tf->getMinimum() / (tf->getMaximum()-tf->getMinimum());
tfScale = 1.0f / (tf->getMaximum()-tf->getMinimum());
}
osg::ref_ptr<osg::Texture1D> tf_texture = new osg::Texture1D;
tf_texture->setImage(tf->getImage());
tf_texture->setResizeNonPowerOfTwoHint(false);
tf_texture->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR);
tf_texture->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR);
tf_texture->setWrap(osg::Texture::WRAP_R,osg::Texture::CLAMP_TO_EDGE);
stateset->setTextureAttributeAndModes(transferFunctionTextureUnit, tf_texture.get(), osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE);
stateset->addUniform(new osg::Uniform("tfTexture",int(transferFunctionTextureUnit)));
stateset->addUniform(new osg::Uniform("tfOffset",tfOffset));
stateset->addUniform(new osg::Uniform("tfScale",tfScale));
}
// creates CullFace attributes to apply to front/back StateSet configurations.
osg::ref_ptr<osg::CullFace> front_CullFace = new osg::CullFace(osg::CullFace::BACK);
osg::ref_ptr<osg::CullFace> back_CullFace = new osg::CullFace(osg::CullFace::FRONT);
osg::ref_ptr<osg::Shader> main_vertexShader = osgDB::readRefShaderFile(osg::Shader::VERTEX, "shaders/volume_multipass.vert");
if (!main_vertexShader)
{
#include "Shaders/volume_multipass_vert.cpp"
main_vertexShader = new osg::Shader(osg::Shader::VERTEX, volume_multipass_vert);
}
osg::ref_ptr<osg::Shader> computeRayColorShader = osgDB::readRefShaderFile(osg::Shader::FRAGMENT, "shaders/volume_compute_ray_color.frag");
if (!computeRayColorShader)
{
#include "Shaders/volume_compute_ray_color_frag.cpp"
computeRayColorShader = new osg::Shader(osg::Shader::FRAGMENT, volume_compute_ray_color_frag);
}
// clear any previous settings
_stateSetMap.clear();
// set up the program template for rendering just the cube
osg::ref_ptr<osg::Shader> cube_main_fragmentShader = osgDB::readRefShaderFile(osg::Shader::FRAGMENT, "shaders/volume_multipass_cube.frag");;
if (!cube_main_fragmentShader)
{
#include "Shaders/volume_multipass_cube_frag.cpp"
cube_main_fragmentShader = new osg::Shader(osg::Shader::FRAGMENT, volume_multipass_cube_frag);
}
osg::ref_ptr<osg::StateSet> cube_stateset_prototype = new osg::StateSet;
osg::ref_ptr<osg::Program> cube_program_prototype = new osg::Program;
{
cube_stateset_prototype->setAttributeAndModes(back_CullFace.get(), osg::StateAttribute::ON|osg::StateAttribute::OVERRIDE);
cube_program_prototype->addShader(main_vertexShader.get());
cube_program_prototype->addShader(cube_main_fragmentShader.get());
cube_program_prototype->addShader(computeRayColorShader.get());
}
// set up the program template for rendering just the hull
osg::ref_ptr<osg::Shader> hull_main_fragmentShader = osgDB::readRefShaderFile(osg::Shader::FRAGMENT, "shaders/volume_multipass_hull.frag");;
if (!hull_main_fragmentShader)
{
#include "Shaders/volume_multipass_hull_frag.cpp"
hull_main_fragmentShader = new osg::Shader(osg::Shader::FRAGMENT, volume_multipass_hull_frag);
}
osg::ref_ptr<osg::StateSet> hull_stateset_prototype = new osg::StateSet;
osg::ref_ptr<osg::Program> hull_program_prototype = new osg::Program;
{
hull_stateset_prototype->setAttributeAndModes(back_CullFace.get(), osg::StateAttribute::ON|osg::StateAttribute::OVERRIDE);
hull_stateset_prototype->addUniform(new osg::Uniform("frontFaceDepthTexture",2));
hull_program_prototype->addShader(main_vertexShader.get());
hull_program_prototype->addShader(hull_main_fragmentShader.get());
hull_program_prototype->addShader(computeRayColorShader.get());
}
// set up the program template for rendering just the cube
osg::ref_ptr<osg::Shader> cube_and_hull_main_fragmentShader = osgDB::readRefShaderFile(osg::Shader::FRAGMENT, "shaders/volume_multipass_cube_and_hull.frag");;
if (!cube_and_hull_main_fragmentShader)
{
#include "Shaders/volume_multipass_cube_and_hull_frag.cpp"
cube_and_hull_main_fragmentShader = new osg::Shader(osg::Shader::FRAGMENT, volume_multipass_cube_and_hull_frag);
}
osg::ref_ptr<osg::StateSet> cube_and_hull_stateset_prototype = new osg::StateSet;
osg::ref_ptr<osg::Program> cube_and_hull_program_prototype = new osg::Program;
{
cube_and_hull_stateset_prototype->setAttributeAndModes(back_CullFace.get(), osg::StateAttribute::ON|osg::StateAttribute::OVERRIDE);
cube_and_hull_stateset_prototype->addUniform(new osg::Uniform("frontFaceDepthTexture",2));
cube_and_hull_stateset_prototype->addUniform(new osg::Uniform("backFaceDepthTexture",3));
cube_and_hull_program_prototype->addShader(main_vertexShader.get());
cube_and_hull_program_prototype->addShader(cube_and_hull_main_fragmentShader.get());
cube_and_hull_program_prototype->addShader(computeRayColorShader.get());
}
// set up the rendering of the front face
{
_frontFaceStateSet = new osg::StateSet;
// cull only the bac faces so we write only the front
_frontFaceStateSet->setAttributeAndModes(front_CullFace.get(), osg::StateAttribute::ON|osg::StateAttribute::OVERRIDE);
// set up the front falce
osg::ref_ptr<osg::Program> program = new osg::Program;
program->addShader(main_vertexShader.get());
_frontFaceStateSet->setAttribute(program.get(), osg::StateAttribute::ON|osg::StateAttribute::OVERRIDE);
}
// STANDARD_SHADERS
{
// STANDARD_SHADERS without TransferFunction
{
osg::ref_ptr<osg::Shader> accumulateSamplesShader = osgDB::readRefShaderFile(osg::Shader::FRAGMENT, "shaders/volume_accumulateSamples_standard.frag");
if (!accumulateSamplesShader)
{
#include "Shaders/volume_accumulateSamples_standard_frag.cpp"
accumulateSamplesShader = new osg::Shader(osg::Shader::FRAGMENT, volume_accumulateSamples_standard_frag);
}
_stateSetMap[STANDARD_SHADERS|CUBE_SHADERS] = createStateSet(cube_stateset_prototype.get(), cube_program_prototype.get(), accumulateSamplesShader.get());
_stateSetMap[STANDARD_SHADERS|HULL_SHADERS] = createStateSet(hull_stateset_prototype.get(), hull_program_prototype.get(), accumulateSamplesShader.get());
_stateSetMap[STANDARD_SHADERS|CUBE_AND_HULL_SHADERS] = createStateSet(cube_and_hull_stateset_prototype.get(), cube_and_hull_program_prototype.get(), accumulateSamplesShader.get());
}
// STANDARD_SHADERS with TransferFunction
if (tf)
{
osg::ref_ptr<osg::Shader> accumulateSamplesShader = osgDB::readRefShaderFile(osg::Shader::FRAGMENT, "shaders/volume_accumulateSamples_standard_tf.frag");
if (!accumulateSamplesShader)
{
#include "Shaders/volume_accumulateSamples_standard_tf_frag.cpp"
accumulateSamplesShader = new osg::Shader(osg::Shader::FRAGMENT, volume_accumulateSamples_standard_tf_frag);
}
_stateSetMap[STANDARD_SHADERS|CUBE_SHADERS|TF_SHADERS] = createStateSet(cube_stateset_prototype.get(), cube_program_prototype.get(), accumulateSamplesShader.get());
_stateSetMap[STANDARD_SHADERS|HULL_SHADERS|TF_SHADERS] = createStateSet(hull_stateset_prototype.get(), hull_program_prototype.get(), accumulateSamplesShader.get());
_stateSetMap[STANDARD_SHADERS|CUBE_AND_HULL_SHADERS|TF_SHADERS] = createStateSet(cube_and_hull_stateset_prototype.get(), cube_and_hull_program_prototype.get(), accumulateSamplesShader.get());
}
}
// ISO_SHADERS
if (cpv._isoProperty.valid())
{
// ISO_SHADERS without TransferFunction
{
osg::ref_ptr<osg::Shader> accumulateSamplesShader = osgDB::readRefShaderFile(osg::Shader::FRAGMENT, "shaders/volume_accumulateSamples_iso.frag");
if (!accumulateSamplesShader)
{
#include "Shaders/volume_accumulateSamples_iso_frag.cpp"
accumulateSamplesShader = new osg::Shader(osg::Shader::FRAGMENT, volume_accumulateSamples_iso_frag);
}
_stateSetMap[ISO_SHADERS|CUBE_SHADERS] = createStateSet(cube_stateset_prototype.get(), cube_program_prototype.get(), accumulateSamplesShader.get());
_stateSetMap[ISO_SHADERS|HULL_SHADERS] = createStateSet(hull_stateset_prototype.get(), hull_program_prototype.get(), accumulateSamplesShader.get());
_stateSetMap[ISO_SHADERS|CUBE_AND_HULL_SHADERS] = createStateSet(cube_and_hull_stateset_prototype.get(), cube_and_hull_program_prototype.get(), accumulateSamplesShader.get());
}
// ISO_SHADERS with TransferFunction
if (tf)
{
osg::ref_ptr<osg::Shader> accumulateSamplesShader = osgDB::readRefShaderFile(osg::Shader::FRAGMENT, "shaders/volume_accumulateSamples_iso_tf.frag");
if (!accumulateSamplesShader)
{
#include "Shaders/volume_accumulateSamples_iso_tf_frag.cpp"
accumulateSamplesShader = new osg::Shader(osg::Shader::FRAGMENT, volume_accumulateSamples_iso_tf_frag);
}
_stateSetMap[ISO_SHADERS|CUBE_SHADERS|TF_SHADERS] = createStateSet(cube_stateset_prototype.get(), cube_program_prototype.get(), accumulateSamplesShader.get());
_stateSetMap[ISO_SHADERS|HULL_SHADERS|TF_SHADERS] = createStateSet(hull_stateset_prototype.get(), hull_program_prototype.get(), accumulateSamplesShader.get());
_stateSetMap[ISO_SHADERS|CUBE_AND_HULL_SHADERS|TF_SHADERS] = createStateSet(cube_and_hull_stateset_prototype.get(), cube_and_hull_program_prototype.get(), accumulateSamplesShader.get());
}
}
// MIP_SHADERS
if (cpv._mipProperty.valid())
{
// MIP_SHADERS without TransferFunction
{
osg::ref_ptr<osg::Shader> accumulateSamplesShader = osgDB::readRefShaderFile(osg::Shader::FRAGMENT, "shaders/volume_accumulateSamples_mip.frag");
if (!accumulateSamplesShader)
{
#include "Shaders/volume_accumulateSamples_mip_frag.cpp"
accumulateSamplesShader = new osg::Shader(osg::Shader::FRAGMENT, volume_accumulateSamples_mip_frag);
}
_stateSetMap[MIP_SHADERS|CUBE_SHADERS] = createStateSet(cube_stateset_prototype.get(), cube_program_prototype.get(), accumulateSamplesShader.get());
_stateSetMap[MIP_SHADERS|HULL_SHADERS] = createStateSet(hull_stateset_prototype.get(), hull_program_prototype.get(), accumulateSamplesShader.get());
_stateSetMap[MIP_SHADERS|CUBE_AND_HULL_SHADERS] = createStateSet(cube_and_hull_stateset_prototype.get(), cube_and_hull_program_prototype.get(), accumulateSamplesShader.get());
}
// MIP_SHADERS with TransferFunction
if (tf)
{
osg::ref_ptr<osg::Shader> accumulateSamplesShader = osgDB::readRefShaderFile(osg::Shader::FRAGMENT, "shaders/volume_accumulateSamples_mip_tf.frag");
if (!accumulateSamplesShader)
{
#include "Shaders/volume_accumulateSamples_mip_tf_frag.cpp"
accumulateSamplesShader = new osg::Shader(osg::Shader::FRAGMENT, volume_accumulateSamples_mip_tf_frag);
}
_stateSetMap[MIP_SHADERS|CUBE_SHADERS|TF_SHADERS] = createStateSet(cube_stateset_prototype.get(), cube_program_prototype.get(), accumulateSamplesShader.get());
_stateSetMap[MIP_SHADERS|HULL_SHADERS|TF_SHADERS] = createStateSet(hull_stateset_prototype.get(), hull_program_prototype.get(), accumulateSamplesShader.get());
_stateSetMap[MIP_SHADERS|CUBE_AND_HULL_SHADERS|TF_SHADERS] = createStateSet(cube_and_hull_stateset_prototype.get(), cube_and_hull_program_prototype.get(), accumulateSamplesShader.get());
}
}
// LIT_SHADERS
if (cpv._lightingProperty.valid())
{
// LIT_SHADERS without TransferFunction
{
osg::ref_ptr<osg::Shader> accumulateSamplesShader = osgDB::readRefShaderFile(osg::Shader::FRAGMENT, "shaders/volume_accumulateSamples_lit.frag");
if (!accumulateSamplesShader)
{
#include "Shaders/volume_accumulateSamples_lit_frag.cpp"
accumulateSamplesShader = new osg::Shader(osg::Shader::FRAGMENT, volume_accumulateSamples_lit_frag);
}
_stateSetMap[LIT_SHADERS|CUBE_SHADERS] = createStateSet(cube_stateset_prototype.get(), cube_program_prototype.get(), accumulateSamplesShader.get());
_stateSetMap[LIT_SHADERS|HULL_SHADERS] = createStateSet(hull_stateset_prototype.get(), hull_program_prototype.get(), accumulateSamplesShader.get());
_stateSetMap[LIT_SHADERS|CUBE_AND_HULL_SHADERS] = createStateSet(cube_and_hull_stateset_prototype.get(), cube_and_hull_program_prototype.get(), accumulateSamplesShader.get());
}
// MIP_SHADERS with TransferFunction
if (tf)
{
osg::ref_ptr<osg::Shader> accumulateSamplesShader = osgDB::readRefShaderFile(osg::Shader::FRAGMENT, "shaders/volume_accumulateSamples_lit_tf.frag");
if (!accumulateSamplesShader)
{
#include "Shaders/volume_accumulateSamples_lit_tf_frag.cpp"
accumulateSamplesShader = new osg::Shader(osg::Shader::FRAGMENT, volume_accumulateSamples_lit_tf_frag);
}
_stateSetMap[LIT_SHADERS|CUBE_SHADERS|TF_SHADERS] = createStateSet(cube_stateset_prototype.get(), cube_program_prototype.get(), accumulateSamplesShader.get());
_stateSetMap[LIT_SHADERS|HULL_SHADERS|TF_SHADERS] = createStateSet(hull_stateset_prototype.get(), hull_program_prototype.get(), accumulateSamplesShader.get());
_stateSetMap[LIT_SHADERS|CUBE_AND_HULL_SHADERS|TF_SHADERS] = createStateSet(cube_and_hull_stateset_prototype.get(), cube_and_hull_program_prototype.get(), accumulateSamplesShader.get());
}
}
if (cpv._sampleRatioWhenMovingProperty.valid())
{
_whenMovingStateSet = new osg::StateSet;
_whenMovingStateSet->addUniform(cpv._sampleRatioWhenMovingProperty->getUniform(), osg::StateAttribute::OVERRIDE | osg::StateAttribute::ON);
}
}
void MultipassTechnique::update(osgUtil::UpdateVisitor* uv)
{
// OSG_NOTICE<<"MultipassTechnique:update(osgUtil::UpdateVisitor* uv):"<<std::endl;
if (getVolumeTile()->getNumChildren()>0)
{
getVolumeTile()->osg::Group::traverse(*uv);
}
else
{
_transform->accept(*uv);
}
}
void MultipassTechnique::backfaceSubgraphCullTraversal(osgUtil::CullVisitor* cv)
{
if (!cv) return;
cv->pushStateSet(_frontFaceStateSet.get());
if (getVolumeTile()->getNumChildren()>0)
{
getVolumeTile()->osg::Group::traverse(*cv);
}
else
{
_transform->accept(*cv);
}
cv->popStateSet();
}
class RTTBackfaceCameraCullCallback : public osg::NodeCallback
{
public:
RTTBackfaceCameraCullCallback(MultipassTechnique::MultipassTileData* tileData, MultipassTechnique* mt):
_tileData(tileData),
_mt(mt) {}
virtual void operator()(osg::Node* /*node*/, osg::NodeVisitor* nv)
{
osgUtil::CullVisitor* cv = nv->asCullVisitor();
cv->pushProjectionMatrix(_tileData->projectionMatrix.get());
_mt->backfaceSubgraphCullTraversal(cv);
cv->popProjectionMatrix();
}
protected:
virtual ~RTTBackfaceCameraCullCallback() {}
osg::observer_ptr<osgVolume::MultipassTechnique::MultipassTileData> _tileData;
osg::observer_ptr<osgVolume::MultipassTechnique> _mt;
};
class ShadingModelVisitor : public osgVolume::PropertyVisitor
{
public:
ShadingModelVisitor():
PropertyVisitor(true),
_shadingModel(VolumeSettings::Standard),
_usesTransferFunction(false) {}
virtual void apply(IsoSurfaceProperty& /*iso*/) { _shadingModel = VolumeSettings::Isosurface; }
virtual void apply(MaximumIntensityProjectionProperty& /*mip*/) { _shadingModel = VolumeSettings::MaximumIntensityProjection; }
virtual void apply(LightingProperty& /*lp*/) { _shadingModel = VolumeSettings::Light; }
virtual void apply(TransferFunctionProperty& /*tf*/) { _usesTransferFunction = true; }
virtual void apply(VolumeSettings& vs) { _shadingModel = vs.getShadingModel(); }
VolumeSettings::ShadingModel _shadingModel;
bool _usesTransferFunction;
};
void MultipassTechnique::cull(osgUtil::CullVisitor* cv)
{
std::string traversalPass;
bool rttTraversal = cv->getUserValue("VolumeSceneTraversal", traversalPass) && traversalPass=="RenderToTexture";
// OSG_NOTICE<<"MultipassTechnique::cull() traversalPass="<<traversalPass<<std::endl;
osgVolume::VolumeScene* vs = 0;
osg::NodePath& nodePath = cv->getNodePath();
for(osg::NodePath::reverse_iterator itr = nodePath.rbegin();
(itr != nodePath.rend()) && (vs == 0);
++itr)
{
vs = dynamic_cast<osgVolume::VolumeScene*>(*itr);
}
RenderingMode renderingMode = computeRenderingMode();
if (rttTraversal)
{
if (vs)
{
MultipassTileData* tileData = dynamic_cast<MultipassTileData*>(vs->tileVisited(cv, getVolumeTile()));
if (tileData)
{
switch(renderingMode)
{
case(CUBE):
{
// no work required to pre-render
break;
}
case(HULL):
{
if (tileData->frontFaceRttCamera.valid()) tileData->frontFaceRttCamera->accept(*cv);
break;
}
case(CUBE_AND_HULL):
{
// OSG_NOTICE<<"Doing pre-rendering"<<std::endl;
if (tileData->frontFaceRttCamera.valid())
{
// OSG_NOTICE<<" frontFaceRttCamera"<<std::endl;
tileData->frontFaceRttCamera->accept(*cv);
}
if (tileData->backFaceRttCamera.valid())
{
// OSG_NOTICE<<" backFaceRttCamera"<<std::endl;
tileData->backFaceRttCamera->accept(*cv);
}
}
}
}
osg::BoundingBox bb;
if (_transform.valid()) bb.expandBy(_transform->getBound());
if (getVolumeTile()->getNumChildren()>0) bb.expandBy(getVolumeTile()->getBound());
cv->updateCalculatedNearFar(*(cv->getModelViewMatrix()),bb);
}
}
else
{
int shaderMask = 0;
if (_volumeTile->getLayer()->getProperty())
{
ShadingModelVisitor smv;
_volumeTile->getLayer()->getProperty()->accept(smv);
if (smv._usesTransferFunction)
{
shaderMask |= TF_SHADERS;
}
switch(smv._shadingModel)
{
case(VolumeSettings::Isosurface): shaderMask |= ISO_SHADERS; break;
case(VolumeSettings::MaximumIntensityProjection): shaderMask |= MIP_SHADERS; break;
case(VolumeSettings::Light): shaderMask |= LIT_SHADERS; break;
default: shaderMask |= STANDARD_SHADERS; break;
}
}
switch(renderingMode)
{
case(CUBE): shaderMask |= CUBE_SHADERS; break;
case(HULL): shaderMask |= HULL_SHADERS; break;
case(CUBE_AND_HULL): shaderMask |= CUBE_AND_HULL_SHADERS; break;
}
if (vs)
{
MultipassTileData* tileData = dynamic_cast<MultipassTileData*>(vs->getTileData(cv, getVolumeTile()));
if (tileData)
{
Locator* tileLocator = _volumeTile->getLocator();
osg::Matrixd tileToEye = tileLocator->getTransform() * (*(cv->getModelViewMatrix()));
osg::Matrixd eyeToTile;
eyeToTile.invert(tileToEye);
tileData->eyeToTileUniform->set(osg::Matrixf(eyeToTile));
Locator* layerLocator = _volumeTile->getLayer()->getLocator();
if (tileLocator==layerLocator)
{
tileData->tileToImageUniform->set(osg::Matrixf::identity());
}
else
{
osg::Matrixd tileToImage(tileLocator->getTransform() * osg::Matrixd::inverse(layerLocator->getTransform()));
tileData->tileToImageUniform->set(osg::Matrixf(tileToImage));
}
// OSG_NOTICE<<"Updating texgen"<<std::endl;
}
}
osg::ref_ptr<osg::StateSet> moving_stateset = (_whenMovingStateSet.valid() && isMoving(cv)) ? _whenMovingStateSet : 0;
if (moving_stateset.valid())
{
// OSG_NOTICE<<"Using MovingStateSet"<<std::endl;
cv->pushStateSet(moving_stateset.get());
}
osg::ref_ptr<osg::StateSet> program_stateset = _stateSetMap[shaderMask];
if (program_stateset.valid())
{
// OSG_NOTICE<<"Have program_stateset OK."<<std::endl;
cv->pushStateSet(program_stateset.get());
cv->pushStateSet(_volumeRenderStateSet.get());
switch(renderingMode)
{
case(CUBE):
// OSG_NOTICE<<"Travering Transform for CUBE rendering"<<std::endl;
_transform->accept(*cv);
break;
case(HULL):
// OSG_NOTICE<<"Travering children for HULL rendering"<<std::endl;
getVolumeTile()->osg::Group::traverse(*cv);
break;
case(CUBE_AND_HULL):
// OSG_NOTICE<<"Travering Transform for CUBE_AND_HULL rendering"<<std::endl;
_transform->accept(*cv);
//getVolumeTile()->osg::Group::traverse(*cv);
break;
}
cv->popStateSet();
cv->popStateSet();
}
else
{
OSG_NOTICE<<"Warning: No program available for required shader mask "<<shaderMask<<std::endl;
}
if (moving_stateset.valid())
{
// OSG_NOTICE<<"Using MovingStateSet"<<std::endl;
cv->popStateSet();
}
}
}
void MultipassTechnique::cleanSceneGraph()
{
OSG_NOTICE<<"MultipassTechnique::cleanSceneGraph()"<<std::endl;
}
void MultipassTechnique::traverse(osg::NodeVisitor& nv)
{
// OSG_NOTICE<<"MultipassTechnique::traverse(osg::NodeVisitor& nv)"<<std::endl;
if (!_volumeTile) return;
// if app traversal update the frame count.
if (nv.getVisitorType()==osg::NodeVisitor::UPDATE_VISITOR)
{
if (_volumeTile->getDirty()) _volumeTile->init();
osgUtil::UpdateVisitor* uv = nv.asUpdateVisitor();
if (uv)
{
update(uv);
return;
}
}
else if (nv.getVisitorType()==osg::NodeVisitor::CULL_VISITOR)
{
osgUtil::CullVisitor* cv = nv.asCullVisitor();
if (cv)
{
cull(cv);
return;
}
}
if (_volumeTile->getDirty())
{
OSG_INFO<<"******* Doing init ***********"<<std::endl;
_volumeTile->init();
}
}
} // end of osgVolume namespace