I have been stuck in GLSL shaders for a couple of weeks now, and I just don't get it workin (though I think I'm close to it). I am using Irrlicht as graphics engine, and I try to get some "sunlight" shadows done, for the moment just with the sun right above the scene, so I created a material that renders the depth of the scene to a texture using an orthogonal camera. The position of this camera is 500 units above the center of the visible scene.
Here are 2 images of the problem I have:


The camera position of the second image is the same as in the first, it's just pitched a little up. I put the whole shading thing online:
http://bulletbyte.de.../shadertest.zip
I don't think that it's a big problem, I just can't get my mind around it. I've been trying to fix the problem for some weeks now, and I think I'm running in circles, so I though I'd post it here. I hope someone can push me into a direction that helps. Thanks in advance.
Here is the vertex shader for the shadowmap:
struct VS_OUTPUT
{
vec4 Position;
vec4 Color;
};
uniform mat4 mWorldViewProj;
VS_OUTPUT vertexMain( in vec3 Position )
{
vec4 hpos;
VS_OUTPUT OUT;
hpos = (mWorldViewProj * vec4( Position.x , Position.y , Position.z , 1.00000) );
OUT.Color = hpos;
OUT.Position = hpos;
return OUT;
}
void main()
{
VS_OUTPUT xlat_retVal;
xlat_retVal = vertexMain( vec3(gl_Vertex));
gl_Position = vec4( xlat_retVal.Position);
gl_TexCoord[0] = vec4( xlat_retVal.Color);
}
... and the corresponding fragment shader:
uniform float MaxD;
vec4 packFloatToVec4i(const float value)
{
const vec4 bitSh = vec4(256.0*256.0*256.0, 256.0*256.0, 256.0, 1.0);
const vec4 bitMsk = vec4(0.0, 1.0/256.0, 1.0/256.0, 1.0/256.0);
vec4 res = fract(value * bitSh);
res -= res.xxyz * bitMsk;
return res;
}
void main()
{
float depth=((gl_FragCoord.z/gl_FragCoord.w)/MaxD);
gl_FragColor = packFloatToVec4i(depth);
}
I draw the shadow map on the upper left side of the output window, and (for me) it looks reasonable. Here are the shaders that use the shadowmap:
Vertex shader:
uniform mat4 mWorldViewProj;
uniform mat4 mWorldViewProj2;
uniform mat4 mInvWorld;
uniform float MaxD;
varying vec3 normalVec;
varying vec2 texCoords;
varying float shadowDist;
void main(void)
{
gl_Position=mWorldViewProj * gl_Vertex;
vec4 shadowPos=mWorldViewProj2*gl_Vertex;
shadowDist=shadowPos.z/MaxD;
vec4 normal=vec4(gl_Normal, 0.0);
normal=mInvWorld * normal;
normal=normalize(normal);
normalVec[0]=normal[0];
normalVec[1]=normal[1];
normalVec[2]=normal[2];
gl_TexCoord[0]=gl_MultiTexCoord0;
vec4 vecDummy=mWorldViewProj2*gl_Vertex;
texCoords=vecDummy.xy;
texCoords=0.5*texCoords+0.5;
}
Fragment Shader:
uniform sampler2D ColoredTextureSampler;
uniform sampler2D ShadowMapSampler;
uniform sampler2D myTexture;
uniform float fShadowMapSize;
varying vec3 normalVec;
varying vec2 texCoords;
varying float shadowDist;
float unpackFloatFromVec4i(const vec4 value)
{
const vec4 bitSh = vec4(1.0/(256.0*256.0*256.0), 1.0/(256.0*256.0), 1.0/256.0, 1.0);
return(dot(value, bitSh));
}
float testShadow(vec2 smTexCoord, float realDistance) {
float extractedDistance = unpackFloatFromVec4i(texture2D( ShadowMapSampler, vec2( smTexCoord )));
return (extractedDistance <= realDistance) ? ( 0.5 ) : ( 0.000000 );
}
void main (void) {
vec4 texColor=texture2D(myTexture,vec2(gl_TexCoord[0]));
if (texColor.a<0.5) discard;
vec3 lightVector=vec3(1,1,0);
vec4 fogColor=vec4(0.227,0.204,0.0,1.0);
float fShadow=0.0;
for (int i=0; i<16; i++) fShadow+=testShadow(texCoords,shadowDist);
fShadow/=16.0;
gl_FragColor=mix(texColor,vec4(0.0,0.0,0.0,1.0),fShadow);
}
and, last but not least, the main .cpp file:
#include <irrlicht.h>
#include <CRoadMeshLoader.h>
#include <CRandomForestNode.h>
using namespace irr;
class CShadowMapper : public video::IShaderConstantSetCallBack {
protected:
enum ERenderPass {
enPassShadow,
enPassFinal
};
IrrlichtDevice *m_pDevice;
scene::ISceneManager *m_pSmgr;
video::ITexture *m_pTexture;
video::IVideoDriver *m_pDrv;
scene::ISceneNode *m_pRoot;
scene::ICameraSceneNode *m_pLightCam;
f32 m_fHeight,
m_fShadowMapSize;
s32 m_iDepthMaterial,
m_iShadowMaterial;
u32 m_iShadowMapSize;
core::array<video::E_MATERIAL_TYPE> m_aMaterials;
core::dimension2du m_cShadowMapDim;
core::matrix4 m_cWorldViewProj,
m_cWorldViewProj2;
core::matrix4 m_cInvWorld;
ERenderPass m_iRenderPass;
bool isCulled(scene::ISceneNode *pNode) {
if (pNode->getType()==scene::ESNT_EMPTY) return false;
core::aabbox3d<f32> tbox=pNode->getBoundingBox();
pNode->getAbsoluteTransformation().transformBoxEx(tbox);
return !(tbox.intersectsWithBox(m_pLightCam->getViewFrustum()->getBoundingBox()));
}
void render(scene::ISceneNode *pNode) {
if (pNode->getType()==scene::ESNT_TERRAIN) return;
if (pNode->getID()!=-23 && (!isCulled(pNode) || pNode->getType()==scene::ESNT_EMPTY)) {
for (u32 i=0; i<pNode->getMaterialCount(); i++) {
m_aMaterials[i]=pNode->getMaterial(i).MaterialType;
pNode->getMaterial(i).MaterialType=(video::E_MATERIAL_TYPE)m_iDepthMaterial;
}
pNode->render();
for (u32 i=0; i<pNode->getMaterialCount(); i++) pNode->getMaterial(i).MaterialType=m_aMaterials[i];
}
else return;
core::list<scene::ISceneNode *> lChildren=pNode->getChildren();
core::list<scene::ISceneNode *>::Iterator it;
for (it=lChildren.begin(); it!=lChildren.end(); it++) render(*it);
}
u32 getMaxNumberOfMeshBuffers(scene::ISceneNode *pNode) {
u32 iRet=pNode->getMaterialCount();
for (u32 i=0; i<iRet; i++) {
pNode->getMaterial(i).MaterialType=(video::E_MATERIAL_TYPE)m_iShadowMaterial;
pNode->getMaterial(i).setTexture(1,m_pTexture);
}
core::list<scene::ISceneNode *> lChildren=pNode->getChildren();
core::list<scene::ISceneNode *>::Iterator it;
for (it=lChildren.begin(); it!=lChildren.end(); it++) {
u32 i=getMaxNumberOfMeshBuffers(*it);
if (i>iRet) iRet=i;
}
return iRet;
}
public:
CShadowMapper(IrrlichtDevice *pDevice, u32 iShadowMapSize, f32 fHeight, scene::ISceneNode *pRoot=NULL) {
//Get the standard Irrlicht instances
m_pDevice=pDevice;
m_pSmgr =pDevice->getSceneManager();
m_pDrv =pDevice->getVideoDriver();
m_pRoot =pRoot==NULL?m_pSmgr->getRootSceneNode():pRoot;
//calculate the view frustrum of the current camera
const scene::SViewFrustum *cFrustrum=m_pSmgr->getActiveCamera()->getViewFrustum();
core::vector3df vLeftUp=cFrustrum->getFarLeftUp(),
vRightDown=cFrustrum->getFarRightDown();
//initialize some members
m_fHeight=fHeight;
m_iShadowMapSize=iShadowMapSize;
m_cShadowMapDim=core::dimension2du(m_iShadowMapSize,m_iShadowMapSize);
m_fShadowMapSize=(f32)m_iShadowMapSize;
m_pTexture=pDevice->getVideoDriver()->addRenderTargetTexture(m_cShadowMapDim,"ShadowMap");
m_iRenderPass=enPassFinal;
//Initialize the Lightcam
m_pLightCam=m_pSmgr->addCameraSceneNode();
core::matrix4 cProjMat;
f32 size=vLeftUp.X; if (size<0.0f) size=-size;
cProjMat.buildProjectionMatrixOrthoLH(size,size,10,2.0f*m_fHeight);
m_pLightCam->setProjectionMatrix(cProjMat,true);
m_pLightCam->setPosition(core::vector3df(3000,200,3000));
//Compile the shaders
video::IGPUProgrammingServices *pGpu=m_pDrv->getGPUProgrammingServices();
printf("**** compiling pass 1 shaders (fHeight=%.2f)...\n",fHeight);
m_iDepthMaterial=pGpu->addHighLevelShaderMaterialFromFiles("../../shaders/SSM_ShadowPass1V.glsl","main",video::EVST_VS_2_0,
"../../shaders/SSM_ShadowPass1P.glsl","main",video::EPST_PS_2_0,
this,video::EMT_SOLID);
printf("**** compiling pass 2 shaders...\n");
m_iShadowMaterial=pGpu->addHighLevelShaderMaterialFromFiles("../../shaders/SSM_ShadowPass2V.glsl","main",video::EVST_VS_2_0,
"../../shaders/SSM_ShadowPass2P.glsl","main",video::EPST_PS_2_0,
this,video::EMT_SOLID);
printf("**** Ready.\n");
u32 iNumberOfTextures=getMaxNumberOfMeshBuffers(m_pRoot);
for (u32 i=0; i<iNumberOfTextures; i++) m_aMaterials.push_back(video::EMT_SOLID);
}
void update() {
//Activate Lightcam
scene::ICameraSceneNode *pActiveCamera=m_pSmgr->getActiveCamera();
m_pSmgr->setActiveCamera(m_pLightCam);
//Calculate new position and target of the lightcam
core::vector3df vPos=pActiveCamera->getPosition(),
vTgt=vPos-pActiveCamera->getTarget();
vTgt.normalize();
vPos-=750.0f*vTgt;
if (vPos.Y<0.0f) vPos.Y=0.0f;
m_pLightCam->setTarget(vPos);
vPos.Y+=m_fHeight;
m_pLightCam->setPosition(vPos);
m_pLightCam->setUpVector(-vTgt);
//Render the shadowmap
m_iRenderPass=enPassShadow;
m_pDrv->setRenderTarget(m_pTexture);
core::recti cRect=core::recti(0,0,m_cShadowMapDim.Width,m_cShadowMapDim.Height);
m_pDrv->draw2DRectangle(video::SColor(0xFF,0xFF,0xFF,0xFF),cRect);
m_pDrv->setTransform(video::ETS_VIEW ,m_pLightCam->getViewMatrix ());
m_pDrv->setTransform(video::ETS_PROJECTION,m_pLightCam->getProjectionMatrix());
m_pLightCam->render();
render(m_pRoot);
//restore the active camera
m_pSmgr->setActiveCamera(pActiveCamera);
m_pDrv->setRenderTarget(video::ERT_FRAME_BUFFER);
m_iRenderPass=enPassFinal;
}
virtual void OnSetConstants(video::IMaterialRendererServices* services, s32 userData) {
switch (m_iRenderPass) {
case enPassShadow:
m_cWorldViewProj =m_pDrv->getTransform(video::ETS_PROJECTION);
m_cWorldViewProj*=m_pDrv->getTransform(video::ETS_VIEW);
m_cWorldViewProj*=m_pDrv->getTransform(video::ETS_WORLD);
services->setVertexShaderConstant("mWorldViewProj",m_cWorldViewProj.pointer(), 16);
services->setPixelShaderConstant("MaxD",&m_fHeight,1);
break;
case enPassFinal: {
m_cInvWorld=m_pDrv->getTransform(video::ETS_WORLD);
m_cInvWorld.makeInverse();
services->setVertexShaderConstant("mInvWorld", m_cInvWorld.pointer(), 16);
m_cWorldViewProj =m_pDrv->getTransform(video::ETS_PROJECTION);
m_cWorldViewProj*=m_pDrv->getTransform(video::ETS_VIEW);
m_cWorldViewProj*=m_pDrv->getTransform(video::ETS_WORLD);
services->setVertexShaderConstant("mWorldViewProj", m_cWorldViewProj.pointer(), 16);
core::matrix4 dummy=m_pLightCam->getViewMatrix();
dummy.makeInverse();
m_cWorldViewProj2 =m_pLightCam->getProjectionMatrix();
m_cWorldViewProj2*=m_pLightCam->getViewMatrix();
m_cWorldViewProj2*=m_pDrv ->getTransform(video::ETS_WORLD);
services->setVertexShaderConstant("mWorldViewProj2", m_cWorldViewProj2.pointer(), 16);
core::matrix4 world = m_pDrv->getTransform(video::ETS_WORLD);
world = world.getTransposed();
services->setVertexShaderConstant("mTransWorld",world.pointer(), 16);
s32 var0=0;
services->setPixelShaderConstant("ColoredTextureSampler", (f32 *)(&var0), 1);
services->setPixelShaderConstant("fShadowMapSize",&m_fShadowMapSize,1);
f32 MaxD=m_fHeight;
services->setVertexShaderConstant("MaxD",&MaxD,1);
s32 var2=1;
services->setPixelShaderConstant("ShadowMapSampler",(f32 *)&var2,1);
}
break;
}
}
video::ITexture *getDepthMap() { return m_pTexture; }
};
int main() {
core::dimension2du cWindowSize=core::dimension2du(1280,1024);
IrrlichtDevice *pDevice=createDevice(video::EDT_OPENGL,cWindowSize,32,false,false,false,0);
pDevice->setWindowCaption(L"Hello World! - Irrlicht Engine Demo");
video::IVideoDriver *pDrv =pDevice->getVideoDriver();
scene::ISceneManager *pSmgr =pDevice->getSceneManager();
gui::IGUIEnvironment *pGuienv=pDevice->getGUIEnvironment();
CRandomForestFactory *pForest=new CRandomForestFactory(pSmgr);
pSmgr->registerSceneNodeFactory(pForest);
CRoadMeshLoader *pRoad=new CRoadMeshLoader(pDevice);
pSmgr->addExternalMeshLoader(pRoad);
pSmgr->loadScene("../../data/irrodecar.xml");
scene::ICameraSceneNode *pFpsCam=pSmgr->addCameraSceneNodeFPS();
pFpsCam->setNearValue(10.0f);
pFpsCam->setFarValue(1500.0f);
pFpsCam->setPosition(core::vector3df(6525.0f,390.0f,7550.0f));
pFpsCam->setTarget(core::vector3df(-2000.0f,-750.0f,2450.0f));
u32 iShadowSize=2048;
CShadowMapper *pShadow=new CShadowMapper(pDevice,iShadowSize,500.0f,pSmgr->getSceneNodeFromName("worldNode"));
video::ITexture *pDepth=pShadow->getDepthMap();
s32 iLastFPS=-1;
while(pDevice->run()) {
pSmgr->setActiveCamera(pFpsCam);
pDrv->beginScene(true,true,video::SColor(0,200,200,200));
pShadow->update();
pSmgr->drawAll();
pDrv->draw2DImage(pDepth,core::rect<s32>(0,0,384,384),core::rect<s32>(0,0,iShadowSize,iShadowSize));
pGuienv->drawAll();
pDrv->endScene();
s32 iFps=pDrv->getFPS();
if (iLastFPS!=iFps) {
core::stringw str = L"Shadow Mapping [";
str+=pDrv->getName();
str+="] FPS:";
str+=iFps;
pDevice->setWindowCaption(str.c_str());
iLastFPS=iFps;
}
}
pDevice->drop();
return 0;
}


















