[edit]Sorry, I had pasted in the regular PSSM shadow caster shader instead of the VSM shadow caster shader doh! Fixed now[/edit]
Here's the PSVSM shadow caster material:
Code: Select all
vertex_program pssm_vsm_caster_vp glsl
{
source pssm_vsm_caster_vp.glsl
default_params
{
param_named_auto wvMat worldview_matrix
}
}
fragment_program pssm_vsm_caster_fp glsl
{
source pssm_vsm_caster_fp.glsl
default_params
{
param_named_auto depthRange scene_depth_range
}
}
material pssm_vsm_caster
{
technique
{
// all this will do is write depth and depth squared to red and green
pass
{
vertex_program_ref pssm_vsm_caster_vp
{
}
fragment_program_ref pssm_vsm_caster_fp
{
}
}
}
}
Code: Select all
uniform mat4 wvMat;
varying vec4 vertexDepth;
void main()
{
vertexDepth = wvMat * gl_Vertex;
// pass the vertex position, transforming it to clip space
gl_Position = ftransform();
}
Code: Select all
uniform vec4 depthRange;
varying vec4 vertexDepth;
void main()
{
// derive a per-pixel depth and depth squared
// (length of the view space position == distance from camera)
// (this is linear space, not post-projection quadratic space)
float d = (length(vertexDepth.xyz) - depthRange.x) * depthRange.w;
gl_FragColor = vec4(d, d * d, 1, 1);
}
Code: Select all
vertex_program pssm_vsm_diffuse_vp glsl
{
source pssm_vsm_vp.glsl
default_params
{
param_named_auto lightPosition light_position 0
param_named_auto worldViewProjMatrix worldviewproj_matrix
param_named_auto worldMatrix world_matrix
param_named_auto texViewProjMatrix0 texture_viewproj_matrix 0
param_named_auto texViewProjMatrix1 texture_viewproj_matrix 1
param_named_auto texViewProjMatrix2 texture_viewproj_matrix 2
}
}
fragment_program pssm_vsm_diffuse_fp glsl
{
source pssm_vsm_fp.glsl
default_params
{
param_named shadowMap0 int 0
param_named shadowMap1 int 1
param_named shadowMap2 int 2
param_named diffuseMap0 int 3
// pssm shadow info
param_named_auto invShadowMapSize0 inverse_texture_size 0
param_named_auto invShadowMapSize1 inverse_texture_size 1
param_named_auto invShadowMapSize2 inverse_texture_size 2
param_named_auto depthRange0 shadow_scene_depth_range 0
param_named_auto depthRange1 shadow_scene_depth_range 1
param_named_auto depthRange2 shadow_scene_depth_range 2
param_named_auto lightPos0 light_position 0
}
}
material pssm_vsm_material
{
technique
{
// put your ambient pass here and uncomment the scene_blend below if you want to do a separate ambient pass first
pass
{
max_lights 8
// scene_blend add // only needed when doing multiple passes (with ambient)
iteration once_per_light
ambient 0 0 0
diffuse 1 1 1
specular 1 1 1 128
vertex_program_ref pssm_vsm_diffuse_vp
{
}
fragment_program_ref pssm_vsm_diffuse_fp
{
}
texture_unit shadow_tex0
{
content_type shadow
filtering anisotropic
max_anisotropy 16
tex_address_mode border
tex_border_colour 1 1 1
}
texture_unit shadow_tex1
{
content_type shadow
filtering anisotropic
max_anisotropy 16
tex_address_mode border
tex_border_colour 1 1 1
}
texture_unit shadow_tex2
{
content_type shadow
filtering anisotropic
max_anisotropy 16
tex_address_mode border
tex_border_colour 1 1 1
}
texture_unit diffuse_tex
{
texture grey.png
tex_coord_set 0
}
}
}
}
Code: Select all
uniform vec4 lightPosition; // world space
uniform mat4 worldViewProjMatrix;
uniform mat4 worldMatrix;
uniform mat4 texViewProjMatrix0;
uniform mat4 texViewProjMatrix1;
uniform mat4 texViewProjMatrix2;
uniform mat4 worldInverseTranspose;
varying vec4 lightPosition0;
varying vec4 lightPosition1;
varying vec4 lightPosition2;
varying vec3 vertexNormal;
varying vec4 vertexWorldPosition;
varying float shadowDistance;
void main()
{
// get normal in world space
vertexNormal = vec3(worldMatrix * vec4(normalize(gl_Normal.xyz), 0.0));
// pass the vertex position, transforming it to clip space
gl_Position = ftransform();
// pass texture co-ords through unchanged
gl_TexCoord[0] = gl_MultiTexCoord0;
// shadowDistance is in clip space
shadowDistance = gl_Position.z;
// get vertex position in world space
vertexWorldPosition = worldMatrix * gl_Vertex;
// Calculate the position of vertex in light space for shadowing
lightPosition0 = texViewProjMatrix0 * vertexWorldPosition;
lightPosition1 = texViewProjMatrix1 * vertexWorldPosition;
lightPosition2 = texViewProjMatrix2 * vertexWorldPosition;
}
Code: Select all
uniform sampler2D shadowMap0;
uniform sampler2D shadowMap1;
uniform sampler2D shadowMap2;
uniform sampler2D diffuseMap0;
uniform vec4 invShadowMapSize0;
uniform vec4 invShadowMapSize1;
uniform vec4 invShadowMapSize2;
uniform vec4 pssmSplitPoints;
uniform vec4 depthRange0;
uniform vec4 depthRange1;
uniform vec4 depthRange2;
uniform vec4 lightPos0;
varying vec3 vertexNormal;
varying vec4 vertexWorldPosition;
varying vec4 lightPosition0;
varying vec4 lightPosition1;
varying vec4 lightPosition2;
varying float shadowDistance;
float shadowPCF(in sampler2D shadowMap, in vec4 shadowMapPos, const in vec2 offset, float depth)
{
shadowMapPos = shadowMapPos / shadowMapPos.w;
vec2 uv = shadowMapPos.xy;
vec2 o = offset;
vec2 c = texture2D(shadowMap, uv.xy).rg; // center
c += texture2D(shadowMap, uv.xy - o.xy).rg; // top left
c += texture2D(shadowMap, uv.xy + o.xy).rg; // bottom right
c += texture2D(shadowMap, vec2(uv.x - o.x, uv.y)).rg; // left
c += texture2D(shadowMap, vec2(uv.x + o.x, uv.y)).rg; // right
c += texture2D(shadowMap, vec2(uv.x, uv.y + o.y)).rg; // bottom
c += texture2D(shadowMap, vec2(uv.x, uv.y - o.y)).rg; // top
c += texture2D(shadowMap, vec2(uv.x - o.x, uv.y + o.y)).rg; // bottom left
c += texture2D(shadowMap, vec2(uv.x + o.x, uv.y - o.y)).rg; // top right
c /= 9.0;
vec2 moments = c;
float litFactor = (depth <= moments.x ? 1.0 : 0.0);
// standard variance shadow mapping code
float E_x2 = moments.y;
float Ex_2 = moments.x * moments.x;
float vsmEpsilon = 0.0001;
float variance = min(max(E_x2 - Ex_2, 0.0) + vsmEpsilon, 1.0);
float m_d = moments.x - depth;
float p = variance / (variance + m_d * m_d);
return smoothstep(0.4, 1.0, max(litFactor, p));
}
void main()
{
vec3 lightDir0 = normalize(lightPos0.xyz - (lightPos0.w * vertexWorldPosition.xyz));
vec3 normalizedNormal = normalize(vertexNormal);
float diffuseAmount = max(dot(normalizedNormal, lightDir0), 0.0);
// calculate shadow
float shadowAmount = 1.0;
vec4 shadowColor = vec4(0.0, 0.0, 0.0, 1.0);
if (shadowDistance <= pssmSplitPoints.y) {
float depth = (length(lightPos0.xyz - vertexWorldPosition.xyz) - depthRange0.x) * depthRange0.w;
shadowAmount = shadowPCF(shadowMap0, lightPosition0, invShadowMapSize0.xy, depth);
shadowColor = vec4(1.0, 0, 0, 1.0);
}
else if (shadowDistance <= pssmSplitPoints.z) {
float depth = (length(lightPos0.xyz - vertexWorldPosition.xyz) - depthRange1.x) * depthRange1.w;
shadowAmount = shadowPCF(shadowMap1, lightPosition1, invShadowMapSize1.xy, depth);
shadowColor = vec4(0.0, 1.0, 0, 1.0);
}
else {
float depth = (length(lightPos0.xyz - vertexWorldPosition.xyz) - depthRange2.x) * depthRange2.w;
shadowAmount = shadowPCF(shadowMap2, lightPosition2, invShadowMapSize2.xy, depth);
shadowColor = vec4(0.0, 0.0, 1.0, 1.0);
}
const float shadowAmbient = 0.9;
// if you do your own ambient pass you'll want to remove the ambient code from here
const vec4 ambientColor = vec4 (0.05, 0.075, 0.1, 1.0);
vec4 diffuseColor = texture2D(diffuseMap0, gl_TexCoord[0].st);
gl_FragColor = ((1.0 - shadowAmbient) * diffuseColor * shadowColor) +
(diffuseColor * shadowAmbient * diffuseAmount * shadowAmount * shadowColor);
}
It still needs tuning because I have a large landscape and also using 1024x1024 kills my framerate as you can see in those screenshots.
BTW this is roughly what I'm doing to set up the shadows in my app:
Code: Select all
// 3 shadow textures per light
const int numShadowTextures = 3;
mSceneMgr->setShadowTechnique(SHADOWTYPE_TEXTURE_MODULATIVE_INTEGRATED);
MaterialManager::getSingleton().setDefaultTextureFiltering(Ogre::TFO_ANISOTROPIC);
// set shadow far distance BEFORE creating any lights (see API documentation)
mSceneMgr->setShadowFarDistance(4096);
mSceneMgr->setShadowTextureCountPerLightType(Ogre::Light::LT_POINT, numShadowTextures);
mSceneMgr->setShadowTextureCount(numShadowTextures);
// can't use anything larger than 512 if our screen/window resolution? is less than 1024 in either dimension and
// we're using open gl - macbook pro screen is 1440x900 or something so it has this issue ugh.
// might also try other pixel formats to improve the speed at the risk of quality: PF_FLOAT32_R, PF_X8R8G8B8, PF_R8G8B8
if(mRoot->getRenderSystem()->getName() == "OpenGL Rendering Subsystem") {
if(mWindow->getWidth() >= 1024 && mWindow->getHeight() >= 1024) {
mSceneMgr->setShadowTextureConfig(0, 1024, 1024, PF_FLOAT16_RGB);
}
else {
mSceneMgr->setShadowTextureConfig(0, 512, 512, PF_FLOAT16_RGB);
}
}
else {
mSceneMgr->setShadowTextureConfig(0, 512, 512, PF_FLOAT16_RGB);
}
mSceneMgr->setShadowTextureConfig(1, 1024, 1024, PF_FLOAT16_RGB);
mSceneMgr->setShadowTextureConfig(2, 1024, 1024, PF_FLOAT16_RGB);
mSceneMgr->setShadowTextureSelfShadow(true);
// shadow camera setup
PSSMShadowCameraSetup* pssmSetup = new PSSMShadowCameraSetup();
PSSMShadowCameraSetup::SplitPointList splitPointList = pssmSetup->getSplitPoints();
#ifdef MANUAL_SPLIT_POINTS
// set the split points manually
splitPointList[0] = 1.0;
splitPointList[1] = 96.0;
splitPointList[2] = 256.0;
splitPointList[3] = 512.0;
pssmSetup->setSplitPoints(splitPointList);
#else
// use the Ogre split point calculator
pssmSetup->calculateSplitPoints(numShadowTextures, mCamera->getNearClipDistance(), mCamera->getFarClipDistance());
#endif
pssmSetup->setSplitPadding(5);
// set the LISPM adjustment factor (see API documentation for these)
pssmSetup->setOptimalAdjustFactor(0, 2.0);
pssmSetup->setOptimalAdjustFactor(1, 1.0);
pssmSetup->setOptimalAdjustFactor(2, 0.5);
mSceneMgr->setShadowCameraSetup(ShadowCameraSetupPtr(pssmSetup));
// use a vsm caster material
mSceneMgr->setShadowTextureCasterMaterial("pssm_vsm_caster");
mSceneMgr->setShadowCasterRenderBackFaces(false);
// set the receiver params for any materials that need the split point information
Vector4 splitPoints;
splitPointList = pssmSetup->getSplitPoints();
for (int i = 0; i < numShadowTextures; ++i) {
splitPoints[i] = splitPointList[i];
}
mat = MaterialManager::getSingleton().getByName("pssm_vsm_material");
for(int i = 0; i < mat->getNumTechniques(); ++i) {
mat->getTechnique(i)->getPass(0)->getFragmentProgramParameters()->setNamedConstant("pssmSplitPoints", splitPoints);
}
// create a light now after all that
Light* l = mSceneMgr->createLight("Sun");
l->setType(Light::LT_POINT);
l->setPosition(0.0, 10000.0, 0.0);
// add the overlay elements to show the shadow maps:
// init overlay elements
OverlayManager& mgr = OverlayManager::getSingleton();
Overlay* overlay = mgr.create("DebugOverlay");
for (size_t i = 0; i < numShadowTextures; ++i) {
TexturePtr tex = mSceneMgr->getShadowTexture(i);
// Set up a debug panel to display the shadow
MaterialPtr debugMat = MaterialManager::getSingleton().create(
"Ogre/DebugTexture" + StringConverter::toString(i),
ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
debugMat->getTechnique(0)->getPass(0)->setLightingEnabled(false);
TextureUnitState *t = debugMat->getTechnique(0)->getPass(0)->createTextureUnitState(tex->getName());
t->setTextureAddressingMode(TextureUnitState::TAM_CLAMP);
OverlayContainer* debugPanel = (OverlayContainer*)
(OverlayManager::getSingleton().createOverlayElement("Panel", "Ogre/DebugTexPanel" + StringConverter::toString(i)));
debugPanel->_setPosition(0.8, i*0.25);
debugPanel->_setDimensions(0.2, 0.24);
debugPanel->setMaterialName(debugMat->getName());
overlay->add2D(debugPanel);
}
Happy New Year!!