DX9 Cube shadow maps

Started by
11 comments, last by smittix 12 years, 11 months ago
No problem, thanks for your help by the way. I'm including all relevant code below. Basically, the shadow from the sphere is correctly projected onto the wall behind it, but assuming everything was done correctly, the backside of the sphere should have been shadowed as well. However, the backside of the sphere has no shadow applied to it at all.

DX Code

float ScreenWidth = 640.0F;
float ScreenHeight = 480.0F;

#include <windows.h>
#include <mmsystem.h>
#include <d3d9.h>
#include <d3dx9.h>
#include "DirectInput.h"
#include "Camera.h"

#define CUBEMAP_RESOLUTION 256

// Globals
HWND hwnd = NULL;
IDirect3DDevice9* D3DDevice = NULL;

DirectInput *DInput;
Camera *ActiveCamera;
ID3DXMesh* RoomMesh;
ID3DXMesh* BallMesh;
ID3DXMesh* TeapotMesh;
IDirect3DTexture9* RoomTexture;
IDirect3DTexture9* StoneTexture;
IDirect3DCubeTexture9* CubeMapTexture;
ID3DXRenderToEnvMap* RenderToEnvMap;
ID3DXEffect* EnvMapEffect;
ID3DXEffect* AmbientEffect;

D3DXVECTOR3 LightPosition(-8.0f, 12.0f, 0.0f);
D3DXMATRIX Proj;

//Timer
DWORD CurrentTime;
DWORD LastTime;
float DeltaTime;

// Prototypes
void UpdateDeltaTime(void);
void LoadXFile(char* Filename, ID3DXMesh* &Mesh);
void Setup(void);
void Cleanup(void);
void RenderScene(void);
void RenderTeapot(void);
void RenderSceneIntoEnvMap(D3DXVECTOR3 Position);
D3DXMATRIX GetCubeMapViewMatrix(DWORD dwFace, D3DXVECTOR3 Position);
void Render(void);
void InitializeDirect3D(void);
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow);

///////////////////////////////////////////////////////
// GetDeltaTime
///////////////////////////////////////////////////////
void UpdateDeltaTime(void)
{
CurrentTime = timeGetTime();

DeltaTime = ((float)CurrentTime - (float)LastTime) * 0.001f;

LastTime = timeGetTime();
}

///////////////////////////////////////////////////////
// Load an X File
///////////////////////////////////////////////////////
void LoadXFile(char* Filename, ID3DXMesh* &Mesh)
{
//Zero Mesh and create buffer
Mesh = 0;
ID3DXBuffer* MeshBuffer = 0;

//Load and optimize the mesh
D3DXLoadMeshFromX( Filename, D3DXMESH_MANAGED, D3DDevice, &MeshBuffer, 0, 0, 0, &Mesh);
Mesh->OptimizeInplace( D3DXMESHOPT_ATTRSORT | D3DXMESHOPT_COMPACT | D3DXMESHOPT_VERTEXCACHE, (DWORD*)MeshBuffer->GetBufferPointer(), 0, 0, 0);

//Release and zero the buffer
MeshBuffer->Release(); MeshBuffer = NULL;
}

