Original Post
Im trying to get a basic shadow mapping implementation working, but I'm having problems. I'm fairly sure the error is with the texture projection, but I can't figure out where (it will probably turn out to be an embarrassingly simple error). Anyway, here is the code, I apologize for the awful coding style, I'm more concerned with getting it working than anything else at this point. The last two functions in the source code is where all the rendering stuff is done, but I included the rest of the code just in case. Thanks guys. main.cpp shadowMap.fx
#include <windows.h>
#include <windowsx.h>
#include <string>
#include <d3d9.h>
#include <d3dx9.h>
const int SHADOWMAP_SIZE = 1024;
#define SHADOWMAP_FORMAT D3DFMT_R32F
HWND g_hWnd;
HINSTANCE g_instance;
ID3DXMesh* g_teapot;
ID3DXEffect* g_shadow;
IDirect3D9* g_d3d;
IDirect3DDevice9* g_d3dDevice;
D3DPRESENT_PARAMETERS g_d3dpp;
D3DXMATRIX g_world;
D3DXMATRIX g_viewProj;
D3DXMATRIX g_lightViewProj;
D3DXMATRIX g_lightProjTex;
D3DXVECTOR4 g_lightPos;
D3DXVECTOR4 g_ambient;
D3DXVECTOR4 g_diffuse;
IDirect3DTexture9* g_shadowMap;
IDirect3DSurface9* g_shadowMapSurface;
IDirect3DSurface9* g_shadowMapZBuffer;
IDirect3DSurface9* g_oldSurface;
IDirect3DSurface9* g_oldZBuffer;
bool CreateAppWindow(std::string appName);
bool CreateD3D();
void CloseD3D();
bool CreateResources();
void ReleaseResources();
bool CreateMesh();
bool CreateShaders();
bool CreateShadowMap();
void DrawShadowMapPreview();
void DrawShadowMap();
void DrawScene();
LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) {
return DefWindowProc(hWnd, msg, wParam, lParam);
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
int WINAPI WinMain(HINSTANCE instance, HINSTANCE prevInstance, LPSTR cmdLine, int showCmd) {
std::string appName = "Arena";
if(!CreateAppWindow(appName))
return 0;
if(!CreateD3D()) {
UnregisterClass(appName.c_str(), instance);
return 0;
}
if(!CreateResources()) {
ReleaseResources();
CloseD3D();
UnregisterClass(appName.c_str(), instance);
return 0;
}
MSG msg;
bool done = false;
while(!done) {
while(PeekMessage(&msg, g_hWnd, 0, 0, PM_REMOVE)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
if(GetAsyncKeyState(VK_ESCAPE))
done = true;
//start render
if(g_d3dDevice->BeginScene()) {
if(FAILED(g_shadow->SetTechnique("Shader")))
MessageBox(g_hWnd, "Error: Couldnt set technique: \"ShadowMap\"", "", MB_OK);
g_shadow->SetMatrix("world", &g_world);
g_shadow->SetMatrix("viewProj", &g_viewProj);
if(FAILED(g_shadow->Begin(0, 0)))
MessageBox(g_hWnd, "Error: Couldnt begin shader: ShadowMap", "", MB_OK);
DrawShadowMap();
DrawScene();
g_shadow->End();
DrawShadowMapPreview();
g_d3dDevice->EndScene();
}
g_d3dDevice->Present(0, 0, 0, 0);
}
ReleaseResources();
CloseD3D();
UnregisterClass(appName.c_str(), instance);
return 0;
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
bool CreateAppWindow(std::string appName) {
g_instance = GetModuleHandle(NULL);
//fill in window data structure
WNDCLASSEX wc = {sizeof(WNDCLASSEX), //size
CS_CLASSDC, //share device context between windows
WndProc, //window procedure
0, //no extra bytes
0, //no extra bytes
g_instance, //instance of window
NULL, //no icon
LoadCursor(NULL, IDC_ARROW), //default arrow
NULL, //no background
NULL, //no menu
appName.c_str(), //class name
NULL}; //no small icon
//register the window with windows
if(!RegisterClassEx(&wc)) {
return false;
}
//create the window
g_hWnd = CreateWindow(appName.c_str(), //class name
appName.c_str(), //window title name
WS_OVERLAPPEDWINDOW, //just a border, caption and close button
100, //100 x indent
100, //100 y indent
640,
480,
GetDesktopWindow(), //parent window
NULL, //no menu
g_instance, //application instance
NULL); //no user data
//if handle is null, then creation failed
if(!g_hWnd) {
return false;
}
//display the window
ShowWindow(g_hWnd, SW_SHOW);
return true;
}
bool CreateResources() {
g_shadow = 0;
g_teapot = 0;
g_shadowMap = 0;
g_shadowMapSurface = 0;
g_shadowMapZBuffer = 0;
if(!CreateMesh())
return false;
if(!CreateShaders())
return false;
if(!CreateShadowMap())
return false;
return true;
}
bool CreateMesh() {
LPD3DXBUFFER adj, mat;
DWORD num;
if(FAILED(D3DXLoadMeshFromX("teapot.x",
D3DXMESH_MANAGED, g_d3dDevice, &adj, &mat, 0, #, &g_teapot))) {
return false;
}
adj->Release();
mat->Release();
return true;
}
bool CreateShaders() {
if(FAILED(D3DXCreateEffectFromFile(g_d3dDevice,
"shadowMap.fx", 0, 0, 0, 0, &g_shadow, 0))) {
MessageBox(g_hWnd, "Error: shadowMap.fx", "", MB_OK);
return false;
}
return true;
}
void ReleaseResources() {
if(g_shadowMap)
g_shadowMap->Release();
if(g_shadowMapSurface)
g_shadowMapSurface->Release();
if(g_shadowMapZBuffer)
g_shadowMapZBuffer->Release();
if(g_shadow)
g_shadow->Release();
if(g_teapot)
g_teapot->Release();
}
bool CreateD3D() {
//create the d3d interface
if(!(g_d3d = Direct3DCreate9(D3D_SDK_VERSION))) {
return false;
}
//fill out present parameters struct
ZeroMemory(&g_d3dpp, sizeof(g_d3dpp));
g_d3dpp.Windowed = true;
g_d3dpp.BackBufferFormat = D3DFMT_UNKNOWN;
g_d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
g_d3dpp.EnableAutoDepthStencil = true;
g_d3dpp.AutoDepthStencilFormat = D3DFMT_D24S8;
g_d3dpp.hDeviceWindow = g_hWnd;
//create the device
if(FAILED(g_d3d->CreateDevice(D3DADAPTER_DEFAULT, //default display device
D3DDEVTYPE_HAL, //hardware abstraction
g_hWnd, //window handle
D3DCREATE_HARDWARE_VERTEXPROCESSING, //shaders on
&g_d3dpp, //present parameters
&g_d3dDevice))) { //device
return false;
}
return true;
}
void CloseD3D() {
if(g_d3dDevice)
g_d3dDevice->Release();
if(g_d3d)
g_d3d->Release();
}
bool CreateShadowMap() {
if(FAILED(g_d3dDevice->CreateTexture(SHADOWMAP_SIZE,
SHADOWMAP_SIZE, 1, D3DUSAGE_RENDERTARGET, SHADOWMAP_FORMAT,
D3DPOOL_DEFAULT, &g_shadowMap, NULL))) {
MessageBox(g_hWnd, "Error: couldnt create shadow map texture", "", MB_OK);
return false;
}
if(FAILED(g_shadowMap->GetSurfaceLevel(0, &g_shadowMapSurface))) {
MessageBox(g_hWnd, "Error: couldnt get render surface from shadow map texture", "", MB_OK);
return false;
}
if(FAILED(g_d3dDevice->CreateDepthStencilSurface(SHADOWMAP_SIZE,
SHADOWMAP_SIZE, D3DFMT_D24S8, D3DMULTISAMPLE_NONE, 0,
TRUE, &g_shadowMapZBuffer, NULL))) {
MessageBox(g_hWnd, "Error: couldnt create shadow map depth surface", "", MB_OK);
return false;
}
return true;
}
void DrawShadowMapPreview() {
//create quad for shadow map preview
struct VERTEX {
float x, y, z, rhw;
float tu, tv;
};
float size = 100.0f;
VERTEX quad[4] = {
{size, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f},
{size, size, 0.0f, 1.0f, 1.0f, 1.0f},
{0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f},
{0.0f, size, 0.0f, 1.0f, 0.0f, 1.0f},
};
//set up texture stages
g_d3dDevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG1);
g_d3dDevice->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
//render
g_d3dDevice->SetTexture(0, g_shadowMap);
g_d3dDevice->SetVertexShader(0);
g_d3dDevice->SetPixelShader(0);
g_d3dDevice->SetFVF(D3DFVF_XYZRHW | D3DFVF_TEX1);
g_d3dDevice->DrawPrimitiveUP(D3DPT_TRIANGLESTRIP, 2, quad, sizeof(VERTEX));
}
void DrawShadowMap() {
//set up light matrices
D3DXMATRIX lightView, lightProj;
D3DXMatrixLookAtLH(&lightView, &D3DXVECTOR3(5.0f, 5.0f, 5.0f),
&D3DXVECTOR3(0.0f, 0.0f, 0.0f), &D3DXVECTOR3(0.0f, 1.0f, 0.0f));
D3DXMatrixPerspectiveFovLH(&lightProj, 0.5f, 1.0f, 1.0f, 100.0f);
D3DXMatrixIdentity(&g_world);
g_lightViewProj = lightView * lightProj;
//upload matrices
g_shadow->SetMatrix("lightViewProj", &(g_world * g_lightViewProj));
g_shadow->CommitChanges();
//switch render targets
g_d3dDevice->GetRenderTarget(0, &g_oldSurface);
g_d3dDevice->GetDepthStencilSurface(&g_oldZBuffer);
g_d3dDevice->SetRenderTarget(0, g_shadowMapSurface);
g_d3dDevice->SetDepthStencilSurface(g_shadowMapZBuffer);
g_d3dDevice->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, 0xffffffff, 1.0f, 0);
g_shadow->BeginPass(0);
g_teapot->DrawSubset(0);
g_shadow->EndPass();
//restore render target
g_d3dDevice->SetRenderTarget(0, g_oldSurface);
g_d3dDevice->SetDepthStencilSurface(g_oldZBuffer);
}
void DrawScene() {
//set up matrices
D3DXMatrixIdentity(&g_world);
D3DXMATRIX view, proj;
D3DXMatrixLookAtLH(&view, &D3DXVECTOR3(5.0f, 5.0f, -5.0f),
&D3DXVECTOR3(0.0f, 0.0f, 0.0f), &D3DXVECTOR3(0.0f, 1.0f, 0.0f));
D3DXMatrixPerspectiveFovLH(&proj, D3DX_PI/4, 1.33f, 1.0f, 100.0f);
g_viewProj = view * proj;
//light projection matrix
float offset = 0.5f + (0.5f/(float)SHADOWMAP_SIZE);
D3DXMATRIX textureAdjust(0.5f, 0.0f, 0.0f, 0.0f,
0.0f, -0.5f, 0.0f, 0.0f,
0.0f, 0.0f, 0.0f, 0.0f,
offset, offset, 0.0f, 1.0f);
g_lightProjTex = g_world * g_lightViewProj * textureAdjust;
//upload matrices
g_shadow->SetMatrix("world", &g_world);
g_shadow->SetMatrix("viewProj", &g_viewProj);
g_shadow->SetMatrix("lightProjTex", &g_lightProjTex);
g_shadow->SetVector("lightPos", &D3DXVECTOR4(5.0f, 5.0f, 5.0f, 0.0f));
g_shadow->SetVector("diffuse", &D3DXVECTOR4(0.8f, 0.0f, 0.0f, 1.0f));
g_shadow->SetVector("ambient", &D3DXVECTOR4(0.2f, 0.0f, 0.0f, 1.0f));
g_shadow->SetTexture("ShadowMap", g_shadowMap);
g_shadow->CommitChanges();
//create ground plane
struct VERTEX {
float x, y, z;
float nx, ny, nz;
};
VERTEX quad[4] = {
{20.0f, -10.0f, 20.0f, 0.0f, 1.0f, 0.0f},
{20.0f, -10.0f, -20.0f, 0.0f, 1.0f, 0.0f},
{-20.0f, -10.0f, 20.0f, 0.0f, 1.0f, 0.0f},
{-20.0f, -10.0f, -20.0f, 0.0f, 1.0f, 0.0f}
};
g_d3dDevice->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, 0xff000000, 1.0f, 0);
g_shadow->BeginPass(1);
g_teapot->DrawSubset(0);
g_d3dDevice->SetFVF(D3DFVF_XYZ | D3DFVF_NORMAL);
g_d3dDevice->DrawPrimitiveUP(D3DPT_TRIANGLESTRIP, 2, quad, sizeof(VERTEX));
g_shadow->EndPass();
}
//camera matrices
float4x4 world;
float4x4 viewProj;
//light matrix
float4x4 lightViewProj;
//light projected texture adjustment matrix
float4x4 lightProjTex;
//colours for diffuse lighting
float4 ambient;
float4 diffuse;
//position of diffuse point light
float3 lightPos;
//------------ Shadow map texture sampler --------------------
texture ShadowMap;
sampler ShadowMapSampler = sampler_state {
Texture = <ShadowMap>;
MinFilter = LINEAR;
MagFilter = LINEAR;
MipFilter = NONE;
AddressU = CLAMP;
AddressV = CLAMP;
AddressW = CLAMP;
};
//------------ Shadow map creation shaders -------------------
struct createIn {
float3 pos: POSITION;
};
struct createOut {
float4 pos: POSITION;
float depth: TEXCOORD0;
};
void CreateShadowMapVS(in createIn input, out createOut output) {
//transform position into light space
output.pos = mul(float4(input.pos, 1.0), lightViewProj);
//perspective divide pos.z to get depth
output.depth = output.pos.z/output.pos.w;
}
float4 CreateShadowMapPS(in float depth: TEXCOORD0) : COLOR {
return depth;
}
//------------ Scene render with shadow map ------------------
struct vsIn {
float3 pos: POSITION;
float3 norm: NORMAL;
};
struct vsOut {
float4 final: POSITION;
float3 pos: TEXCOORD0;
float3 norm: TEXCOORD1;
float4 uv: TEXCOORD2;
float depth: TEXCOORD3;
};
void VS(in vsIn input, out vsOut output) {
//transform vertex to world and save for pixel shader
output.final = mul(float4(input.pos, 1.0), world);
output.pos = output.final.xyz;
//transform vertex to screen space
output.final = mul(output.final, viewProj);
//transform normal into world space, ignoring translation
output.norm = normalize(mul(input.norm, (float3x3) world));
//get shadow map uv coordinates for this vertex
output.uv = mul(input.pos, lightProjTex);
//get depth of vertex from light source
float4 pos = mul(input.pos, lightViewProj);
output.depth = pos.z/pos.w - 0.001;
}
struct psIn {
float3 pos: TEXCOORD0;
float3 norm: TEXCOORD1;
float4 uv: TEXCOORD2;
float depth: TEXCOORD3;
};
float4 PS(in psIn input) : COLOR {
//get shadow map sample for depth comparison
float depth = tex2Dproj(ShadowMapSampler, input.uv).x;
//set ambient colour here as diffuse lighting may be skipped
float4 colour = ambient;
//test whether this pixel is lit or not
if(input.depth - depth > 0.0) {
//renormalize
input.norm = normalize(input.norm);
//get vector to the light source
float3 l = normalize(lightPos - input.pos);
//get amount of light at surface
float d = saturate(dot(input.norm, l));
//add diffuse component to colour
colour += diffuse*d;
}
return colour;
}
//----------------- techniques ----------------------------------
technique Shader {
pass P0 {
VertexShader = compile vs_1_1 CreateShadowMapVS();
PixelShader = compile ps_2_0 CreateShadowMapPS();
}
pass P1 {
Sampler[0] = (ShadowMapSampler);
VertexShader = compile vs_2_0 VS();
PixelShader = compile ps_2_0 PS();
}
}