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();
}
}