///////////////////////////////////////////////////////
// Setup
///////////////////////////////////////////////////////
void Setup( void )
{
//Set the Projection matrix
D3DXMatrixPerspectiveFovLH(&Proj, D3DX_PI/4.0f, ScreenWidth / ScreenHeight, 1.0f, 1000.0f);
D3DDevice->SetTransform(D3DTS_PROJECTION, &Proj);

//Get the back buffer description
IDirect3DSurface9* BackBuffer = NULL;
D3DSURFACE_DESC BackBufferSurfaceDesc;
D3DDevice->GetBackBuffer(0, 0, D3DBACKBUFFER_TYPE_MONO, &BackBuffer);
BackBuffer->GetDesc(&BackBufferSurfaceDesc);
BackBuffer->Release();

//Create RenderToEnvMap object
D3DXCreateRenderToEnvMap(D3DDevice, CUBEMAP_RESOLUTION, 1, BackBufferSurfaceDesc.Format, TRUE, D3DFMT_D24X8, &RenderToEnvMap);

//Create the cubemap texture
D3DXCreateCubeTexture(D3DDevice, CUBEMAP_RESOLUTION, 1, D3DUSAGE_RENDERTARGET, BackBufferSurfaceDesc.Format, D3DPOOL_DEFAULT, &CubeMapTexture);

//Load the environment map effect
ID3DXBuffer* e = 0;
D3DXCreateEffectFromFile(D3DDevice, "HLSLEnvironmentMap.fx", NULL, NULL, D3DXSHADER_DEBUG, NULL, &EnvMapEffect, &e);
if(e)
{
::MessageBox(0, (char*)e->GetBufferPointer(), 0, 0);
}

D3DXCreateEffectFromFile(D3DDevice, "Ambient.fx", NULL, NULL, D3DXSHADER_DEBUG, NULL, &AmbientEffect, &e);
if(e)
{
::MessageBox(0, (char*)e->GetBufferPointer(), 0, 0);
}

//Load the X files
LoadXFile("Room.X", RoomMesh);
LoadXFile("Ball.X", BallMesh);
LoadXFile("Teapot.X", TeapotMesh);

D3DVERTEXELEMENT9 Elements[] =
{
{ 0, sizeof( float ) * 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0 },
{ 0, sizeof( float ) * 3, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_NORMAL, 0 },
{ 0, sizeof( float ) * 6, D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 0 },
{ 0, sizeof( float ) * 8, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TANGENT, 0 },
{ 0, sizeof( float ) * 11, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_BINORMAL, 0 },
D3DDECL_END()
};

//Compute the tangent frame for the mesh. (Needed for normal mapping)
ID3DXMesh* Temp;
RoomMesh->CloneMesh( D3DXMESH_MANAGED, Elements, D3DDevice, &Temp );
RoomMesh->Release();
RoomMesh = Temp;
void* Data;
RoomMesh->LockVertexBuffer(D3DLOCK_DISCARD, &Data);
RoomMesh->UnlockVertexBuffer();
D3DXComputeTangentFrame(RoomMesh, 0);

//Load the texture into RoomTexture
D3DXCreateTextureFromFile( D3DDevice, "conc02.jpg", &RoomTexture );
D3DXCreateTextureFromFile( D3DDevice, "stone_wall.bmp", &StoneTexture );

//Create a new instance of Camera
ActiveCamera = new Camera();

//Disable lighting for this sample
D3DDevice->SetRenderState( D3DRS_LIGHTING, false );

//Set some render states to determine texture quality
D3DDevice->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
D3DDevice->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
D3DDevice->SetSamplerState(0, D3DSAMP_MIPFILTER, D3DTEXF_LINEAR);
}

///////////////////////////////////////////////////////
// Cleanup
///////////////////////////////////////////////////////
void Cleanup( void )
{
//Release all DX objects
RoomMesh->Release();
BallMesh->Release();
TeapotMesh->Release();
RoomTexture->Release();
StoneTexture->Release();
CubeMapTexture->Release();
RenderToEnvMap->Release();
EnvMapEffect->Release();
AmbientEffect->Release();

//Delete the Direct Input device
delete DInput;

//Delete the Camera object
delete ActiveCamera;

//Release the Direct3D device
D3DDevice->Release();
}

