838 lines
42 KiB
C++
838 lines
42 KiB
C++
/* -*-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.
|
|
*
|
|
* ViewDependentShadow codes Copyright (C) 2008 Wojciech Lewandowski
|
|
* Thanks to to my company http://www.ai.com.pl for allowing me free this work.
|
|
*/
|
|
|
|
#include <osgShadow/StandardShadowMap>
|
|
#include <osg/PolygonOffset>
|
|
#include <osg/ComputeBoundsVisitor>
|
|
#include <osgShadow/ShadowedScene>
|
|
#include <osg/Geode>
|
|
#include <osg/Geometry>
|
|
#include <osg/CullFace>
|
|
#include <osg/AlphaFunc>
|
|
#include <osg/Point>
|
|
|
|
#include <stdio.h>
|
|
|
|
using namespace osgShadow;
|
|
|
|
#define DISPLAY_SHADOW_TEXEL_TO_PIXEL_ERROR 0
|
|
#define FRAGMENT_SHADERS_ONLY 1
|
|
|
|
|
|
StandardShadowMap::StandardShadowMap():
|
|
BaseClass(),
|
|
_polygonOffsetFactor( 1.1f ),
|
|
_polygonOffsetUnits( 4.0f ),
|
|
_textureSize( 1024, 1024 ),
|
|
_baseTextureUnit( 0 ),
|
|
_shadowTextureUnit( 1 ),
|
|
_baseTextureCoordIndex( 0 ),
|
|
_shadowTextureCoordIndex( 1 )
|
|
|
|
{
|
|
#if FRAGMENT_SHADERS_ONLY
|
|
_mainFragmentShader = new osg::Shader( osg::Shader::FRAGMENT,
|
|
" // following expressions are auto modified - do not change them: \n"
|
|
" // gl_TexCoord[0] 0 - can be subsituted with other index \n"
|
|
" \n"
|
|
"float DynamicShadow( ); \n"
|
|
" \n"
|
|
"uniform sampler2D baseTexture; \n"
|
|
" \n"
|
|
"void main(void) \n"
|
|
"{ \n"
|
|
" vec4 colorAmbientEmissive = gl_FrontLightModelProduct.sceneColor; \n"
|
|
// " // Add ambient from Light of index = 0 \n"
|
|
// " colorAmbientEmissive += gl_FrontLightProduct[0].ambient; \n"
|
|
" vec4 color = texture2D( baseTexture, gl_TexCoord[0].xy ); \n"
|
|
" color *= mix( colorAmbientEmissive, gl_Color, DynamicShadow() ); \n"
|
|
#if DISPLAY_SHADOW_TEXEL_TO_PIXEL_ERROR
|
|
" color.xy = abs( dFdy( gl_TexCoord[1].xy / gl_TexCoord[1].w ) )* 1024.0; \n"
|
|
" color.z = color.y; \n"
|
|
" color.x = color.z; \n"
|
|
" color.y = color.z; \n"
|
|
" color.a = 1.0; \n"
|
|
#endif
|
|
// " float fog = clamp((gl_Fog.end - gl_FogFragCoord)*gl_Fog.scale, 0.,1.);\n"
|
|
// " color.rgb = mix( gl_Fog.color.rgb, color.rgb, fog ); \n"
|
|
" gl_FragColor = color; \n"
|
|
"} \n" );
|
|
|
|
_shadowFragmentShader = new osg::Shader( osg::Shader::FRAGMENT,
|
|
" // following expressions are auto modified - do not change them: \n"
|
|
" // gl_TexCoord[1] 1 - can be subsituted with other index \n"
|
|
" \n"
|
|
"uniform sampler2DShadow shadowTexture; \n"
|
|
" \n"
|
|
"float DynamicShadow( ) \n"
|
|
"{ \n"
|
|
" return shadow2DProj( shadowTexture, gl_TexCoord[1] ).r; \n"
|
|
"} \n" );
|
|
|
|
|
|
_shadowVertexShader = NULL;
|
|
_mainVertexShader = NULL;
|
|
|
|
#else
|
|
_mainFragmentShader = new osg::Shader( osg::Shader::FRAGMENT,
|
|
" // following expressions are auto modified - do not change them: \n"
|
|
" // gl_TexCoord[0] 0 - can be subsituted with other index \n"
|
|
" \n"
|
|
"float DynamicShadow( ); \n"
|
|
" \n"
|
|
"varying vec4 colorAmbientEmissive; \n"
|
|
" \n"
|
|
"uniform sampler2D baseTexture; \n"
|
|
" \n"
|
|
"void main(void) \n"
|
|
"{ \n"
|
|
" vec4 color = texture2D( baseTexture, gl_TexCoord[0].xy ); \n"
|
|
" color *= mix( colorAmbientEmissive, gl_Color, DynamicShadow() ); \n"
|
|
#if DISPLAY_SHADOW_TEXEL_TO_PIXEL_ERROR
|
|
" color.xy = abs( dFdy( gl_TexCoord[1].xy / gl_TexCoord[1].w ) )* 1024.0; \n"
|
|
" color.z = color.y; \n"
|
|
" color.x = color.z; \n"
|
|
" color.y = color.z; \n"
|
|
" color.a = 1.0; \n"
|
|
#endif
|
|
// " float fog = clamp((gl_Fog.end - gl_FogFragCoord)*gl_Fog.scale, 0.,1.);\n"
|
|
// " color.rgb = mix( gl_Fog.color.rgb, color.rgb, fog ); \n"
|
|
" gl_FragColor = color; \n"
|
|
"} \n" );
|
|
|
|
|
|
_shadowFragmentShader = new osg::Shader( osg::Shader::FRAGMENT,
|
|
" // following expressions are auto modified - do not change them: \n"
|
|
" // gl_TexCoord[1] 1 - can be subsituted with other index \n"
|
|
" \n"
|
|
"uniform sampler2DShadow shadowTexture; \n"
|
|
" \n"
|
|
"float DynamicShadow( ) \n"
|
|
"{ \n"
|
|
" return shadow2DProj( shadowTexture, gl_TexCoord[1] ).r; \n"
|
|
"} \n" );
|
|
|
|
|
|
|
|
_shadowVertexShader = new osg::Shader( osg::Shader::VERTEX,
|
|
" // following expressions are auto modified - do not change them: \n"
|
|
" // gl_TexCoord[1] 1 - can be subsituted with other index \n"
|
|
" // gl_EyePlaneS[1] 1 - can be subsituted with other index \n"
|
|
" // gl_EyePlaneT[1] 1 - can be subsituted with other index \n"
|
|
" // gl_EyePlaneR[1] 1 - can be subsituted with other index \n"
|
|
" // gl_EyePlaneQ[1] 1 - can be subsituted with other index \n"
|
|
" \n"
|
|
"void DynamicShadow( in vec4 ecPosition ) \n"
|
|
"{ \n"
|
|
" // generate coords for shadow mapping \n"
|
|
" gl_TexCoord[1].s = dot( ecPosition, gl_EyePlaneS[1] ); \n"
|
|
" gl_TexCoord[1].t = dot( ecPosition, gl_EyePlaneT[1] ); \n"
|
|
" gl_TexCoord[1].p = dot( ecPosition, gl_EyePlaneR[1] ); \n"
|
|
" gl_TexCoord[1].q = dot( ecPosition, gl_EyePlaneQ[1] ); \n"
|
|
"} \n" );
|
|
|
|
_mainVertexShader = new osg::Shader( osg::Shader::VERTEX,
|
|
" // following expressions are auto modified - do not change them: \n"
|
|
" // gl_TexCoord[0] 0 - can be subsituted with other index \n"
|
|
" // gl_TextureMatrix[0] 0 - can be subsituted with other index \n"
|
|
" // gl_MultiTexCoord0 0 - can be subsituted with other index \n"
|
|
" \n"
|
|
"const int NumEnabledLights = 1; \n"
|
|
" \n"
|
|
"void DynamicShadow( in vec4 ecPosition ); \n"
|
|
" \n"
|
|
"varying vec4 colorAmbientEmissive; \n"
|
|
" \n"
|
|
"void SpotLight(in int i, \n"
|
|
" in vec3 eye, \n"
|
|
" in vec3 ecPosition3, \n"
|
|
" in vec3 normal, \n"
|
|
" inout vec4 ambient, \n"
|
|
" inout vec4 diffuse, \n"
|
|
" inout vec4 specular) \n"
|
|
"{ \n"
|
|
" float nDotVP; // normal . light direction \n"
|
|
" float nDotHV; // normal . light half vector \n"
|
|
" float pf; // power factor \n"
|
|
" float spotDot; // cosine of angle between spotlight \n"
|
|
" float spotAttenuation; // spotlight attenuation factor \n"
|
|
" float attenuation; // computed attenuation factor \n"
|
|
" float d; // distance from surface to light source \n"
|
|
" vec3 VP; // direction from surface to light position \n"
|
|
" vec3 halfVector; // direction of maximum highlights \n"
|
|
" \n"
|
|
" // Compute vector from surface to light position \n"
|
|
" VP = vec3(gl_LightSource[i].position) - ecPosition3; \n"
|
|
" \n"
|
|
" // Compute distance between surface and light position \n"
|
|
" d = length(VP); \n"
|
|
" \n"
|
|
" // Normalize the vector from surface to light position \n"
|
|
" VP = normalize(VP); \n"
|
|
" \n"
|
|
" // Compute attenuation \n"
|
|
" attenuation = 1.0 / (gl_LightSource[i].constantAttenuation + \n"
|
|
" gl_LightSource[i].linearAttenuation * d + \n"
|
|
" gl_LightSource[i].quadraticAttenuation *d*d); \n"
|
|
" \n"
|
|
" // See if point on surface is inside cone of illumination \n"
|
|
" spotDot = dot(-VP, normalize(gl_LightSource[i].spotDirection)); \n"
|
|
" \n"
|
|
" if (spotDot < gl_LightSource[i].spotCosCutoff) \n"
|
|
" spotAttenuation = 0.0; // light adds no contribution \n"
|
|
" else \n"
|
|
" spotAttenuation = pow(spotDot, gl_LightSource[i].spotExponent);\n"
|
|
" \n"
|
|
" // Combine the spotlight and distance attenuation. \n"
|
|
" attenuation *= spotAttenuation; \n"
|
|
" \n"
|
|
" halfVector = normalize(VP + eye); \n"
|
|
" \n"
|
|
" nDotVP = max(0.0, dot(normal, VP)); \n"
|
|
" nDotHV = max(0.0, dot(normal, halfVector)); \n"
|
|
" \n"
|
|
" if (nDotVP == 0.0) \n"
|
|
" pf = 0.0; \n"
|
|
" else \n"
|
|
" pf = pow(nDotHV, gl_FrontMaterial.shininess); \n"
|
|
" \n"
|
|
" ambient += gl_LightSource[i].ambient * attenuation; \n"
|
|
" diffuse += gl_LightSource[i].diffuse * nDotVP * attenuation; \n"
|
|
" specular += gl_LightSource[i].specular * pf * attenuation; \n"
|
|
"} \n"
|
|
" \n"
|
|
"void PointLight(in int i, \n"
|
|
" in vec3 eye, \n"
|
|
" in vec3 ecPosition3, \n"
|
|
" in vec3 normal, \n"
|
|
" inout vec4 ambient, \n"
|
|
" inout vec4 diffuse, \n"
|
|
" inout vec4 specular) \n"
|
|
"{ \n"
|
|
" float nDotVP; // normal . light direction \n"
|
|
" float nDotHV; // normal . light half vector \n"
|
|
" float pf; // power factor \n"
|
|
" float attenuation; // computed attenuation factor \n"
|
|
" float d; // distance from surface to light source \n"
|
|
" vec3 VP; // direction from surface to light position \n"
|
|
" vec3 halfVector; // direction of maximum highlights \n"
|
|
" \n"
|
|
" // Compute vector from surface to light position \n"
|
|
" VP = vec3(gl_LightSource[i].position) - ecPosition3; \n"
|
|
" \n"
|
|
" // Compute distance between surface and light position \n"
|
|
" d = length(VP); \n"
|
|
" \n"
|
|
" // Normalize the vector from surface to light position \n"
|
|
" VP = normalize(VP); \n"
|
|
" \n"
|
|
" // Compute attenuation \n"
|
|
" attenuation = 1.0 / (gl_LightSource[i].constantAttenuation + \n"
|
|
" gl_LightSource[i].linearAttenuation * d + \n"
|
|
" gl_LightSource[i].quadraticAttenuation * d*d);\n"
|
|
" \n"
|
|
" halfVector = normalize(VP + eye); \n"
|
|
" \n"
|
|
" nDotVP = max(0.0, dot(normal, VP)); \n"
|
|
" nDotHV = max(0.0, dot(normal, halfVector)); \n"
|
|
" \n"
|
|
" if (nDotVP == 0.0) \n"
|
|
" pf = 0.0; \n"
|
|
" else \n"
|
|
" pf = pow(nDotHV, gl_FrontMaterial.shininess); \n"
|
|
" \n"
|
|
" ambient += gl_LightSource[i].ambient * attenuation; \n"
|
|
" diffuse += gl_LightSource[i].diffuse * nDotVP * attenuation; \n"
|
|
" specular += gl_LightSource[i].specular * pf * attenuation; \n"
|
|
"} \n"
|
|
" \n"
|
|
"void DirectionalLight(in int i, \n"
|
|
" in vec3 normal, \n"
|
|
" inout vec4 ambient, \n"
|
|
" inout vec4 diffuse, \n"
|
|
" inout vec4 specular) \n"
|
|
"{ \n"
|
|
" float nDotVP; // normal . light direction \n"
|
|
" float nDotHV; // normal . light half vector \n"
|
|
" float pf; // power factor \n"
|
|
" \n"
|
|
" nDotVP = max(0.0, dot(normal, \n"
|
|
" normalize(vec3(gl_LightSource[i].position)))); \n"
|
|
" nDotHV = max(0.0, dot(normal, \n"
|
|
" vec3(gl_LightSource[i].halfVector))); \n"
|
|
" \n"
|
|
" if (nDotVP == 0.0) \n"
|
|
" pf = 0.0; \n"
|
|
" else \n"
|
|
" pf = pow(nDotHV, gl_FrontMaterial.shininess); \n"
|
|
" \n"
|
|
" ambient += gl_LightSource[i].ambient; \n"
|
|
" diffuse += gl_LightSource[i].diffuse * nDotVP; \n"
|
|
" specular += gl_LightSource[i].specular * pf; \n"
|
|
"} \n"
|
|
" \n"
|
|
"void main( ) \n"
|
|
"{ \n"
|
|
" // Transform vertex to clip space \n"
|
|
" gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex; \n"
|
|
" vec3 normal = normalize( gl_NormalMatrix * gl_Normal ); \n"
|
|
" \n"
|
|
" vec4 ecPos = gl_ModelViewMatrix * gl_Vertex; \n"
|
|
" float ecLen = length( ecPos ); \n"
|
|
" vec3 ecPosition3 = ecPos.xyz / ecPos.w; \n"
|
|
" \n"
|
|
" vec3 eye = vec3( 0.0, 0.0, 1.0 ); \n"
|
|
" //vec3 eye = -normalize(ecPosition3); \n"
|
|
" \n"
|
|
" DynamicShadow( ecPos ); \n"
|
|
" \n"
|
|
" gl_TexCoord[0] = gl_TextureMatrix[0] * gl_MultiTexCoord0; \n"
|
|
" \n"
|
|
" // Front Face lighting \n"
|
|
" \n"
|
|
" // Clear the light intensity accumulators \n"
|
|
" vec4 amb = vec4(0.0); \n"
|
|
" vec4 diff = vec4(0.0); \n"
|
|
" vec4 spec = vec4(0.0); \n"
|
|
" \n"
|
|
" // Loop through enabled lights, compute contribution from each \n"
|
|
" for (int i = 0; i < NumEnabledLights; i++) \n"
|
|
" { \n"
|
|
" if (gl_LightSource[i].position.w == 0.0) \n"
|
|
" DirectionalLight(i, normal, amb, diff, spec); \n"
|
|
" else if (gl_LightSource[i].spotCutoff == 180.0) \n"
|
|
" PointLight(i, eye, ecPosition3, normal, amb, diff, spec); \n"
|
|
" else \n"
|
|
" SpotLight(i, eye, ecPosition3, normal, amb, diff, spec); \n"
|
|
" } \n"
|
|
" \n"
|
|
" colorAmbientEmissive = gl_FrontLightModelProduct.sceneColor + \n"
|
|
" amb * gl_FrontMaterial.ambient; \n"
|
|
" \n"
|
|
" gl_FrontColor = colorAmbientEmissive + \n"
|
|
" diff * gl_FrontMaterial.diffuse; \n"
|
|
" \n"
|
|
" gl_FrontSecondaryColor = vec4(spec*gl_FrontMaterial.specular); \n"
|
|
" \n"
|
|
" gl_BackColor = gl_FrontColor; \n"
|
|
" gl_BackSecondaryColor = gl_FrontSecondaryColor; \n"
|
|
" \n"
|
|
" gl_FogFragCoord = ecLen; \n"
|
|
"} \n" );
|
|
#endif
|
|
}
|
|
|
|
StandardShadowMap::StandardShadowMap(const StandardShadowMap& copy, const osg::CopyOp& copyop) :
|
|
BaseClass(copy,copyop),
|
|
_polygonOffsetFactor( copy._polygonOffsetFactor ),
|
|
_polygonOffsetUnits( copy._polygonOffsetUnits ),
|
|
_textureSize( copy._textureSize ),
|
|
_baseTextureUnit( copy._baseTextureUnit ),
|
|
_shadowTextureUnit( copy._shadowTextureUnit ),
|
|
_baseTextureCoordIndex( copy._baseTextureCoordIndex ),
|
|
_shadowTextureCoordIndex( copy._shadowTextureCoordIndex )
|
|
{
|
|
if( copy._mainVertexShader.valid() )
|
|
_mainVertexShader = dynamic_cast<osg::Shader*>
|
|
( copy._mainVertexShader->clone(copyop) );
|
|
|
|
if( copy._mainFragmentShader.valid() )
|
|
_mainFragmentShader = dynamic_cast<osg::Shader*>
|
|
( copy._mainFragmentShader->clone(copyop) );
|
|
|
|
if( copy._shadowVertexShader.valid() )
|
|
_shadowVertexShader = dynamic_cast<osg::Shader*>
|
|
( copy._shadowVertexShader->clone(copyop) );
|
|
|
|
if( copy._shadowFragmentShader.valid() )
|
|
_shadowFragmentShader = dynamic_cast<osg::Shader*>
|
|
( copy._shadowFragmentShader->clone(copyop) );
|
|
}
|
|
|
|
StandardShadowMap::~StandardShadowMap(void)
|
|
{
|
|
|
|
}
|
|
|
|
void StandardShadowMap::resizeGLObjectBuffers(unsigned int maxSize)
|
|
{
|
|
osg::resizeGLObjectBuffers(_mainVertexShader, maxSize);
|
|
osg::resizeGLObjectBuffers(_mainFragmentShader, maxSize);
|
|
osg::resizeGLObjectBuffers(_shadowVertexShader, maxSize);
|
|
osg::resizeGLObjectBuffers(_shadowFragmentShader, maxSize);
|
|
|
|
DebugShadowMap::resizeGLObjectBuffers(maxSize);
|
|
}
|
|
|
|
void StandardShadowMap::releaseGLObjects(osg::State* state) const
|
|
{
|
|
osg::releaseGLObjects(_mainVertexShader, state);
|
|
osg::releaseGLObjects(_mainFragmentShader, state);
|
|
osg::releaseGLObjects(_shadowVertexShader, state);
|
|
osg::releaseGLObjects(_shadowFragmentShader, state);
|
|
|
|
DebugShadowMap::releaseGLObjects(state);
|
|
}
|
|
|
|
void StandardShadowMap::ViewData::resizeGLObjectBuffers(unsigned int maxSize)
|
|
{
|
|
osg::resizeGLObjectBuffers(_stateset, maxSize);
|
|
}
|
|
|
|
void StandardShadowMap::ViewData::releaseGLObjects(osg::State* state) const
|
|
{
|
|
osg::releaseGLObjects(_stateset, state);
|
|
}
|
|
|
|
|
|
void StandardShadowMap::updateTextureCoordIndices( unsigned int fromTextureCoordIndex, unsigned int toTextureCoordIndex )
|
|
{
|
|
|
|
if( fromTextureCoordIndex == toTextureCoordIndex ) return;
|
|
|
|
const char *expressions[] = {
|
|
"gl_TexCoord[","]",
|
|
"gl_TextureMatrix[","]",
|
|
"gl_MultiTexCoord","",
|
|
"gl_EyePlaneS[","]",
|
|
"gl_EyePlaneT[","]",
|
|
"gl_EyePlaneR[","]",
|
|
"gl_EyePlaneQ[","]"
|
|
};
|
|
|
|
for( unsigned int i = 0;
|
|
i < sizeof( expressions ) / sizeof( expressions[0] );
|
|
i+=2 )
|
|
{
|
|
char acFrom[ 32 ], acTo[32];
|
|
|
|
// its not elegant to mix stdio & stl strings
|
|
// but in this context I do an exception for cleaner code
|
|
|
|
sprintf( acFrom, "%s%d%s", expressions[i], fromTextureCoordIndex, expressions[i+1]);
|
|
sprintf( acTo, "%s%d%s", expressions[i], toTextureCoordIndex, expressions[i+1]);
|
|
|
|
std::string from( acFrom ), to( acTo );
|
|
|
|
searchAndReplaceShaderSource( getShadowVertexShader(), from, to );
|
|
searchAndReplaceShaderSource( getShadowFragmentShader(), from, to );
|
|
searchAndReplaceShaderSource( getMainVertexShader(), from, to );
|
|
searchAndReplaceShaderSource( getMainFragmentShader(), from, to );
|
|
}
|
|
|
|
dirty();
|
|
}
|
|
|
|
void StandardShadowMap::searchAndReplaceShaderSource
|
|
( osg::Shader* shader, std::string fromString, std::string toString )
|
|
{
|
|
if( !shader || fromString == toString ) return;
|
|
|
|
const std::string & srceString = shader->getShaderSource();
|
|
std::string destString;
|
|
|
|
std::string::size_type fromLength = fromString.length();
|
|
std::string::size_type srceLength = srceString.length();
|
|
|
|
for( std::string::size_type pos = 0; pos < srceLength; )
|
|
{
|
|
std::string::size_type end = srceString.find( fromString, pos );
|
|
|
|
if( end == std::string::npos )
|
|
end = srceLength;
|
|
|
|
destString.append( srceString, pos, end - pos );
|
|
|
|
if( end == srceLength )
|
|
break;
|
|
|
|
destString.append( toString );
|
|
pos = end + fromLength;
|
|
}
|
|
|
|
shader->setShaderSource( destString );
|
|
}
|
|
|
|
void StandardShadowMap::ViewData::cull()
|
|
{
|
|
// step 1:
|
|
// cull shadowed scene ie put into render bins and states into stage graphs
|
|
cullShadowReceivingScene( );
|
|
|
|
// step 2:
|
|
// find the light casting our shadows
|
|
osg::Vec4 lightPos;
|
|
osg::Vec3 lightDir;
|
|
osg::Vec3 lightUp( 0,0,0 ); // force computing most approprate dir
|
|
const osg::Light *light = selectLight( lightPos, lightDir );
|
|
|
|
if ( !light )
|
|
return;// bail out - no shadowing needed in darkest night
|
|
|
|
// step 3:
|
|
// compute shadow casting matrices and apply them to shadow map camera
|
|
aimShadowCastingCamera( light, lightPos, lightDir, lightUp );
|
|
|
|
// step 4:
|
|
// cull scene casting shadow and generate render
|
|
cullShadowCastingScene( );
|
|
|
|
// step 5:
|
|
// setup texgen generating shadow map coords for the shadow receiving scene
|
|
addShadowReceivingTexGen( );
|
|
|
|
BaseClass::ViewData::cull();
|
|
}
|
|
|
|
void StandardShadowMap::ViewData::init( ThisClass *st, osgUtil::CullVisitor *cv )
|
|
{
|
|
BaseClass::ViewData::init( st, cv );
|
|
|
|
_lightPtr = &st->_light;
|
|
_shadowTextureUnitPtr = &st->_shadowTextureUnit;
|
|
_baseTextureUnitPtr = &st->_baseTextureUnit;
|
|
|
|
{ // Setup shadow texture
|
|
osg::Texture2D * texture = new osg::Texture2D;
|
|
texture->setTextureSize( st->_textureSize.x(), st->_textureSize.y());
|
|
texture->setInternalFormat(GL_DEPTH_COMPONENT);
|
|
texture->setShadowComparison(true);
|
|
texture->setShadowTextureMode(osg::Texture2D::LUMINANCE);
|
|
texture->setFilter(osg::Texture2D::MIN_FILTER,osg::Texture2D::LINEAR);
|
|
texture->setFilter(osg::Texture2D::MAG_FILTER,osg::Texture2D::LINEAR);
|
|
|
|
// the shadow comparison should fail if object is outside the texture
|
|
texture->setWrap(osg::Texture2D::WRAP_S,osg::Texture2D::CLAMP_TO_BORDER);
|
|
texture->setWrap(osg::Texture2D::WRAP_T,osg::Texture2D::CLAMP_TO_BORDER);
|
|
texture->setBorderColor(osg::Vec4(1.0f,1.0f,1.0f,1.0f));
|
|
_texture = texture;
|
|
}
|
|
|
|
_camera = new osg::Camera;
|
|
{ // Setup shadow map camera
|
|
_camera->setName( "ShadowCamera" );
|
|
#if 0 // Absolute reference frame INHERIT_VIEWPOINT works better than this
|
|
_camera->setCullingMode
|
|
( _camera->getCullingMode() & ~osg::CullSettings::SMALL_FEATURE_CULLING );
|
|
#endif
|
|
_camera->setReferenceFrame(osg::Camera::ABSOLUTE_RF_INHERIT_VIEWPOINT);
|
|
_camera->setCullCallback(new CameraCullCallback( st ));
|
|
_camera->setClearMask(GL_DEPTH_BUFFER_BIT);
|
|
|
|
#if 0 // Left in case of some debug testing
|
|
_camera->setClearMask(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
|
|
_camera->setClearColor( osg::Vec4(1.0f,1.0f,1.0f,1.0f) );
|
|
#endif
|
|
_camera->setComputeNearFarMode(osg::Camera::DO_NOT_COMPUTE_NEAR_FAR);
|
|
_camera->setViewport(0,0, st->_textureSize.x(), st->_textureSize.y() );
|
|
_camera->setRenderOrder(osg::Camera::PRE_RENDER);
|
|
_camera->setRenderTargetImplementation(osg::Camera::FRAME_BUFFER_OBJECT);
|
|
_camera->attach(osg::Camera::DEPTH_BUFFER, _texture.get());
|
|
}
|
|
|
|
_texgen = new osg::TexGen;
|
|
|
|
_stateset = new osg::StateSet;
|
|
{ // Create and add fake texture for use with nodes without any texture
|
|
osg::Image * image = new osg::Image;
|
|
image->allocateImage( 1, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE );
|
|
*(osg::Vec4ub*)image->data() = osg::Vec4ub( 0xFF, 0xFF, 0xFF, 0xFF );
|
|
|
|
osg::Texture2D* fakeTex = new osg::Texture2D( image );
|
|
fakeTex->setWrap(osg::Texture2D::WRAP_S,osg::Texture2D::REPEAT);
|
|
fakeTex->setWrap(osg::Texture2D::WRAP_T,osg::Texture2D::REPEAT);
|
|
fakeTex->setFilter(osg::Texture2D::MIN_FILTER,osg::Texture2D::NEAREST);
|
|
fakeTex->setFilter(osg::Texture2D::MAG_FILTER,osg::Texture2D::NEAREST);
|
|
|
|
_stateset->setTextureAttribute(st->_baseTextureUnit,fakeTex,osg::StateAttribute::ON);
|
|
_stateset->setTextureMode(st->_baseTextureUnit,GL_TEXTURE_2D,osg::StateAttribute::ON);
|
|
_stateset->setTextureMode(st->_baseTextureUnit,GL_TEXTURE_3D,osg::StateAttribute::OFF);
|
|
#if !defined(OSG_GLES1_AVAILABLE) && !defined(OSG_GLES2_AVAILABLE) && !defined(OSG_GLES3_AVAILABLE)
|
|
_stateset->setTextureMode(st->_baseTextureUnit,GL_TEXTURE_1D,osg::StateAttribute::OFF);
|
|
#endif
|
|
}
|
|
|
|
{ // Add shadow texture
|
|
_stateset->setTextureAttributeAndModes(st->_shadowTextureUnit,_texture.get(),osg::StateAttribute::ON);
|
|
_stateset->setTextureMode(st->_shadowTextureUnit,GL_TEXTURE_GEN_S,osg::StateAttribute::ON);
|
|
_stateset->setTextureMode(st->_shadowTextureUnit,GL_TEXTURE_GEN_T,osg::StateAttribute::ON);
|
|
_stateset->setTextureMode(st->_shadowTextureUnit,GL_TEXTURE_GEN_R,osg::StateAttribute::ON);
|
|
_stateset->setTextureMode(st->_shadowTextureUnit,GL_TEXTURE_GEN_Q,osg::StateAttribute::ON);
|
|
}
|
|
|
|
{ // Setup shaders used in shadow casting
|
|
osg::Program * program = new osg::Program();
|
|
_stateset->setAttribute( program );
|
|
|
|
if( st->_shadowFragmentShader.valid() )
|
|
program->addShader( st->_shadowFragmentShader.get() );
|
|
|
|
if( st->_mainFragmentShader.valid() )
|
|
program->addShader( st->_mainFragmentShader.get() );
|
|
|
|
if( st->_shadowVertexShader.valid() )
|
|
program->addShader( st->_shadowVertexShader.get() );
|
|
|
|
if( st->_mainVertexShader.valid() )
|
|
program->addShader( st->_mainVertexShader.get() );
|
|
|
|
_stateset->addUniform
|
|
( new osg::Uniform( "baseTexture", int( st->_baseTextureUnit ) ) );
|
|
_stateset->addUniform
|
|
( new osg::Uniform( "shadowTexture", int( st->_shadowTextureUnit ) ) );
|
|
}
|
|
|
|
{ // Setup states used for shadow map generation
|
|
osg::StateSet * stateset = _camera->getOrCreateStateSet();
|
|
|
|
stateset->setAttribute(
|
|
new osg::PolygonOffset( st->_polygonOffsetFactor, st->_polygonOffsetUnits ),
|
|
osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE );
|
|
|
|
stateset->setMode( GL_POLYGON_OFFSET_FILL,
|
|
osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE );
|
|
|
|
// aggressive optimization
|
|
stateset->setRenderBinDetails( 0, "RenderBin",
|
|
osg::StateSet::OVERRIDE_RENDERBIN_DETAILS );
|
|
|
|
// Assure that AlphaTest/AlphaRef works when redirecting all drawables to single bin.
|
|
// If AlphaFunc/AlphaTest is off - transparent objects will cast solid blocky shadows.
|
|
// No override to allow users change this policy in the model if really want solid shadows.
|
|
stateset->setAttributeAndModes
|
|
( new osg::AlphaFunc( osg::AlphaFunc::GREATER, 0 ), osg::StateAttribute::ON );
|
|
|
|
// aggressive optimization
|
|
stateset->setAttributeAndModes
|
|
( new osg::ColorMask( false, false, false, false ),
|
|
osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE );
|
|
|
|
// note soft (attribute only no mode override) setting. When this works ?
|
|
// 1. for objects prepared for backface culling
|
|
// because they usually also set CullFace and CullMode on in their state
|
|
// For them we override CullFace but CullMode remains set by them
|
|
// 2. For one faced, trees, and similar objects which cannot use
|
|
// backface nor front face so they usually use CullMode off set here.
|
|
// In this case we will draw them in their entirety.
|
|
|
|
stateset->setAttribute( new osg::CullFace( osg::CullFace::FRONT ),
|
|
osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE );
|
|
|
|
// make sure GL_CULL_FACE is off by default
|
|
// we assume that if object has cull face attribute set to back
|
|
// it will also set cull face mode ON so no need for override
|
|
stateset->setMode( GL_CULL_FACE, osg::StateAttribute::OFF );
|
|
|
|
// optimization attributes
|
|
osg::Program* program = new osg::Program;
|
|
stateset->setAttribute( program, osg::StateAttribute::OVERRIDE | osg::StateAttribute::ON );
|
|
stateset->setMode
|
|
( GL_LIGHTING, osg::StateAttribute::OVERRIDE | osg::StateAttribute::OFF );
|
|
stateset->setMode
|
|
( GL_BLEND, osg::StateAttribute::OVERRIDE | osg::StateAttribute::OFF );
|
|
|
|
#if 0 // fixed pipeline seems faster (at least on my 7800)
|
|
program->addShader( new osg::Shader( osg::Shader::FRAGMENT,
|
|
"uniform sampler2D texture; \n"
|
|
"void main(void) \n"
|
|
"{ \n"
|
|
" gl_FragColor = texture2D( texture, gl_TexCoord[0].xy ); \n"
|
|
"} \n"
|
|
) ); // program->addShader Fragment
|
|
|
|
program->addShader( new osg::Shader( osg::Shader::VERTEX,
|
|
"void main(void) \n"
|
|
"{ \n"
|
|
" gl_Position = ftransform(); \n"
|
|
" gl_TexCoord[0] = gl_MultiTexCoord0; \n"
|
|
"} \n"
|
|
) ); // program->addShader Vertex
|
|
#endif
|
|
|
|
for( unsigned stage = 1; stage < 4; stage ++ )
|
|
{
|
|
#if !defined(OSG_GLES1_AVAILABLE) && !defined(OSG_GLES2_AVAILABLE) && !defined(OSG_GLES3_AVAILABLE)
|
|
stateset->setTextureMode( stage, GL_TEXTURE_1D, osg::StateAttribute::OVERRIDE | osg::StateAttribute::OFF );
|
|
#endif
|
|
stateset->setTextureMode( stage, GL_TEXTURE_2D, osg::StateAttribute::OVERRIDE | osg::StateAttribute::OFF );
|
|
stateset->setTextureMode( stage, GL_TEXTURE_3D, osg::StateAttribute::OVERRIDE | osg::StateAttribute::OFF );
|
|
}
|
|
}
|
|
}
|
|
|
|
const osg::Light* StandardShadowMap::ViewData::selectLight
|
|
( osg::Vec4 & lightPos, osg::Vec3 & lightDir )
|
|
{
|
|
const osg::Light* light = 0;
|
|
|
|
//MR testing giving a specific light
|
|
osgUtil::RenderStage * rs = _cv->getRenderStage();
|
|
|
|
osgUtil::PositionalStateContainer::AttrMatrixList& aml =
|
|
rs->getPositionalStateContainer()->getAttrMatrixList();
|
|
|
|
osg::RefMatrix* matrix = 0;
|
|
|
|
for(osgUtil::PositionalStateContainer::AttrMatrixList::iterator itr = aml.begin();
|
|
itr != aml.end();
|
|
++itr)
|
|
{
|
|
const osg::Light* found = dynamic_cast<const osg::Light*>(itr->first.get());
|
|
if( found )
|
|
{
|
|
if( _lightPtr->valid() && _lightPtr->get() != found )
|
|
continue; // continue search for the right one
|
|
|
|
light = found;
|
|
matrix = itr->second.get();
|
|
}
|
|
}
|
|
|
|
if( light ) { // transform light to world space
|
|
|
|
osg::Matrix localToWorld = osg::Matrix::inverse( *_cv->getModelViewMatrix() );
|
|
if( matrix ) localToWorld.preMult( *matrix );
|
|
|
|
lightPos = light->getPosition();
|
|
|
|
if( lightPos[3] == 0 )
|
|
lightDir.set( -lightPos[0], -lightPos[1], -lightPos[2] );
|
|
else
|
|
lightDir = light->getDirection();
|
|
|
|
lightPos = lightPos * localToWorld;
|
|
lightDir = osg::Matrix::transform3x3( lightDir, localToWorld );
|
|
lightDir.normalize();
|
|
}
|
|
|
|
return light;
|
|
}
|
|
|
|
void StandardShadowMap::ViewData::aimShadowCastingCamera( const osg::Light *light,
|
|
const osg::Vec4 &lightPos,
|
|
const osg::Vec3 &lightDir,
|
|
const osg::Vec3 &lightUp
|
|
/* by default = osg::Vec3( 0, 1 0 )*/ )
|
|
{
|
|
#if 0 // less precise but faster
|
|
osg::BoundingSphere bs =_st->getShadowedScene()->getBound();
|
|
#else
|
|
// get the bounds of the model.
|
|
osg::ComputeBoundsVisitor cbbv(osg::NodeVisitor::TRAVERSE_ACTIVE_CHILDREN);
|
|
cbbv.setTraversalMask(_st->getShadowedScene()->getCastsShadowTraversalMask());
|
|
_st->getShadowedScene()->osg::Group::traverse(cbbv);
|
|
osg::BoundingSphere bs( cbbv.getBoundingBox() );
|
|
#endif
|
|
|
|
aimShadowCastingCamera
|
|
( bs, light, lightPos, lightDir, lightUp );
|
|
}
|
|
|
|
void StandardShadowMap::ViewData::aimShadowCastingCamera(
|
|
const osg::BoundingSphere &bs,
|
|
const osg::Light *light,
|
|
const osg::Vec4 &lightPos,
|
|
const osg::Vec3 &lightDir,
|
|
const osg::Vec3 &lightUpVector
|
|
/* by default = osg::Vec3( 0, 1 0 )*/ )
|
|
{
|
|
osg::Matrixd & view = _camera->getViewMatrix();
|
|
osg::Matrixd & projection = _camera->getProjectionMatrix();
|
|
|
|
osg::Vec3 up = lightUpVector;
|
|
if( up.length2() <= 0 ) up.set( 0,1,0 );
|
|
|
|
osg::Vec3d position(lightPos.x(), lightPos.y(), lightPos.z());
|
|
if (lightPos[3]==0.0) // infinite directional light
|
|
{
|
|
// make an orthographic projection
|
|
// set the position far away along the light direction
|
|
position = bs.center() - lightDir * bs.radius() * 2;
|
|
}
|
|
|
|
float centerDistance = (position-bs.center()).length();
|
|
float znear = centerDistance-bs.radius();
|
|
float zfar = centerDistance+bs.radius();
|
|
float zNearRatio = 0.001f;
|
|
if (znear<zfar*zNearRatio)
|
|
znear = zfar*zNearRatio;
|
|
|
|
if ( lightPos[3]!=0.0 ) { // positional light
|
|
if( light->getSpotCutoff() < 180.0f) // also needs znear zfar estimates
|
|
{
|
|
float spotAngle = light->getSpotCutoff();
|
|
projection.makePerspective( spotAngle * 2, 1.0, znear, zfar);
|
|
view.makeLookAt(position,position+lightDir,up);
|
|
} else { // standard omnidirectional positional light
|
|
float top = (bs.radius()/centerDistance)*znear;
|
|
float right = top;
|
|
|
|
projection.makeFrustum(-right,right,-top,top,znear,zfar);
|
|
view.makeLookAt(position,bs.center(),up );
|
|
}
|
|
}
|
|
else // directional light
|
|
{
|
|
float top = bs.radius();
|
|
float right = top;
|
|
projection.makeOrtho(-right, right, -top, top, znear, zfar);
|
|
view.makeLookAt(position,bs.center(),up);
|
|
}
|
|
}
|
|
|
|
void StandardShadowMap::ViewData::cullShadowReceivingScene( )
|
|
{
|
|
_cv->pushStateSet( _stateset.get() );
|
|
|
|
_st->getShadowedScene()->osg::Group::traverse( *_cv );
|
|
|
|
_cv->popStateSet();
|
|
}
|
|
|
|
void StandardShadowMap::ViewData::cullShadowCastingScene( )
|
|
{
|
|
// record the traversal mask on entry so we can reapply it later.
|
|
unsigned int traversalMask = _cv->getTraversalMask();
|
|
|
|
_cv->setTraversalMask( traversalMask &
|
|
_st->getShadowedScene()->getCastsShadowTraversalMask() );
|
|
|
|
// do RTT camera traversal
|
|
_camera->accept(*_cv);
|
|
|
|
// reapply the original traversal mask
|
|
_cv->setTraversalMask( traversalMask );
|
|
}
|
|
|
|
void StandardShadowMap::ViewData::addShadowReceivingTexGen( )
|
|
{
|
|
_texgen->setMode(osg::TexGen::EYE_LINEAR);
|
|
|
|
// compute the matrix which takes a vertex from view coords into tex coords
|
|
_texgen->setPlanesFromMatrix(
|
|
_camera->getProjectionMatrix() *
|
|
osg::Matrix::translate(1.0,1.0,1.0) *
|
|
osg::Matrix::scale(0.5f,0.5f,0.5f) );
|
|
|
|
osg::RefMatrix * refMatrix = new osg::RefMatrix
|
|
( _camera->getInverseViewMatrix() * *_cv->getModelViewMatrix() );
|
|
|
|
_cv->getRenderStage()->getPositionalStateContainer()->
|
|
addPositionedTextureAttribute
|
|
( *_shadowTextureUnitPtr, refMatrix, _texgen.get() );
|
|
}
|
|
|