From Bruno Hebelin,

"I have adapted to osgShadow the soft shadow map technique described in  "Efficient Soft-Edged Shadows Using Pixel Shader Branching" by Yury Uralsky, Chapter 17 of GPU Gems 2 (Matt Pharr ed. Addison-Wesley).

Here is my code in attachment: basically, it works in the same way as osgShadow/ShadowMap (core code is copied from it) but implements a specific GLSL shader for the soft rendering of penumbra.

I have tested it under Linux with a NVidia graphic card, but there should be no dependency on platform nor on the graphics driver (as far as they support GLSL 2). Screenshots attached show the current results (frame rate bound to v-sync, but the shader takes actually not much time)."
This commit is contained in:
Robert Osfield 2007-08-12 12:12:55 +00:00
parent babe9f6de0
commit 932608f804
3 changed files with 629 additions and 24 deletions

View File

@ -0,0 +1,94 @@
/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 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.
*/
#ifndef OSGSHADOW_SOFTSHADOWMAP
#define OSGSHADOW_SOFTSHADOWMAP 1
#include <osg/Camera>
#include <osg/Material>
#include <osgShadow/ShadowTechnique>
namespace osgShadow {
/** SoftShadowMap provides an implementation of soft shadows with shadow maps.*/
class OSGSHADOW_EXPORT SoftShadowMap : public ShadowTechnique
{
public :
SoftShadowMap();
SoftShadowMap(const SoftShadowMap& es, const osg::CopyOp& copyop=osg::CopyOp::SHALLOW_COPY);
META_Object(osgShadow, SoftShadowMap);
/** Set the texture unit that the shadow texture will be applied on.*/
void setTextureUnit(unsigned int unit);
/** Get the texture unit that the shadow texture will be applied on.*/
unsigned int getTextureUnit() const { return _textureUnit; }
/** Set the values for the ambient bias the shader will use.*/
void setAmbientBias(const osg::Vec2& ambientBias );
/** Set the values for width of the soft penumbra the shader will use.
* Zero is for hard shadow (no penumbra). 0.01 is already very soft penumbra.
* Default is 0.005.*/
void setSoftnessWidth(const float softnesswidth );
/** Set the values for jittering scale the shader will use.
* Zero is no jittering (i.e. see the banding in penumbra)
* High values (>64) cause 'pixelization' of the penumbra.
* Usually but not necessarily power of two number.
* Default is 32. */
void setJitteringScale(const float jitteringscale );
/** Get the values that are used for the ambient bias in the shader.*/
const osg::Vec2& getAmbientBias() const { return _ambientBias; }
/** Get the value used for width of the soft penumbra in the shader.*/
const float getSoftnessWidth() const { return _softnesswidth; }
/** Get the value used for jittering scale in the shader.*/
const float getJitteringScale() const { return _jitteringscale; }
/** initialize the ShadowedScene and local cached data structures.*/
virtual void init();
/** run the update traversal of the ShadowedScene and update any loca chached data structures.*/
virtual void update(osg::NodeVisitor& nv);
/** run the cull traversal of the ShadowedScene and set up the rendering for this ShadowTechnique.*/
virtual void cull(osgUtil::CullVisitor& cv);
/** Clean scene graph from any shadow technique specific nodes, state and drawables.*/
virtual void cleanSceneGraph();
protected :
virtual ~SoftShadowMap() {}
void initJittering(osg::StateSet *);
osg::ref_ptr<osg::Camera> _camera;
osg::ref_ptr<osg::TexGen> _texgen;
osg::ref_ptr<osg::Texture2D> _texture;
osg::ref_ptr<osg::StateSet> _stateset;
unsigned int _textureUnit;
osg::Vec2 _ambientBias;
float _softnesswidth;
float _jitteringscale;
};
}
#endif

View File