///////////////////////////////////////////////////////
// Render the scene without the teapot
///////////////////////////////////////////////////////
void RenderScene(const D3DXMATRIX& view, const D3DXMATRIX& proj)
{
AmbientEffect->SetTechnique("EnvironmentMap");

//Only one pass in this shader
UINT numPasses = 0;
AmbientEffect->Begin(&numPasses, 0);
AmbientEffect->BeginPass(0);

// Render room
D3DXMATRIX World;
D3DXMatrixIdentity(&World);
AmbientEffect->SetTexture("ColorTex", RoomTexture);
AmbientEffect->SetMatrix("WorldViewProj", &(World * view * proj));
AmbientEffect->CommitChanges();
RoomMesh->DrawSubset(0);

// Render ball
World._41 = 8.0f;
World._42 = 12.0f;
AmbientEffect->SetTexture("ColorTex", StoneTexture);
AmbientEffect->SetMatrix("WorldViewProj", &(World * view * proj));
AmbientEffect->CommitChanges();
BallMesh->DrawSubset(0);

AmbientEffect->EndPass();
AmbientEffect->End();
}

///////////////////////////////////////////////////////
// Render the teapot with environment mapping
///////////////////////////////////////////////////////
void RenderFinalScene(void)
{
//Set the world matrix
D3DXMATRIX World;
D3DXMatrixIdentity(&World);

//Set shader variables
EnvMapEffect->SetMatrix("World", &World);
EnvMapEffect->SetMatrix("WorldViewProj", &(World * ActiveCamera->View * Proj));
EnvMapEffect->SetValue("LightPos", &LightPosition, sizeof(D3DXVECTOR3));
EnvMapEffect->SetTexture("ColorTex", RoomTexture);
EnvMapEffect->SetTexture("CubeShadowTex", CubeMapTexture);
EnvMapEffect->SetTechnique("CubeShadowMap");

//Only one pass in this shader
UINT numPasses = 0;
EnvMapEffect->Begin(&numPasses, 0);
EnvMapEffect->BeginPass(0);

//Draw the teapot with the environment map applied
RoomMesh->DrawSubset(0);

//Render ball
World._41 = 8.0f;
World._42 = 12.0f;
EnvMapEffect->SetMatrix("World", &World);
EnvMapEffect->SetMatrix("WorldViewProj", &(World * ActiveCamera->View * Proj));
EnvMapEffect->SetTexture("ColorTex", StoneTexture);
EnvMapEffect->CommitChanges();
BallMesh->DrawSubset(0);

EnvMapEffect->EndPass();
EnvMapEffect->End();
}

///////////////////////////////////////////////////////
// Render the scene 6 times to make the environment map
///////////////////////////////////////////////////////
void RenderSceneIntoEnvMap(D3DXVECTOR3 Position)
{
//Need a square 90 degree projection to get a seamless environment map texture
D3DXMATRIX NewProj;
D3DXMatrixPerspectiveFovLH( &NewProj, D3DX_PI * 0.5f, 1.0f, 0.5f, 1000.0f );
D3DDevice->SetTransform(D3DTS_PROJECTION, &NewProj);

//Begin rendering to environment map texture
RenderToEnvMap->BeginCube(CubeMapTexture);

for(UINT i = 0; i < 6; i++)
{
//Get cube map render target surface and set it as render target
RenderToEnvMap->Face((D3DCUBEMAP_FACES)i, 0 );

//Set the view transform for this cubemap surface
D3DXMATRIX viewProj = GetCubeMapViewMatrix((D3DCUBEMAP_FACES)i, Position);

//Rrender the scene (except for the teapot)
D3DDevice->Clear( 0, NULL, D3DCLEAR_ZBUFFER, 0x00000000, 1.0f, 0 );
RenderScene(viewProj, NewProj);
}

//End rendering to environment map texture
RenderToEnvMap->End(0);
}

