PDA

View Full Version : Shadow mapping



NecroDOME
05-09-2006, 10:58 AM
So, after a while reading tutorials n'stuff I deceided to update my engine so it will support shadow mapping.

But now I'm a little stuck :(

This code is NOT from me, but I want it to work with my engine
constructor TD3DMeshShadow.Create(C:IDirect3DDevice9; _mesh:TD3DXMesh; _light:TD3DXVector3);
var i:integer;

begin
Canvas:=C;
Mesh:=_Mesh;
light:=_Light;

mesh.Mesh.LockVertexBuffer(0, Pointer(pVertices));
mesh.Mesh.LockIndexBuffer(0, Pointer(pIndices));
numFaces:=mesh.Mesh.GetNumFaces;

etLength(pEdges, NumFaces*3);
NumEdges:=0;

loclight:=light;

for i:=0 to NumFaces-1 do begin

wFace0:=pIndices[3*i+0];
wFace1:=pIndices[3*i+1];
wFace2:=pIndices[3*i+2];

v0:=pVertices[wFace0].position;
v1:=pVertices[wFace1].position;
v2:=pVertices[wFace2].position;

D3DXVec3Subtract(vCross1,v2,v1);
D3DXVec3Subtract(vCross2,v1,v0);
D3DXVec3Cross(vNormal,vCross1,vCross2);

if D3DXVec3Dot(vNormal,loclight)>=0.0 then begin
AddEdge(pEdges,NumEdges,wFace0,wFace1);
AddEdge(pEdges,NumEdges,wFace1,wFace2);
AddEdge(pEdges,NumEdges,wFace2,wFace0);
end;
end;

NumVertices:=0;
D3DXVec3Scale(v001,loclight,10);
for i:=0 to NumEdges-1 do begin
v1:=pVertices[pEdges[2*i+0]].position;
v2:=pVertices[pEdges[2*i+1]].position;

D3DXVec3Subtract(v3,v1,v001);
D3DXVec3Subtract(v4,v2,v001);

m_pVertices[NumVertices]:=v1; inc(NumVertices);
m_pVertices[NumVertices]:=v2; inc(NumVertices);
m_pVertices[NumVertices]:=v3; inc(NumVertices);

m_pVertices[NumVertices]:=v2; inc(NumVertices);
m_pVertices[NumVertices]:=v4; inc(NumVertices);
m_pVertices[NumVertices]:=v3; inc(NumVertices);
end;

vb.Lock(0, NumVertices, zeiger, D3DLoCK_DISCARD);
Move(m_pVertices, zeiger^, NumVertices*SizeOf(TD3DXVector3));
vb.Unlock;

setLength(pEdges, 0);

mesh.Mesh.UnlockVertexBuffer;
mesh.Mesh.UnlockIndexBuffer;
end;

This piece of code uses Lock/Unlock an Index buffer within a TD3DXMesh. My engine supports only my own mesh formats, so how can I adapt this code to work only with vertex buffers or where do the indices come from?

Here are the resources I got it from:
Delphi Code:
http://www.delphidev.de/phpBB2/viewtopic.php?t=859&postdays=0&postorder=asc&start=0

c++ code:
http://www.codesampler.com/dx9src/dx9src_7.htm#dx9_shadow_volume

NecroDOME
05-09-2006, 04:57 PM
Still can't get it to work :(

Here is the full source of my shadow mapper...

unit Necro3DShadowMapper;

interface

uses
Windows, SysUtils,
OmegaScreen,
Direct3D9, D3DX9,
Necro3DLight, Necro3DGeometry, Necro3DMesh, Necro3DVertexDefs, Necro3DMeshInfo;

type
TNecro3D_ShadowMapper = class
private
FScreen: TOmegaScreen;
FGeometry : TNecro3D_Geometry;
FMatrix : TD3DMatrix;
FRenderEngine: Pointer;