@ -1,41 +1,43 @@
IF (DYNAMIC_OPENSCENEGRAPH) IF (DYNAMIC_OPENSCENEGRAPH)
ADD_DEFINITIONS(-DOSGSHADOW_LIBRARY) ADD_DEFINITIONS(-DOSGSHADOW_LIBRARY)
ELSE (DYNAMIC_OPENSCENEGRAPH) ELSE (DYNAMIC_OPENSCENEGRAPH)
ADD_DEFINITIONS(-DOSG_LIBRARY_STATIC) ADD_DEFINITIONS(-DOSG_LIBRARY_STATIC)
ENDIF(DYNAMIC_OPENSCENEGRAPH) ENDIF(DYNAMIC_OPENSCENEGRAPH)
SET(LIB_NAME osgShadow) SET(LIB_NAME osgShadow)
SET(HEADER_PATH ${OpenSceneGraph_SOURCE_DIR}/include/${LIB_NAME}) SET(HEADER_PATH ${OpenSceneGraph_SOURCE_DIR}/include/${LIB_NAME})
SET(LIB_PUBLIC_HEADERS SET(LIB_PUBLIC_HEADERS
${HEADER_PATH}/Export ${HEADER_PATH}/Export
${HEADER_PATH}/OccluderGeometry ${HEADER_PATH}/OccluderGeometry
${HEADER_PATH}/ShadowMap ${HEADER_PATH}/ShadowMap
${HEADER_PATH}/ShadowTechnique ${HEADER_PATH}/ShadowTechnique
${HEADER_PATH}/ShadowTexture ${HEADER_PATH}/ShadowTexture
${HEADER_PATH}/ShadowVolume ${HEADER_PATH}/ShadowVolume
${HEADER_PATH}/ShadowedScene ${HEADER_PATH}/ShadowedScene
${HEADER_PATH}/Version ${HEADER_PATH}/SoftShadowMap
${HEADER_PATH}/Version
) )
# FIXME: For OS X, need flag for Framework or dylib # FIXME: For OS X, need flag for Framework or dylib
ADD_LIBRARY(${LIB_NAME} ADD_LIBRARY(${LIB_NAME}
${OPENSCENEGRAPH_USER_DEFINED_DYNAMIC_OR_STATIC} ${OPENSCENEGRAPH_USER_DEFINED_DYNAMIC_OR_STATIC}
${LIB_PUBLIC_HEADERS} ${LIB_PUBLIC_HEADERS}
OccluderGeometry.cpp OccluderGeometry.cpp
ShadowMap.cpp ShadowMap.cpp
ShadowTechnique.cpp ShadowTechnique.cpp
ShadowTexture.cpp ShadowTexture.cpp
ShadowVolume.cpp ShadowVolume.cpp
ShadowedScene.cpp ShadowedScene.cpp
Version.cpp SoftShadowMap.cpp
Version.cpp
) )
LINK_INTERNAL(${LIB_NAME} LINK_INTERNAL(${LIB_NAME}
osgUtil osgUtil
osgDB osgDB
osg osg
OpenThreads OpenThreads
) )
LINK_CORELIB_DEFAULT(${LIB_NAME}) LINK_CORELIB_DEFAULT(${LIB_NAME})

509
src/osgShadow/SoftShadowMap.cpp Executable file
View File