///////////////////////////////////////////////////////
// Get propor view matrix for rendering cubemap texture
///////////////////////////////////////////////////////
D3DXMATRIX GetCubeMapViewMatrix(DWORD dwFace, D3DXVECTOR3 Position)
{
D3DXVECTOR3 LookDir;
D3DXVECTOR3 UpDir;

switch( dwFace )
{
case D3DCUBEMAP_FACE_POSITIVE_X:
LookDir = D3DXVECTOR3( 1.0f, 0.0f, 0.0f ) + Position;
UpDir = D3DXVECTOR3( 0.0f, 1.0f, 0.0f );
break;
case D3DCUBEMAP_FACE_NEGATIVE_X:
LookDir = D3DXVECTOR3(-1.0f, 0.0f, 0.0f ) + Position;
UpDir = D3DXVECTOR3( 0.0f, 1.0f, 0.0f );
break;
case D3DCUBEMAP_FACE_POSITIVE_Y:
LookDir = D3DXVECTOR3( 0.0f, 1.0f, 0.0f ) + Position;
UpDir = D3DXVECTOR3( 0.0f, 0.0f,-1.0f );
break;
case D3DCUBEMAP_FACE_NEGATIVE_Y:
LookDir = D3DXVECTOR3( 0.0f,-1.0f, 0.0f ) + Position;
UpDir = D3DXVECTOR3( 0.0f, 0.0f, 1.0f );
break;
case D3DCUBEMAP_FACE_POSITIVE_Z:
LookDir = D3DXVECTOR3( 0.0f, 0.0f, 1.0f ) + Position;
UpDir = D3DXVECTOR3( 0.0f, 1.0f, 0.0f );
break;
case D3DCUBEMAP_FACE_NEGATIVE_Z:
LookDir = D3DXVECTOR3( 0.0f, 0.0f,-1.0f ) + Position;
UpDir = D3DXVECTOR3( 0.0f, 1.0f, 0.0f );
break;
}

// Set the view transform for this cubemap surface
D3DXMATRIX NewView;
D3DXMatrixLookAtLH( &NewView, &Position, &LookDir, &UpDir );
return NewView;
}

///////////////////////////////////////////////////////
// Render
///////////////////////////////////////////////////////
void Render( void )
{
//Update the Delta Time
UpdateDeltaTime();

//Poll the DirectInput device
DInput->Poll();

//Move Camera
float ForwardUnits = 0;
float SidewardUnits = 0;

//Process keyboard presses
if( DInput->KeyDown( DIK_W ) )
ForwardUnits = 50.0f * DeltaTime;
if( DInput->KeyDown( DIK_S ) )
ForwardUnits = -50.0f * DeltaTime;
if( DInput->KeyDown( DIK_A ) )
SidewardUnits = -50.0f * DeltaTime;
if( DInput->KeyDown( DIK_D ) )
SidewardUnits = 50.0f * DeltaTime;

//Process mouse button presses
if( DInput->MouseButtonDown(0) )
ForwardUnits = 50.0f * DeltaTime;
if( DInput->MouseButtonDown(1) )
ForwardUnits = -50.0f * DeltaTime;

//Rotate Camera
ActiveCamera->Pitch -= DInput->MouseState.lY * DeltaTime * 0.1f;
ActiveCamera->Yaw -= DInput->MouseState.lX * DeltaTime * 0.1f;

//Update the camera
ActiveCamera->UpdateCamera(ForwardUnits, SidewardUnits);

//Update the active view
D3DDevice->SetTransform(D3DTS_VIEW, &ActiveCamera->View);

//Create the environment map texture
RenderSceneIntoEnvMap(LightPosition);

D3DDevice->Clear( 0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, 0x00000000, 1.0f, 0 );
D3DDevice->BeginScene();

//Render the teapot with environment mapping
RenderFinalScene();

D3DDevice->EndScene();
D3DDevice->Present( NULL, NULL, NULL, NULL );
}

///////////////////////////////////////////////////////
// WindowProc
///////////////////////////////////////////////////////
LRESULT CALLBACK WindowProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam )
{
switch( msg )
{
case WM_KEYDOWN:
{
if( wParam == VK_ESCAPE )
{
PostQuitMessage(0);
break;
}
}
break;

case WM_CLOSE:
{
PostQuitMessage(0);
}

case WM_DESTROY:
{
PostQuitMessage(0);
}
break;

default:
{
return DefWindowProc( hWnd, msg, wParam, lParam );
}
break;
}
return 0;
}