m_pVertices : array[0..32000] of TD3DVector;
NumVertices : LongWord;
NumFaces : LongWord;
zeiger : Pointer;

pEdges : array of Word;
NumEdges : LongWord;

vb : IDirect3DVertexBuffer9;

public
TwoSidedStencilsAvailable : boolean;
constructor Create;
procedure Init(Screen :TOmegaScreen; Geometry: TNecro3D_Geometry; RenderEngine: Pointer);
procedure RenderShadowToStencilBuffer;
procedure RenderShadowToScene;
procedure RenderShadow(Light: TNecro3D_Light; Mesh: TNecro3D_MeshInfo);
procedure BuildShadowVolume(Mesh: TNecro3D_Mesh; Lod: integer; Light: TD3DVector);
procedure AddEdge(var _pEdges:array of Word; var _dwNumEdges:LongWord; _v0,_v1:Word);
procedure Reset;
procedure Render;
end;

implementation

uses
Necro3DUtils, Necro3DObject, Necro3DLog, Necro3DRender;

{ TNecro3D_ShadowMapper }

//************************************************** ************
// Adds an edge to a list of silohuette edges of a shadow volume.
//************************************************** ************
procedure TNecro3D_ShadowMapper.AddEdge(var _pEdges: array of Word;
var _dwNumEdges: LongWord; _v0, _v1: Word);
var i:integer;
begin
for i:=0 to _dwNumEdges-1 do
begin
if ((_pEdges[2*i+0]=_v0) and (_pEdges[2*i+1]=_v1)) or
((_pEdges[2*i+0]=_v1) and (_pEdges[2*i+1]=_v0)) then
begin

if (_dwNumEdges > 1) then
begin
_pEdges[2*i+0]:=_pEdges[2*(_dwNumEdges-1)+0];
_pEdges[2*i+1]:=_pEdges[2*(_dwNumEdges-1)+1];
end;

Dec(_dwNumEdges);
Exit;
end;
end;

_pEdges[2*_dwNumEdges+0] := _v0;
_pEdges[2*_dwNumEdges+1] := _v1;
inc(_dwNumEdges);
end;

//************************************************** ************
// Technique used considers each triangle of the mesh, and adds it's
// edges to a temporary list. The edge list is maintained, such that
// only silohuette edges are kept. Finally, the silohuette edges are
// extruded to make the shadow volume vertex list.
//************************************************** ************
procedure TNecro3D_ShadowMapper.BuildShadowVolume(Mesh: TNecro3D_Mesh; Lod: integer;
Light: TD3DVector);
var i : integer;
LocLight : TD3DVector;
wface0, wface1, wface2 : Word;
v0, v1, v2, v3, v4: TD3DVector;
v001: TD3DVector;
vNormal, vCross1, vCross2:TD3DVector;
begin
// Mesh.LockVertexBuffer(0, Pointer(pVertices));
// Mesh.LockIndexBuffer(0, Pointer(pIndices));

numFaces := Mesh.LOD[Lod].Necro3D_Object.GetTriangleCount;

SetLength(pEdges, NumFaces*3);
NumEdges := 0;

LocLight := Light;

for i := 0 to NumFaces-1 do
begin
with Mesh.LOD[Lod].Necro3D_Object do
begin
wFace0 := Triangles[i].Verticles[0].Vector;
wFace1 := Triangles[i].Verticles[1].Vector;
wFace2 := Triangles[i].Verticles[2].Vector;

v0 := Vectors[ wFace0 ];
v1 := Vectors[ wFace1 ];
v2 := Vectors[ wFace2 ];
end;
{
wFace0 := pIndices[3*i+0];
wFace1 := pIndices[3*i+1];
wFace2 := pIndices[3*i+2];

v0 := MakeD3DVector(pVertices[wFace0].x, pVertices[wFace0].y, pVertices[wFace0].z);
v1 := MakeD3DVector(pVertices[wFace1].x, pVertices[wFace1].y, pVertices[wFace1].z);
v2 := MakeD3DVector(pVertices[wFace2].x, pVertices[wFace2].y, pVertices[wFace2].z);
}