@ -0,0 +1,509 @@
/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 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 <osgShadow/SoftShadowMap>
#include <osgShadow/ShadowedScene>
#include <osg/Notify>
#include <osg/ComputeBoundsVisitor>
#include <osg/PolygonOffset>
#include <osg/CullFace>
#include <osg/io_utils>
#include <osg/Texture3D>
#include <osg/TexGen>
#include <iostream>
using namespace osgShadow;
//////////////////////////////////////////////////////////////////
// fragment shader
//
// Implementation from Chapter 17, Efficient Soft-Edged Shadows Using Pixel Shader Branching, Yury Uralsky.
// GPU Gems 2, Matt Pharr ed. Addison-Wesley.
//
static const char fShaderSource_noBaseTexture[] =
"#define SAMPLECOUNT 64 \n"
"#define SAMPLECOUNT_D2 32 \n"
"#define INV_SAMPLECOUNT (1.0f / SAMPLECOUNT) \n"
"uniform sampler2DShadow shadowTexture; \n"
"uniform sampler3D jitterMapSampler; \n"
"uniform vec2 ambientBias; \n"
"uniform float softwidth; \n"
"uniform float jscale; \n"
"void main(void) \n"
"{ \n"
" vec4 sceneShadowProj = gl_TexCoord[1]; \n"
" float softFactor = softwidth * sceneShadowProj.w; \n"
" vec4 smCoord = sceneShadowProj; \n"
" vec3 jitterCoord = vec3( gl_FragCoord.xy / jscale, 0.0 ); \n"
" vec4 shadow = vec4(0.0, 0.0, 0.0, 0.0); \n"
// First "cheap" sample test
" const float pass_div = 1.0f / (2.0 * 4); \n"
" for ( int i = 0; i < 4; ++i ) \n"
" { \n"
// Get jitter values in [0,1]; adjust to have values in [-1,1]
" vec4 offset = 2.0 * texture3D( jitterMapSampler, jitterCoord ) -1.0; \n"
" jitterCoord.z += 1.0 / SAMPLECOUNT_D2; \n"
" smCoord.xy = sceneShadowProj.xy + (offset.xy) * softFactor; \n"
" shadow += shadow2DProj( shadowTexture, smCoord ) * pass_div; \n"
" smCoord.xy = sceneShadowProj.xy + (offset.zw) * softFactor; \n"
" shadow += shadow2DProj( shadowTexture, smCoord ) *pass_div; \n"
" } \n"
// skip all the expensive shadow sampling if not needed
" if ( shadow * (shadow -1.0) != 0 ) \n"
" { \n"
" shadow *= pass_div; \n"
" for (int i=0; i<SAMPLECOUNT_D2 -4; ++i){ \n"
" vec4 offset = 2.0 * texture3D( jitterMapSampler, jitterCoord ) - 1.0; \n"
" jitterCoord.z += 1.0 / SAMPLECOUNT_D2; \n"
" smCoord.xy = sceneShadowProj.xy + offset.xy * softFactor; \n"
" shadow += shadow2DProj( shadowTexture, smCoord ) * INV_SAMPLECOUNT; \n"
" smCoord.xy = sceneShadowProj.xy + offset.zw * softFactor; \n"
" shadow += shadow2DProj( shadowTexture, smCoord ) * INV_SAMPLECOUNT; \n"
" } \n"
" } \n"
// apply shadow, modulo the ambient bias
" gl_FragColor = gl_Color * (ambientBias.x + shadow * ambientBias.y); \n"
"} \n";
//////////////////////////////////////////////////////////////////
// fragment shader
//
static const char fShaderSource_withBaseTexture[] =
"#define SAMPLECOUNT 64 \n"
"#define SAMPLECOUNT_D2 32 \n"
"#define INV_SAMPLECOUNT (1.0f / SAMPLECOUNT) \n"
"uniform sampler2D baseTexture; \n"
"uniform sampler2DShadow shadowTexture; \n"
"uniform sampler3D jitterMapSampler; \n"
"uniform vec2 ambientBias; \n"
"uniform float softwidth; \n"
"uniform float jscale; \n"
"void main(void) \n"
"{ \n"
" vec4 sceneShadowProj = gl_TexCoord[1]; \n"
" float softFactor = softwidth * sceneShadowProj.w; \n"
" vec4 smCoord = sceneShadowProj; \n"
" vec3 jitterCoord = vec3( gl_FragCoord.xy / jscale, 0.0 ); \n"
" vec4 shadow = vec4(0.0, 0.0, 0.0, 0.0); \n"
// First "cheap" sample test
" const float pass_div = 1.0f / (2.0 * 4); \n"
" for ( int i = 0; i < 4; ++i ) \n"
" { \n"
// Get jitter values in [0,1]; adjust to have values in [-1,1]
" vec4 offset = 2.0 * texture3D( jitterMapSampler, jitterCoord ) -1.0; \n"
" jitterCoord.z += 1.0 / SAMPLECOUNT_D2; \n"
" smCoord.xy = sceneShadowProj.xy + (offset.xy) * softFactor; \n"
" shadow += shadow2DProj( shadowTexture, smCoord ) * pass_div; \n"
" smCoord.xy = sceneShadowProj.xy + (offset.zw) * softFactor; \n"
" shadow += shadow2DProj( shadowTexture, smCoord ) *pass_div; \n"
" } \n"
// skip all the expensive shadow sampling if not needed
" if ( shadow * (shadow -1.0) != 0 ) \n"
" { \n"
" shadow *= pass_div; \n"
" for (int i=0; i<SAMPLECOUNT_D2 -4; ++i){ \n"
" vec4 offset = 2.0 * texture3D( jitterMapSampler, jitterCoord ) - 1.0; \n"
" jitterCoord.z += 1.0 / SAMPLECOUNT_D2; \n"
" smCoord.xy = sceneShadowProj.xy + offset.xy * softFactor; \n"
" shadow += shadow2DProj( shadowTexture, smCoord ) * INV_SAMPLECOUNT; \n"
" smCoord.xy = sceneShadowProj.xy + offset.zw * softFactor; \n"
" shadow += shadow2DProj( shadowTexture, smCoord ) * INV_SAMPLECOUNT; \n"
" } \n"
" } \n"
// apply color and object base texture
" vec4 color = gl_Color * texture2D( baseTexture, gl_TexCoord[0].xy ); \n"
// apply shadow, modulo the ambient bias
" gl_FragColor = color * (ambientBias.x + shadow * ambientBias.y); \n"
"} \n";
SoftShadowMap::SoftShadowMap():
_textureUnit(1),
_ambientBias(0.5f,0.5f),
_softnesswidth(0.005f),
_jitteringscale(32.f)
{
}
SoftShadowMap::SoftShadowMap(const SoftShadowMap& copy, const osg::CopyOp& copyop):
ShadowTechnique(copy,copyop),
_textureUnit(copy._textureUnit),
_ambientBias(copy._ambientBias),
_softnesswidth(copy._softnesswidth),
_jitteringscale(copy._jitteringscale)
{
}
void SoftShadowMap::setTextureUnit(unsigned int unit)
{
_textureUnit = unit;
}
void SoftShadowMap::setAmbientBias(const osg::Vec2& ambientBias)
{
_ambientBias = ambientBias;
}
void SoftShadowMap::setSoftnessWidth(const float softnesswidth )
{
_softnesswidth = softnesswidth;
}
void SoftShadowMap::setJitteringScale(const float jitteringscale )
{
_jitteringscale = jitteringscale;
}
void SoftShadowMap::init()
{
if (!_shadowedScene) return;
unsigned int tex_width = 1024;
unsigned int tex_height = 1024;
_texture = new osg::Texture2D;
_texture->setTextureSize(tex_width, tex_height);
_texture->setInternalFormat(GL_DEPTH_COMPONENT);
_texture->setSourceType(GL_UNSIGNED_INT);
// Sets GL_TEXTURE_COMPARE_MODE_ARB to GL_COMPARE_R_TO_TEXTURE_ARB
_texture->setShadowComparison(true);
_texture->setShadowCompareFunc(osg::Texture::LEQUAL);
_texture->setShadowTextureMode(osg::Texture::LUMINANCE);
_texture->setFilter(osg::Texture::MIN_FILTER,osg::Texture::LINEAR);
_texture->setFilter(osg::Texture::MAG_FILTER,osg::Texture::LINEAR);
_texture->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE);
_texture->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE);
// set up the render to texture camera.
{
// create the camera
_camera = new osg::Camera;
_camera->setCullCallback(new CameraCullCallback(this));
_camera->setClearMask(GL_DEPTH_BUFFER_BIT);
//_camera->setClearMask(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
_camera->setClearColor(osg::Vec4(1.0f,1.0f,1.0f,1.0f));
_camera->setComputeNearFarMode(osg::Camera::DO_NOT_COMPUTE_NEAR_FAR);
// set viewport
_camera->setViewport(0,0,tex_width,tex_height);
// 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->setRenderTargetImplementation(osg::Camera::SEPERATE_WINDOW);
// attach the texture and use it as the color buffer.
_camera->attach(osg::Camera::DEPTH_BUFFER, _texture.get());
osg::StateSet* stateset = _camera->getOrCreateStateSet();
float factor = 0.0f;
float units = 1.0f;
osg::ref_ptr<osg::PolygonOffset> polygon_offset = new osg::PolygonOffset;
polygon_offset->setFactor(factor);
polygon_offset->setUnits(units);
stateset->setAttribute(polygon_offset.get(), osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE);
stateset->setMode(GL_POLYGON_OFFSET_FILL, osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE);
osg::ref_ptr<osg::CullFace> cull_face = new osg::CullFace;
// set culling to BACK facing (cf message from Wojtek Lewandowski in osg Mailing list)
cull_face->setMode(osg::CullFace::FRONT);
stateset->setAttribute(cull_face.get(), osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE);
stateset->setMode(GL_CULL_FACE, osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE);
}
{
_stateset = new osg::StateSet;
_stateset->setTextureAttributeAndModes(_textureUnit,_texture.get(),osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE);
_stateset->setTextureMode(_textureUnit,GL_TEXTURE_GEN_S,osg::StateAttribute::ON);
_stateset->setTextureMode(_textureUnit,GL_TEXTURE_GEN_T,osg::StateAttribute::ON);
_stateset->setTextureMode(_textureUnit,GL_TEXTURE_GEN_R,osg::StateAttribute::ON);
_stateset->setTextureMode(_textureUnit,GL_TEXTURE_GEN_Q,osg::StateAttribute::ON);
_texgen = new osg::TexGen;
osg::Program* program = new osg::Program;
_stateset->setAttribute(program);
if ( _textureUnit==0)
{
osg::Shader* fragment_shader = new osg::Shader(osg::Shader::FRAGMENT, fShaderSource_noBaseTexture);
program->addShader(fragment_shader);
}
else
{
osg::Shader* fragment_shader = new osg::Shader(osg::Shader::FRAGMENT, fShaderSource_withBaseTexture);
program->addShader(fragment_shader);
osg::Uniform* baseTextureSampler = new osg::Uniform("baseTexture",0);
_stateset->addUniform(baseTextureSampler);
}
osg::Uniform* shadowTextureSampler = new osg::Uniform("shadowTexture",(int)_textureUnit);
_stateset->addUniform(shadowTextureSampler);
osg::Uniform* ambientBias = new osg::Uniform("ambientBias",_ambientBias);
_stateset->addUniform(ambientBias);
// bhbn
// Initialisation of jittering texture
initJittering(_stateset.get());
osg::Uniform* jitterMapSampler = new osg::Uniform("jitterMapSampler",(int)_textureUnit + 1);
_stateset->addUniform(jitterMapSampler);
osg::Uniform* softwidth = new osg::Uniform("softwidth",_softnesswidth);
_stateset->addUniform(softwidth);
osg::Uniform* jscale = new osg::Uniform("jscale",_jitteringscale);
_stateset->addUniform(jscale);
}
_dirty = false;
}
void SoftShadowMap::update(osg::NodeVisitor& nv)
{
_shadowedScene->osg::Group::traverse(nv);
}
void SoftShadowMap::cull(osgUtil::CullVisitor& cv)
{
// record the traversal mask on entry so we can reapply it later.
unsigned int traversalMask = cv.getTraversalMask();
osgUtil::RenderStage* orig_rs = cv.getRenderStage();
// do traversal of shadow recieving scene which does need to be decorated by the shadow map
{
cv.pushStateSet(_stateset.get());
_shadowedScene->osg::Group::traverse(cv);
cv.popStateSet();
}
// need to compute view frustum for RTT camera.
// 1) get the light position
// 2) get the center and extents of the view frustum
const osg::Light* selectLight = 0;
osg::Vec4 lightpos;
osgUtil::PositionalStateContainer::AttrMatrixList& aml = orig_rs->getPositionalStateContainer()->getAttrMatrixList();
for(osgUtil::PositionalStateContainer::AttrMatrixList::iterator itr = aml.begin();
itr != aml.end();
++itr)
{
const osg::Light* light = dynamic_cast<const osg::Light*>(itr->first.get());
if (light)
{
osg::RefMatrix* matrix = itr->second.get();
if (matrix) lightpos = light->getPosition() * (*matrix);
else lightpos = light->getPosition();
selectLight = light;
}
}
osg::Matrix eyeToWorld;
eyeToWorld.invert(*cv.getModelViewMatrix());
lightpos = lightpos * eyeToWorld;
if (selectLight)
{
// get the bounds of the model.
osg::ComputeBoundsVisitor cbbv(osg::NodeVisitor::TRAVERSE_ACTIVE_CHILDREN);
cbbv.setTraversalMask(getShadowedScene()->getCastsShadowTraversalMask());
_shadowedScene->osg::Group::traverse(cbbv);
osg::BoundingBox bb = cbbv.getBoundingBox();
osg::Vec3 position;
if (lightpos[3]!=0.0)
{
position.set(lightpos.x(), lightpos.y(), lightpos.z());
}
else
{
// make an orthographic projection
osg::Vec3 lightDir(lightpos.x(), lightpos.y(), lightpos.z());
lightDir.normalize();
// set the position far away along the light direction
position = lightDir * bb.radius() * 20;
}
float centerDistance = (position-bb.center()).length();
float znear = centerDistance-bb.radius();
float zfar = centerDistance+bb.radius();
float zNearRatio = 0.001f;
if (znear<zfar*zNearRatio) znear = zfar*zNearRatio;
float top = bb.radius();
float right = top;
_camera->setReferenceFrame(osg::Camera::ABSOLUTE_RF);
_camera->setProjectionMatrixAsOrtho(-right, right, -top, top, znear, zfar);
_camera->setViewMatrixAsLookAt(position, bb.center(), osg::Vec3(0.0f,1.0f,0.0f));
// compute the matrix which takes a vertex from local coords into tex coords
// will use this later to specify osg::TexGen..
osg::Matrix MVPT = _camera->getViewMatrix() *
_camera->getProjectionMatrix() *
osg::Matrix::translate(1.0,1.0,1.0) *
osg::Matrix::scale(0.5f,0.5f,0.5f);
_texgen->setMode(osg::TexGen::EYE_LINEAR);
_texgen->setPlanesFromMatrix(MVPT);
cv.setTraversalMask( traversalMask &
getShadowedScene()->getCastsShadowTraversalMask() );
// do RTT camera traversal
_camera->accept(cv);
orig_rs->getPositionalStateContainer()->addPositionedTextureAttribute(_textureUnit, cv.getModelViewMatrix(), _texgen.get());
}
// reapply the original traversal mask
cv.setTraversalMask( traversalMask );
}
void SoftShadowMap::cleanSceneGraph()
{
}
// Implementation from Chapter 17, Efficient Soft-Edged Shadows Using Pixel Shader Branching, Yury Uralsky.
// GPU Gems 2, Matt Pharr ed. Addison-Wesley.
//
// Creates a 3D texture containing jittering data used in the shader to take samples of the shadow map.
void SoftShadowMap::initJittering(osg::StateSet *ss)
{
// create a 3D texture with hw mipmapping
osg::Texture3D* texture3D = new osg::Texture3D;
texture3D->setFilter(osg::Texture3D::MIN_FILTER,osg::Texture3D::NEAREST);
texture3D->setFilter(osg::Texture3D::MAG_FILTER,osg::Texture3D::NEAREST);
texture3D->setWrap(osg::Texture3D::WRAP_S,osg::Texture3D::REPEAT);
texture3D->setWrap(osg::Texture3D::WRAP_T,osg::Texture3D::REPEAT);
texture3D->setWrap(osg::Texture3D::WRAP_R,osg::Texture3D::REPEAT);
texture3D->setUseHardwareMipMapGeneration(true);
const uint size = 16;
const uint gridW = 8;
const uint gridH = 8;
uint R = (gridW * gridH / 2);
texture3D->setTextureSize(size, size, R);
// then create the 3d image to fill with jittering data
osg::Image* image3D = new osg::Image;
unsigned char *data3D = new unsigned char[size * size * R * 4];
for ( uint s = 0; s < size; ++s )
{
for ( uint t = 0; t < size; ++t )
{
float v[4], d[4];
for ( uint r = 0; r < R; ++r )
{
const int x = r % ( gridW / 2 );
const int y = ( gridH - 1 ) - ( r / (gridW / 2) );
// Generate points on a regular gridW x gridH rectangular
// grid. We multiply x by 2 because, we treat 2
// consecutive x each loop iteration. Add 0.5f to be in
// the center of the pixel. x, y belongs to [ 0.0, 1.0 ].
v[0] = float( x * 2 + 0.5f ) / gridW;
v[1] = float( y + 0.5f ) / gridH;
v[2] = float( x * 2 + 1 + 0.5f ) / gridW;
v[3] = v[1];
// Jitter positions. ( 0.5f / w ) == ( 1.0f / 2*w )
v[0] += ((float)rand() * 2.f / RAND_MAX - 1.f) * ( 0.5f / gridW );
v[1] += ((float)rand() * 2.f / RAND_MAX - 1.f) * ( 0.5f / gridH );
v[2] += ((float)rand() * 2.f / RAND_MAX - 1.f) * ( 0.5f / gridW );
v[3] += ((float)rand() * 2.f / RAND_MAX - 1.f) * ( 0.5f / gridH );
// Warp to disk; values in [-1,1]
d[0] = sqrtf( v[1] ) * cosf( 2.f * 3.1415926f * v[0] );
d[1] = sqrtf( v[1] ) * sinf( 2.f * 3.1415926f * v[0] );
d[2] = sqrtf( v[3] ) * cosf( 2.f * 3.1415926f * v[2] );
d[3] = sqrtf( v[3] ) * sinf( 2.f * 3.1415926f * v[2] );
// store d into unsigned values [0,255]
const uint tmp = ( (r * size * size) + (t * size) + s ) * 4;
data3D[ tmp + 0 ] = (unsigned char)( ( 1.f + d[0] ) * 127 );
data3D[ tmp + 1 ] = (unsigned char)( ( 1.f + d[1] ) * 127 );
data3D[ tmp + 2 ] = (unsigned char)( ( 1.f + d[2] ) * 127 );
data3D[ tmp + 3 ] = (unsigned char)( ( 1.f + d[3] ) * 127 );
}
}
}
// the GPU Gem implementation uses a NV specific internal texture format (GL_SIGNED_RGBA_NV)
// In order to make it more generic, we use GL_RGBA4 which should be cross platform.
image3D->setImage(size, size, R, GL_RGBA4, GL_RGBA, GL_UNSIGNED_BYTE, data3D, osg::Image::USE_NEW_DELETE);
texture3D->setImage(image3D);
ss->setTextureAttributeAndModes((int)_textureUnit + 1, texture3D, osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE);
ss->setTextureMode((int)_textureUnit + 1,GL_TEXTURE_GEN_S,osg::StateAttribute::ON);
ss->setTextureMode((int)_textureUnit + 1,GL_TEXTURE_GEN_T,osg::StateAttribute::ON);
ss->setTextureMode((int)_textureUnit + 1,GL_TEXTURE_GEN_R,osg::StateAttribute::ON);
}