///////////////////////////////////////////////////////
// InitializeDirect3D
///////////////////////////////////////////////////////
void InitializeDirect3D( void )
{
IDirect3D9* D3D = NULL;

D3D = Direct3DCreate9( D3D_SDK_VERSION );

if( !D3D )
{
if( D3D != NULL )
D3D->Release();

::MessageBox(0, "Direct3DCreate9() - Failed", 0, 0);
return;
}

D3DDISPLAYMODE d3ddm;

if( FAILED( D3D->GetAdapterDisplayMode( D3DADAPTER_DEFAULT, &d3ddm ) ) )
{
if( D3D != NULL )
D3D->Release();

::MessageBox(0, "GetAdapterDisplayMode() - Failed", 0, 0);
return;
}

HRESULT hr;

if( FAILED( hr = D3D->CheckDeviceFormat( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL,
d3ddm.Format, D3DUSAGE_DEPTHSTENCIL,
D3DRTYPE_SURFACE, D3DFMT_D16 ) ) )
{
if( hr == D3DERR_NOTAVAILABLE )
{
if( D3D != NULL )
D3D->Release();

::MessageBox(0, "CheckDeviceFormat() - Failed", 0, 0);
return;
}
}

D3DCAPS9 d3dCaps;

if( FAILED( D3D->GetDeviceCaps( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, &d3dCaps ) ) )
{
if( D3D != NULL )
D3D->Release();

::MessageBox(0, "GetDeviceCaps() - Failed", 0, 0);
return;
}

DWORD dwBehaviorFlags = 0;

// Use hardware vertex processing if supported, otherwise default to software
if( d3dCaps.VertexProcessingCaps != 0 )
dwBehaviorFlags |= D3DCREATE_HARDWARE_VERTEXPROCESSING;
else
dwBehaviorFlags |= D3DCREATE_SOFTWARE_VERTEXPROCESSING;

// All system checks passed, create the device

D3DPRESENT_PARAMETERS d3dpp;
memset(&d3dpp, 0, sizeof(d3dpp));

d3dpp.BackBufferFormat = D3DFMT_X8R8G8B8;
d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
d3dpp.Windowed = TRUE;
d3dpp.EnableAutoDepthStencil = TRUE;
d3dpp.BackBufferHeight = (UINT)ScreenHeight;
d3dpp.BackBufferWidth = (UINT)ScreenWidth;
d3dpp.AutoDepthStencilFormat = D3DFMT_D24X8; // D3DFMT_D24S8;
d3dpp.Flags = D3DPRESENTFLAG_DISCARD_DEPTHSTENCIL;
d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_ONE;

if( FAILED( D3D->CreateDevice( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hwnd,
dwBehaviorFlags, &d3dpp, &D3DDevice ) ) )
{
if( D3D != NULL )
D3D->Release();

::MessageBox(0, "CreateDevice() - Failed", 0, 0);
return;
}

// No longer needed, release it
if( D3D != NULL )
D3D->Release();
}