D3DXVec3Subtract(vCross1, v2, v1);
D3DXVec3Subtract(vCross2, v1, v0);
D3DXVec3Cross(vNormal, vCross1, vCross2);

if D3DXVec3Dot(vNormal, LocLight)>=0.0 then
begin
AddEdge(pEdges, NumEdges, wFace0, wFace1);
AddEdge(pEdges, NumEdges, wFace1, wFace2);
AddEdge(pEdges, NumEdges, wFace2, wFace0);
end;
end;

NumVertices := 0;
D3DXVec3Scale(v001, LocLight, 10);

for i := 0 to NumEdges-1 do
begin
with Mesh.LOD[Lod].Necro3D_Object do
begin
v1 := Vectors[pEdges[2*i+0]];
v2 := Vectors[pEdges[2*i+1]];
end;
{
v1 := MakeD3DVector(pVertices[pEdges[2*i+0]].x, pVertices[pEdges[2*i+0]].y, pVertices[pEdges[2*i+0]].z);
v2 := MakeD3DVector(pVertices[pEdges[2*i+1]].x, pVertices[pEdges[2*i+1]].y, pVertices[pEdges[2*i+1]].z);
}
D3DXVec3Subtract(v3, v1, v001);
D3DXVec3Subtract(v4, v2, v001);

m_pVertices[NumVertices] := v1; inc(NumVertices);
m_pVertices[NumVertices] := v2; inc(NumVertices);
m_pVertices[NumVertices] := v3; inc(NumVertices);

m_pVertices[NumVertices] := v2; inc(NumVertices);
m_pVertices[NumVertices] := v4; inc(NumVertices);
m_pVertices[NumVertices] := v3; inc(NumVertices);
end;

FScreen.Device.CreateVertexBuffer(32000*SizeOf(TD3 DVector),
D3DUSAGE_WRITEONLY,
D3DFVF_XYZ,
D3DPOOL_MANAGED,
vb, nil);

vb.Lock(0, NumVertices*3, zeiger, 0);
Move(m_pVertices, zeiger^, NumVertices*SizeOf(TD3DVector));
vb.Unlock;

setLength(pEdges, 0);

// mesh.Mesh.UnlockVertexBuffer;
// mesh.Mesh.UnlockIndexBuffer;
end;

constructor TNecro3D_ShadowMapper.Create;
begin
TwoSidedStencilsAvailable := False;
end;

procedure TNecro3D_ShadowMapper.Init(Screen: TOmegaScreen; Geometry: TNecro3D_Geometry;
RenderEngine: Pointer);
begin
FScreen := Screen;
FGeometry := Geometry;
FRenderEngine := RenderEngine;
end;

procedure TNecro3D_ShadowMapper.Render;
begin
FScreen.Device.SetFVF( D3DFVF_XYZ );
FScreen.Device.SetStreamSource(0, vb, 0, SizeOf(TD3DVector));
FScreen.Device.DrawPrimitive( D3DPT_TRIANGLELIST, 0, NumVertices);
end;

procedure TNecro3D_ShadowMapper.RenderShadow(Light: TNecro3D_Light; Mesh: TNecro3D_MeshInfo);
var Matrix, MatInverse : TD3DMatrix;
LightInWorldSpace, LightInObjectSpace : TD3DVector;
begin
// transform the light vector from world-space to object-space, so
// the teapot and light will both be in the same frame of reference.
Matrix := Mesh.GetMatrix;

LightInWorldSpace := Maked3DVector( Light.Position.x, Light.Position.y, Light.Position.z );

D3DXMatrixInverse( MatInverse, nil, Matrix );
D3DXVec3TransformNormal( LightInObjectSpace, LightInWorldSpace, MatInverse );

