Hi. N00b here.
I wrote a class to create a graph of a surface and this class includes a shadow map. With one object it works fine. With multiple objects, strange things are happening. See picture - for all six objects the same calls are made, only the viewport coordinates are different. Only one of them displays the shadow correctly. The rendering sequence of the objects makes a difference to the behaviour, so it seems to me it is a problem with the way the shadow maps get initialised.
For the attached picture, this was the way the objects were treated:
surfacegraph Graph3, Graph4, Graph2, Graph5, Graph6,Graph; ..... Graph.Initialise(perRow,0,0,600,520,nscale,Eye); Graph2.Initialise(perRow,0,525,600,520,nscale,Eye); Graph3.Initialise(perRow,580,0,480,520,nscale,Eye); Graph4.Initialise(perRow,580,525,480,520,nscale,Eye); Graph5.Initialise(perRow,1050,0,600,520,nscale,Eye); Graph6.Initialise(perRow,1050,525,600,520,nscale,Eye); ..... Graph2.Render(); // the rendering sequence appears to make a big difference Graph4.Render(); // to the shadow maps that result Graph6.Render(); Graph.Render(); Graph3.Render(); Graph5.Render();
This creates the picture where Graph2 displays correctly, Graph displays with no shadow and all the rest display completely in shadow.
However I don't see why the objects are affecting each others' shadow maps.
This is some of the class definition:
class surfacegraph
{
public:
static const int SHADOWMAPRESOLUTION = 4096;
int perRow; int texWidth,texHeight; int numVertices, numTriangles;
IDirect3DVertexBuffer9* TerrainVertexBuffer;
IDirect3DIndexBuffer9* TerrainIndexBuffer;
IDirect3DTexture9* texture_grid;
D3DVIEWPORT9 vp;
D3DVIEWPORT9 shadow_vp;
ID3DXEffect* mFX;
D3DXMATRIXA16 matView;
D3DXMATRIXA16 matProj;
DirLight mLight;
Mtrl mWhiteMtrl;
D3DXVECTOR3 Eye; // eye position
// for shadow map:
IDirect3DSurface9* pSurface; // never gets used for anything though - not sure why this was here
D3DXMATRIX mLightVP;
DrawableTex2D* mShadowMap;
D3DXHANDLE mhTech;
D3DXHANDLE mhWVP;
D3DXHANDLE mhTex;
D3DXHANDLE mhEyePos; // for specular
D3DXHANDLE mhDiffuseMtrl;
D3DXHANDLE mhAmbientMtrl;
D3DXHANDLE mhLightVector;
D3DXHANDLE mhLightDiffuseColour;
D3DXHANDLE mhLightAmbientColour;
D3DXHANDLE mhLightSpecularColour;
D3DXHANDLE mhSpecularMtrl;
D3DXHANDLE mhSpecularPower;
D3DXHANDLE mhBuildShadowMapTech;
D3DXHANDLE mhLightWVP;
D3DXHANDLE mhShadowMap;
D3DXHANDLE mhSwitch;
float xyscale,nscale;
surfacegraph();
HRESULT Initialise( parameters );
...
HRESULT SetxzDataAutomatic (float minimum, float maximum, float in_xyscale);
HRESULT SetDataFromDoubleScaled(double **, double);
HRESULT SetDataAgain(float **);
HRESULT SetColourData(float **, float **, float **);
VOID surfacegraph::Render(void);
~surfacegraph();
};
The following is the start of the Render method for the class. See code comments.
VOID surfacegraph::Render()
{
....
// DRAW SHADOW MAP:
Direct3D.pd3dDevice->SetViewport(&vp);
// without this^^^, 5 graphs, all except the last rendered, appear as small shadows on white
mShadowMap->beginScene();
//Direct3D.pd3dDevice->SetViewport(&shadow_vp);
// completely whites out all but last-to-render graph. No idea why.
// That may suggest that Device->Clear is affecting the back buffer but we also know it is affecting the shadow map!
Direct3D.pd3dDevice->Clear( 0, 0, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER,
D3DCOLOR_XRGB( 255, 255, 255 ), 1.0f, 0 );
// this clearly does affect shadow map successfully...
// 0x00000000, 1.0f, 0 );
// because this^^^^^ leads to everything being in shadow
DXChk(mFX->SetTechnique(mhBuildShadowMapTech));
UINT numPasses = 0;
DXChk(mFX->Begin(&numPasses, 0));
DXChk(mFX->SetMatrix(mhLightWVP, &(mLightVP)));
DXChk(mFX->CommitChanges());
Direct3D.pd3dDevice->SetStreamSource(0, TerrainVertexBuffer, 0, sizeof(VertexPNT));
Direct3D.pd3dDevice->SetIndices(TerrainIndexBuffer);
Direct3D.pd3dDevice->SetVertexDeclaration(VertexPNT::Decl);
for (UINT ii = 0; ii < numPasses; ++ii)
{
mFX->BeginPass(ii);
Direct3D.pd3dDevice->DrawIndexedPrimitive(
D3DPT_TRIANGLELIST,
0,
0,
numVertices,
0,
numTriangles);
mFX->EndPass();
};
DXChk(mFX->End());
mShadowMap->endScene();
// axis drawing in fixed function pipeline:
Direct3D.pd3dDevice->SetViewport(&vp);
...
In case it is necessary background information, the "DrawableTex2D" class is
class DrawableTex2D
{
public:
IDirect3DTexture9* mTex;
ID3DXRenderToSurface* mRTS;
IDirect3DSurface9* mTopSurf;
UINT mWidth;
UINT mHeight;
UINT mMipLevels;
D3DFORMAT mTexFormat;
bool mUseDepthBuffer;
D3DFORMAT mDepthFormat;
D3DVIEWPORT9 mViewPort;
bool mAutoGenMips;
DrawableTex2D(UINT width, UINT height, UINT mipLevels,
D3DFORMAT texFormat, bool useDepthBuffer,
D3DFORMAT depthFormat, D3DVIEWPORT9& viewport, bool autoGenMips);
~DrawableTex2D();
IDirect3DTexture9* d3dTex();
void beginScene();
void endScene();
void onLostDevice();
void onResetDevice();
DrawableTex2D(const DrawableTex2D& rhs);
DrawableTex2D& operator=(const DrawableTex2D& rhs);
};
IDirect3DTexture9* DrawableTex2D::d3dTex()
{
return mTex;
}
void DrawableTex2D::onResetDevice()
{
UINT usage = D3DUSAGE_RENDERTARGET;
if(mAutoGenMips)
usage |= D3DUSAGE_AUTOGENMIPMAP;
DXChk(D3DXCreateTexture(Direct3D.pd3dDevice, mWidth, mHeight, mMipLevels, usage, mTexFormat, D3DPOOL_DEFAULT, &mTex));
DXChk(D3DXCreateRenderToSurface(Direct3D.pd3dDevice, mWidth, mHeight, mTexFormat, mUseDepthBuffer, mDepthFormat, &mRTS));
DXChk(mTex->GetSurfaceLevel(0, &mTopSurf));
}
void DrawableTex2D::beginScene()
{
mRTS->BeginScene(mTopSurf, &mViewPort);
}
void DrawableTex2D::endScene()
{
mRTS->EndScene(D3DX_FILTER_NONE);
}
The following method is called, once only, after the graph object has been declared.
HRESULT surfacegraph::Initialise( parameters )
{
...
shadow_vp.X = 0; shadow_vp.Y = 0;
shadow_vp.Width = SHADOWMAPRESOLUTION;
shadow_vp.Height = SHADOWMAPRESOLUTION;
shadow_vp.MinZ = 0.0f;
shadow_vp.MaxZ = 1.0f;
mShadowMap = new DrawableTex2D(SHADOWMAPRESOLUTION, SHADOWMAPRESOLUTION, 1, D3DFMT_R32F, true, D3DFMT_D24X8, shadow_vp, false);
vp.X = left;
vp.Y = top;
vp.Width = width;
vp.Height = height;
vp.MinZ = 0.0f;
vp.MaxZ = 0.0f;
float GlobalAspectRatio = ((float)vp.Width)/(float)vp.Height;
// Create the FX from a .fx file.
ID3DXBuffer* errors = 0;
D3DXCreateEffectFromFile(Direct3D.pd3dDevice, "test1.fx",
0, 0, D3DXSHADER_DEBUG, 0, &mFX, &errors);
if( errors )
MessageBox(0, (char*)errors->GetBufferPointer(), 0, 0);
// Obtain handles.
mhTech = mFX->GetTechniqueByName("WarwickTech");
........
mShadowMap->onResetDevice();
static D3DXVECTOR3 vLookatPt( 0.0f, 1.2f, 0.0f );
static D3DXVECTOR3 vUpVec( 0.0f, 1.0f, 0.0f );
Eye = in_Eye;
D3DXMatrixLookAtLH( &matView, &Eye, &vLookatPt, &vUpVec );
D3DXMatrixPerspectiveFovLH( &matProj, D3DX_PI / 4.0f, GlobalAspectRatio, 1.0f, 12.0f);
mLight.dirW = D3DXVECTOR3(-1.0f, -0.5f, 0.5f);// remember y is height
D3DXVec3Normalize(&mLight.dirW, &mLight.dirW);
mLight.ambient = D3DXCOLOR(0.4f, 0.4f, 0.4f, 1.0f);
mLight.diffuse = D3DXCOLOR(0.6f, 0.6f, 0.6f, 1.0f);
mLight.spec = D3DXCOLOR(0.5f, 0.5f, 0.5f, 1.0f);
mWhiteMtrl.ambient = WHITE*1.0f;
mWhiteMtrl.diffuse = WHITE*1.0f;
mWhiteMtrl.spec = WHITE*1.0f;
mWhiteMtrl.specPower = 8.0f;
// Shadow mapping:
D3DXMATRIX lightView;
D3DXVECTOR3 lightPosW(20.0f, 10.0f, -10.0f);
D3DXVECTOR3 lightTargetW(0.0f, 0.0f, 0.0f);
D3DXVECTOR3 lightUpW(0.0f, 1.0f, 0.0f);
D3DXMatrixLookAtLH(&lightView, &lightPosW, &lightTargetW, &lightUpW);
D3DXMATRIX lightLens;
float lightFOV = D3DX_PI*0.25f;
D3DXMatrixPerspectiveFovLH(&lightLens, lightFOV, 1.0f, 1.0f, 100.0f);
mLightVP = lightView*lightLens;
// graph data:
perRow = nperRow;
numVertices = perRow*perRow;
numTriangles = 2*(perRow-1)*(perRow-1);
texWidth = perRow;
texHeight = perRow;
definedindices = 0;
if (
DXChk(
Direct3D.pd3dDevice->CreateVertexBuffer(numVertices*sizeof(VertexPNT),D3DUSAGE_WRITEONLY,0,D3DPOOL_MANAGED,&TerrainVertexBuffer,0),1)
+
DXChk(
Direct3D.pd3dDevice->CreateIndexBuffer(numTriangles*3*sizeof(DWORD),D3DUSAGE_WRITEONLY, D3DFMT_INDEX32, D3DPOOL_MANAGED,&TerrainIndexBuffer,0),2)
+
DXChk( D3DXCreateTexture( Direct3D.pd3dDevice, texWidth,texHeight, 0,0,D3DFMT_X8R8G8B8,D3DPOOL_MANAGED, &texture_grid),3)
)
return E_FAIL;
D3DSURFACE_DESC textureDesc;
texture_grid->GetLevelDesc(0, &textureDesc);
if ( textureDesc.Format != D3DFMT_X8R8G8B8 ) return E_FAIL;
return S_OK;
}
Sorry to provide so much code, I'm not all that sure what is going on here. I have tried tinkering with what happens at the start of the Render method but I don't seem to know what I'm doing, the results (as described in the code comments) are hard for me to interpret.
Grateful for any suggestions.
I am happy to dropbox the whole source if anyone wishes to tinker with it.