///////////////////////////////////////////////////////
// WinMain
///////////////////////////////////////////////////////
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow )
{
WNDCLASSEX winClass;
MSG uMsg;

memset(&uMsg,0,sizeof(uMsg));

winClass.lpszClassName = "MainWindow";
winClass.cbSize = sizeof(WNDCLASSEX);
winClass.style = CS_HREDRAW | CS_VREDRAW;
winClass.lpfnWndProc = WindowProc;
winClass.hInstance = hInstance;
winClass.hIcon = ::LoadIcon(0, IDI_APPLICATION);
winClass.hIconSm = ::LoadIcon(0, IDI_APPLICATION);
winClass.hCursor = LoadCursor(NULL, IDC_ARROW);
winClass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
winClass.lpszMenuName = NULL;
winClass.cbClsExtra = 0;
winClass.cbWndExtra = 0;

if( !RegisterClassEx(&winClass) )
return E_FAIL;

hwnd = CreateWindowEx( NULL, "MainWindow",
"HLSL Dynamic Environment Map - by Chris Smith",
WS_OVERLAPPEDWINDOW,
0, 0, (UINT)ScreenWidth, (UINT)ScreenHeight, NULL, NULL, hInstance, NULL );

if( hwnd == NULL )
return E_FAIL;

ShowWindow( hwnd, nCmdShow );
UpdateWindow( hwnd );

InitializeDirect3D();

//Initialize the Direct Input object
DInput = new DirectInput();
DInput->InitializeDirectInput(hInstance, hwnd, DISCL_NONEXCLUSIVE|DISCL_FOREGROUND,
DISCL_EXCLUSIVE|DISCL_FOREGROUND);

Setup();

while( uMsg.message != WM_QUIT )
{
if( PeekMessage( &uMsg, NULL, 0, 0, PM_REMOVE ) )
{
TranslateMessage( &uMsg );
DispatchMessage( &uMsg );
}
else
Render();
}

Cleanup();

UnregisterClass( "MY_WINDOWS_CLASS", winClass.hInstance );

return (int)uMsg.wParam;
}


Shader

float4x4 World;
float4x4 WorldViewProj;
float3 LightPos;

texture ColorTex;
texture CubeShadowTex;

#define SHADOW_EPSILON 0.00005f

sampler ColorSampler = sampler_state
{
Texture = (ColorTex);
MinFilter = LINEAR;
MagFilter = LINEAR;
MipFilter = LINEAR;
};

sampler CubeShadowSampler = sampler_state
{
Texture = (CubeShadowTex);
MinFilter = LINEAR;
MagFilter = LINEAR;
MipFilter = LINEAR;
AddressU = WRAP;
AddressV = WRAP;
};

struct CubeShadowMap_IN
{
float4 Position : POSITION;
float3 Normal : NORMAL;
float2 TexCoord0 : TEXCOORD0;
float3 Tangent : TANGENT;
float3 Binormal : BINORMAL;
};

struct CubeShadowMap_OUT
{
float4 position : POSITION0;
float3 lightDir : TEXCOORD0; // Vector from pixel to light
float2 texCoord : TEXCOORD1;
float2 depth : TEXCOORD2;
};

void VS(in CubeShadowMap_IN IN, out CubeShadowMap_OUT OUT)
{
//Transform the position from view space to homogeneous projection space
OUT.position = mul(IN.Position, WorldViewProj);

//Compute world space position
float4 WorldPos = mul(IN.Position, World);

//Compute light direction
OUT.lightDir = LightPos.xyz - WorldPos.xyz;

// Pass texture coordinates through
OUT.texCoord = IN.TexCoord0;
}

float4 PS(in CubeShadowMap_OUT IN) : COLOR
{
float dd = length(IN.lightDir);

float depth = IN.lightDir / dd;

// Find color of pixel
float smDepth = texCUBE(CubeShadowSampler, IN.lightDir).x;
float4 Color = tex2D(ColorSampler, IN.texCoord);

if(depth > smDepth+SHADOW_EPSILON)
return Color * 0.5f;
else
return Color;
}

technique CubeShadowMap
{
pass P0
{
vertexShader = compile vs_1_1 VS();
pixelShader = compile ps_2_0 PS();
}
}
Advertisement
That is an issue that I was having as well. You can play around with the bias, which I think you're calling "shadow_epsilon", to see if you can get the shadow on the back of the object. Scanning over your code I don't see an issue with your technique. You've got the core principles working its just a matter of tweaking.
Thanks for checking it out, yeah I'll continue changing things around and hopefully I can resolve it.

This topic is closed to new replies.

Advertisement