FMatrix := MatInverse;
// Using the light's object-space position, build the shadow volume
// from the teapot's DX9 mesh.

// Reset;
if Mesh.MeshIndex<>-1 then
BuildShadowVolume(FGeometry.MeshList.Meshes[ Mesh.MeshIndex ], 0, LightInObjectSpace );

FScreen.Device.Clear( 0, nil, D3DCLEAR_TARGET or D3DCLEAR_ZBUFFER or D3DCLEAR_STENCIL,
D3DCOLOR_COLORVALUE(0.0, 0.0, 0.0, 1.0), 1.0, 0 );
end;

procedure TNecro3D_ShadowMapper.RenderShadowToScene;
var RenderTextures, RenderLights: boolean;
begin
// Set render states
FScreen.Device.SetRenderState( D3DRS_ZENABLE, 0 );
FScreen.Device.SetRenderState( D3DRS_STENCILENABLE, 1 );
FScreen.Device.SetRenderState( D3DRS_ALPHABLENDENABLE, 1 );
FScreen.Device.SetRenderState( D3DRS_SRCBLEND, D3DBLEND_SRCALPHA );
FScreen.Device.SetRenderState( D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA );


FScreen.Device.SetTextureStageState( 0, D3DTSS_COLORARG1, D3DTA_TEXTURE );
FScreen.Device.SetTextureStageState( 0, D3DTSS_COLORARG2, D3DTA_DIFFUSE );
FScreen.Device.SetTextureStageState( 0, D3DTSS_COLOROP, D3DTOP_MODULATE );
FScreen.Device.SetTextureStageState( 0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE );
FScreen.Device.SetTextureStageState( 0, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE );
FScreen.Device.SetTextureStageState( 0, D3DTSS_ALPHAOP, D3DTOP_MODULATE );

// Only write where stencil value >= 1 (count indicates # of shadows that
// overlap that pixel)
FScreen.Device.SetRenderState( D3DRS_STENCILREF, 1 );
FScreen.Device.SetRenderState( D3DRS_STENCILFUNC, D3DCMP_LESSEQUAL );
FScreen.Device.SetRenderState( D3DRS_STENCILPASS, D3DSTENCILOP_KEEP );

// Render world ??
// Draw a big, gray square
{
RenderTextures := TNecro3D_Render( FRenderEngine ).RenderTextures;
RenderLights := TNecro3D_Render( FRenderEngine ).RenderLights;
TNecro3D_Render( FRenderEngine ).RenderTextures := False;
TNecro3D_Render( FRenderEngine ).RenderLights := False;
TNecro3D_Render( FRenderEngine ).RenderGeometry;
TNecro3D_Render( FRenderEngine ).RenderTextures := RenderTextures;
TNecro3D_Render( FRenderEngine ).RenderLights := RenderLights;
}

// g_pd3dDevice->SetFVF( ShadowVertex::FVF_Flags );
// g_pd3dDevice->SetStreamSource( 0, m_pBigSquareVB, 0, sizeof(ShadowVertex) );
// g_pd3dDevice->DrawPrimitive( D3DPT_TRIANGLESTRIP, 0, 2 );

// Restore render states
FScreen.Device.SetRenderState( D3DRS_ZENABLE, 1 );
FScreen.Device.SetRenderState( D3DRS_STENCILENABLE, 0 );
FScreen.Device.SetRenderState( D3DRS_ALPHABLENDENABLE, 0 );
end;

procedure TNecro3D_ShadowMapper.RenderShadowToStencilBuffer;
begin
// Disable z-buffer writes (note: z-testing still occurs), and enable the
// stencil-buffer
FScreen.Device.SetRenderState(D3DRS_ZWRITEENABLE, 0);
FScreen.Device.SetRenderState(D3DRS_STENCILENABLE, 1);

// Dont bother with interpolating color
FScreen.Device.SetRenderState( D3DRS_SHADEMODE, D3DSHADE_FLAT );

// Set up stencil compare fuction, reference value, and masks.
// Stencil test passes if ((ref & mask) cmpfn (stencil & mask)) is true.
// Note: since we set up the stencil-test to always pass, the STENCILFAIL
// renderstate is really not needed.
FScreen.Device.SetRenderState( D3DRS_STENCILFUNC, D3DCMP_ALWAYS );
FScreen.Device.SetRenderState( D3DRS_STENCILZFAIL, D3DSTENCILOP_KEEP );
FScreen.Device.SetRenderState( D3DRS_STENCILFAIL, D3DSTENCILOP_KEEP );

// If z-test passes, inc/decrement stencil buffer value
FScreen.Device.SetRenderState( D3DRS_STENCILREF, 1 );
FScreen.Device.SetRenderState( D3DRS_STENCILMASK, $FFFFFFFF );
FScreen.Device.SetRenderState( D3DRS_STENCILWRITEMASK, $FFFFFFFF );
FScreen.Device.SetRenderState( D3DRS_STENCILPASS, D3DSTENCILOP_INCR );

// Make sure that no pixels get drawn to the frame buffer
FScreen.Device.SetRenderState( D3DRS_ALPHABLENDENABLE, 1 );
FScreen.Device.SetRenderState( D3DRS_SRCBLEND, D3DBLEND_ZERO );
FScreen.Device.SetRenderState( D3DRS_DESTBLEND, D3DBLEND_ONE );

if TwoSidedStencilsAvailable then
begin
// With 2-sided stencil, we can avoid rendering twice:
FScreen.Device.SetRenderState( D3DRS_TWOSIDEDSTENCILMODE, 1 );
FScreen.Device.SetRenderState( D3DRS_CCW_STENCILFUNC, D3DCMP_ALWAYS );
FScreen.Device.SetRenderState( D3DRS_CCW_STENCILZFAIL, D3DSTENCILOP_KEEP );
FScreen.Device.SetRenderState( D3DRS_CCW_STENCILFAIL, D3DSTENCILOP_KEEP );
FScreen.Device.SetRenderState( D3DRS_CCW_STENCILPASS, D3DSTENCILOP_DECR );

FScreen.Device.SetRenderState( D3DRS_CULLMODE, D3DCULL_NONE );

// Draw both sides of shadow volume in stencil/z only
FScreen.Device.SetTransform( D3DTS_WORLD, FMatrix );
Render;

FScreen.Device.SetRenderState( D3DRS_TWOSIDEDSTENCILMODE, 0 );
end else
begin
// Draw front-side of shadow volume in stencil/z only
FScreen.Device.SetTransform( D3DTS_WORLD, FMatrix );
Render;

// Now reverse cull order so back sides of shadow volume are written.
FScreen.Device.SetRenderState( D3DRS_CULLMODE, D3DCULL_CW );

// Decrement stencil buffer value
FScreen.Device.SetRenderState( D3DRS_STENCILPASS, D3DSTENCILOP_DECR );

// Draw back-side of shadow volume in stencil/z only
FScreen.Device.SetTransform( D3DTS_WORLD, FMatrix );
Render;
end;

// Restore render states
FScreen.Device.SetRenderState( D3DRS_SHADEMODE, D3DSHADE_GOURAUD );
FScreen.Device.SetRenderState( D3DRS_CULLMODE, D3DCULL_CCW );
FScreen.Device.SetRenderState( D3DRS_ZWRITEENABLE, 1 );
FScreen.Device.SetRenderState( D3DRS_STENCILENABLE, 0 );
FScreen.Device.SetRenderState( D3DRS_ALPHABLENDENABLE, 0 );
end;

procedure TNecro3D_ShadowMapper.Reset;
begin
NumVertices := 0;
end;

end.

Hopefully I get it up and running soon!