wilf lalonde ©2012 comp 4501 95.4501 shadows. wilf lalonde ©2012 comp 4501 how do i draw a pixel?...

148
Wilf LaLonde ©2012 Comp 4501 95.4501 Shadows

Upload: augustus-nelson

Post on 25-Dec-2015

218 views

Category:

Documents


2 download

TRANSCRIPT

Page 1: Wilf LaLonde ©2012 Comp 4501 95.4501 Shadows. Wilf LaLonde ©2012 Comp 4501 How Do I Draw a Pixel? Shadows volumes: Used in Doom (Expensive) From lights,

Wilf LaLonde ©2012Comp 4501

95.450195.4501

Shadows

Page 2: Wilf LaLonde ©2012 Comp 4501 95.4501 Shadows. Wilf LaLonde ©2012 Comp 4501 How Do I Draw a Pixel? Shadows volumes: Used in Doom (Expensive) From lights,

Wilf LaLonde ©2012Comp 4501

How Do I Draw a Pixel?How Do I Draw a Pixel?

Shadows volumes: Used in Doom (Expensive)

From lights, build a shadow shape for occluders.

If it’s OUTSIDE a shadow shape, draw lit.If it’s INSIDE a shadow shape, draw dark.

Shadow maps: Most popular Technique

Build a z-buffer texture for what light can see.If light CAN SEE it (at z-buffer), draw normally.If light CAN’T SEE it (behind z-buffer), draw dark

Internally nested shadow shapes possibleInternally nested shadow shapes possible

Occluders don’t need to be known…Occluders don’t need to be known…

Page 3: Wilf LaLonde ©2012 Comp 4501 95.4501 Shadows. Wilf LaLonde ©2012 Comp 4501 How Do I Draw a Pixel? Shadows volumes: Used in Doom (Expensive) From lights,

Wilf LaLonde ©2012Comp 4501

95.450195.4501

Shadow Volumes

Page 4: Wilf LaLonde ©2012 Comp 4501 95.4501 Shadows. Wilf LaLonde ©2012 Comp 4501 How Do I Draw a Pixel? Shadows volumes: Used in Doom (Expensive) From lights,

Wilf LaLonde ©2012Comp 4501

Seeing Nested Shadow VolumesSeeing Nested Shadow Volumes

inside green and red volumes

inside green volume only

inside NO volumes

Look at Demo

Page 5: Wilf LaLonde ©2012 Comp 4501 95.4501 Shadows. Wilf LaLonde ©2012 Comp 4501 How Do I Draw a Pixel? Shadows volumes: Used in Doom (Expensive) From lights,

Wilf LaLonde ©2012Comp 4501

Basic IdeaBasic Idea

• Clear the stencil buffer (a pixel buffer of counters)and turn color buffer off (so it really doesn’t draw).

• Extract the shadow caster’s silhouette generated by the light and draw the volume to infinity.

• As each triangle is drawn, in stencil buffer

Increment front facing ones (if pixel drawn)

Decrement back facing ones (if pixel drawn)• When done,

if stencil counter = 0, it’s NOT in shadowif stencil counter = n, it’s in n shadows…

Page 6: Wilf LaLonde ©2012 Comp 4501 95.4501 Shadows. Wilf LaLonde ©2012 Comp 4501 How Do I Draw a Pixel? Shadows volumes: Used in Doom (Expensive) From lights,

Wilf LaLonde ©2012Comp 4501

Counting Via Stencil BufferCounting Via Stencil Buffer

4 Shadow casters

Camera

zero

Suppose drawing order is 1 2 3

1 +1, -1 Both faces in front of

2 +1, 0 One face in front of

3 +1, 0 One face in front of

Final result +2 for

4

4 0, 0 Both faces behind

Final result 0 for

Page 7: Wilf LaLonde ©2012 Comp 4501 95.4501 Shadows. Wilf LaLonde ©2012 Comp 4501 How Do I Draw a Pixel? Shadows volumes: Used in Doom (Expensive) From lights,

Wilf LaLonde ©2012Comp 4501

95.450195.4501

Shadow Maps

Page 8: Wilf LaLonde ©2012 Comp 4501 95.4501 Shadows. Wilf LaLonde ©2012 Comp 4501 How Do I Draw a Pixel? Shadows volumes: Used in Doom (Expensive) From lights,

Wilf LaLonde ©2012Comp 4501

Shadows Maps: Steps Involved Per LightShadows Maps: Steps Involved Per Light

• Move the camera to the light’s viewpoint. • Draw the world with the color buffer off.• Grab the z-buffer as a texture (need frame

buffers to do this),• Move the camera back• Draw the world normally using a shader

that draws dark when in shade; i.e. dark when pixel’s z in light space > the light z-buffer’s z.

Works best for spot light (since need location and orientation)

Works best for spot light (since need location and orientation)

Page 9: Wilf LaLonde ©2012 Comp 4501 95.4501 Shadows. Wilf LaLonde ©2012 Comp 4501 How Do I Draw a Pixel? Shadows volumes: Used in Doom (Expensive) From lights,

Wilf LaLonde ©2012Comp 4501

Shadow MapsShadow Maps

• From an ATI Demo…. (draw world from light’s position as a camera TO GET light’s z-buffer)

L

Visible (in front of light’s z-buffer)

Not visible (behind light’s z-buffer)

Look at Demo

Page 10: Wilf LaLonde ©2012 Comp 4501 95.4501 Shadows. Wilf LaLonde ©2012 Comp 4501 How Do I Draw a Pixel? Shadows volumes: Used in Doom (Expensive) From lights,

Wilf LaLonde ©2012Comp 4501

What the Special Shader DoesWhat the Special Shader Does

• Let L place the light in the world and P be the lights’s projection matrix.

• Let shadow map be the light’s z-buffer texture.• Let p be the LOCAL position of the pixel

BEFORE transforming to light coordinates.

q = p*(LP) //pixel in light coordinatesq /= q.w; //Because of P, q.w is NOT 1.

depthSeenByLight = shadow map at (q.x, q.y)

if (q.z > depthSeenByLight ) draw dark

else draw normally

Page 11: Wilf LaLonde ©2012 Comp 4501 95.4501 Shadows. Wilf LaLonde ©2012 Comp 4501 How Do I Draw a Pixel? Shadows volumes: Used in Doom (Expensive) From lights,

Wilf LaLonde ©2012Comp 4501

Detailed TopicDetailed Topic

Shadow Maps

(Most popular approach)

Page 12: Wilf LaLonde ©2012 Comp 4501 95.4501 Shadows. Wilf LaLonde ©2012 Comp 4501 How Do I Draw a Pixel? Shadows volumes: Used in Doom (Expensive) From lights,

Wilf LaLonde ©2012Comp 4501

VariationsVariations

Percentage Closer Filtering

Variance Shadow Maps

Cascaded Shadow MapsHierarchical Shadow Maps

Volumetric Shadow Maps

Exponential Shadow Maps

Convolution Shadow Maps

Forward Shadow Maps

Tiled Shadow Maps

Perspective Shadow Maps

Light Space Perspective Shadow Maps

Midpoint Shadow Maps

Omnidirectional Shadow Maps

Page 13: Wilf LaLonde ©2012 Comp 4501 95.4501 Shadows. Wilf LaLonde ©2012 Comp 4501 How Do I Draw a Pixel? Shadows volumes: Used in Doom (Expensive) From lights,

Wilf LaLonde ©2012Comp 4501

Shadow MappingShadow Mapping

From “Real-time shadows” Book, Eisemann et al, CRC Press, 2012, page 33

From “Real-time shadows” Book, Eisemann et al, CRC Press, 2012, page 33

PDF available too

Page 14: Wilf LaLonde ©2012 Comp 4501 95.4501 Shadows. Wilf LaLonde ©2012 Comp 4501 How Do I Draw a Pixel? Shadows volumes: Used in Doom (Expensive) From lights,

Wilf LaLonde ©2012Comp 4501

• [pLPSx, pLPS

y] is the clip space position of the pixel in the depth map where the pixel would project when seen by the light.

• pLPSz is the distance from the pixel to the light.

Given pixel position pLPS (after dividing by w) in

light projection space (also called clip space)

Given pixel position pLPS (after dividing by w) in

light projection space (also called clip space)

pLPSz > texture at [pLPS

x, pLPSy] the pixel is hidden

Hidden in shadow Hidden in shadow

not normally accessed via clip coordinates depth map

Page 15: Wilf LaLonde ©2012 Comp 4501 95.4501 Shadows. Wilf LaLonde ©2012 Comp 4501 How Do I Draw a Pixel? Shadows volumes: Used in Doom (Expensive) From lights,

Wilf LaLonde ©2012Comp 4501

• Clip coordinates c range from [-1,+1].• Texture coordinates t range from [0,1].

Transform via t = c * 0.5 + 0.5

Clip to Texture CoordinatesClip to Texture Coordinates

Why? Consider c ranges from -1 to +1 c*0.5 ranges from -0.5 to +0.5c*0.5+0.5 ranges from 0 to 1.0

Page 16: Wilf LaLonde ©2012 Comp 4501 95.4501 Shadows. Wilf LaLonde ©2012 Comp 4501 How Do I Draw a Pixel? Shadows volumes: Used in Doom (Expensive) From lights,

Wilf LaLonde ©2012Comp 4501

• Magnification artifacts: occur when projected shadow-map texels cover a large area in screen space.

jaggies or aliasing• Minification artifacts: occur when several

shadow map texels are mapped to the same screen-space pixel.

shadow acne or z-fighting (shadows where none should be)

Problems with Shadow MapsProblems with Shadow Maps

How can you both magnify and shrink with a single transformation to perform the mapping?How can you both magnify and shrink with a

single transformation to perform the mapping?

Page 17: Wilf LaLonde ©2012 Comp 4501 95.4501 Shadows. Wilf LaLonde ©2012 Comp 4501 How Do I Draw a Pixel? Shadows volumes: Used in Doom (Expensive) From lights,

Wilf LaLonde ©2012Comp 4501

An example of AliasingAn example of Aliasing

Page 18: Wilf LaLonde ©2012 Comp 4501 95.4501 Shadows. Wilf LaLonde ©2012 Comp 4501 How Do I Draw a Pixel? Shadows volumes: Used in Doom (Expensive) From lights,

Wilf LaLonde ©2012Comp 4501

An Example of AcneAn Example of Acne

Page 19: Wilf LaLonde ©2012 Comp 4501 95.4501 Shadows. Wilf LaLonde ©2012 Comp 4501 How Do I Draw a Pixel? Shadows volumes: Used in Doom (Expensive) From lights,

Wilf LaLonde ©2012Comp 4501

• Reduce areas of extreme mag- and mini-fications (increase resolution or change the engine architecture)

Perspective shadow maps => warp theshadow space to the view space

Cascaded shadow maps => use different shadow maps in different parts of the frustum.

• Remove sampling errors (changes to the shaders)

Filtering

SolutionsSolutions

Page 20: Wilf LaLonde ©2012 Comp 4501 95.4501 Shadows. Wilf LaLonde ©2012 Comp 4501 How Do I Draw a Pixel? Shadows volumes: Used in Doom (Expensive) From lights,

Wilf LaLonde ©2012Comp 4501

• Invented because bilinear, trilinear and anisotropic filtering; i,e., hardware texture filtering is INAPPLICABLE to standard shadows maps.

Hardware filtering example: an average of depths d1 and d2

Type of filtering needed:

an average of “in shadow at d1”and “not in shadow at d2”

Percentage-Closer FilteringPercentage-Closer Filtering

Key insight: Correct filtering needs to filter the results of the depth comparisons, not the depthsKey insight: Correct filtering needs to filter the

results of the depth comparisons, not the depths

Page 21: Wilf LaLonde ©2012 Comp 4501 95.4501 Shadows. Wilf LaLonde ©2012 Comp 4501 How Do I Draw a Pixel? Shadows volumes: Used in Doom (Expensive) From lights,

Wilf LaLonde ©2012Comp 4501

95.450195.4501

Where Do We Start

Page 22: Wilf LaLonde ©2012 Comp 4501 95.4501 Shadows. Wilf LaLonde ©2012 Comp 4501 How Do I Draw a Pixel? Shadows volumes: Used in Doom (Expensive) From lights,

Wilf LaLonde ©2012Comp 4501

• The June 2010 DX SDK has a good DirectX9 demo called “ShadowMap” from 2004.

• Made a copy of the demo in “C:\School\4501.2012\From June 2010 DX SDK – Wilf”.

• Made changes to the software (camera + trackball) to use a RIGHT-HANDED SYSTEM; e.g.,

D3DXMatrixLookAtRH (instead of LH)

D3DXVECTOR3 vLookatPt = D3DXVECTOR3( 0.0f, 0.0f, -1.0f ); //Not +1 for

camera• Add logging facility: clearLog () and void log (char *message, ...)

so we can say; e.g., ::log (“\nI am %d years old.”, 23);

Where Do We StartWhere Do We Start

Search “wilf” for a list of changes.

Page 23: Wilf LaLonde ©2012 Comp 4501 95.4501 Shadows. Wilf LaLonde ©2012 Comp 4501 How Do I Draw a Pixel? Shadows volumes: Used in Doom (Expensive) From lights,

Wilf LaLonde ©2012Comp 4501

• A 1 channel 32F texture “g_pShadowMap” is obtained to contain the depthBuffer data.

PreliminariesPreliminaries

pd3dDevice->CreateTexture (|SHADOWMAP_SIZE, SHADOWMAP_SIZE, 1, D3DUSAGE_RENDERTARGET, D3DFMT_R32F, D3DPOOL_DEFAULT,&g_pShadowMap, NULL);

pd3dDevice->CreateDepthStencilSurface (SHADOWMAP_SIZE, SHADOWMAP_SIZE, d3dSettings.d3d9.pp.AutoDepthStencilFormat,D3DMULTISAMPLE_NONE, 0, TRUE,&g_pDSShadow, NULL);

• A frame buffer “g_pDSShadow” is obtained to draw into the depthBuffer into.

See callback OnResetDevice for details

window size is 640 x 480SHADOWMAP_SIZE is 1024

Actually used as a DRAW texture rather than a DEPTH-BUFFER texture

Page 24: Wilf LaLonde ©2012 Comp 4501 95.4501 Shadows. Wilf LaLonde ©2012 Comp 4501 How Do I Draw a Pixel? Shadows volumes: Used in Doom (Expensive) From lights,

Wilf LaLonde ©2012Comp 4501

Rendering Performs 2 PassesRendering Performs 2 Passes

void CALLBACK OnFrameRender (IDirect3DDevice9* pd3dDevice, double fTime, float fElapsedTime, void* pUserContext) {

//Draw the scene as seen by the light

//Draw the scene as seen by the camera.

}

See later slide

See later slide

Page 25: Wilf LaLonde ©2012 Comp 4501 95.4501 Shadows. Wilf LaLonde ©2012 Comp 4501 How Do I Draw a Pixel? Shadows volumes: Used in Doom (Expensive) From lights,

Wilf LaLonde ©2012Comp 4501

Draw the scene seen by the light (without error checks)Draw the scene seen by the light (without error checks)

LPDIRECT3DSURFACE9 pOldRT = NULL;

pd3dDevice->GetRenderTarget (0, &pOldRT);

LPDIRECT3DSURFACE9 pShadowSurf;

g_pShadowMap->GetSurfaceLevel (0, &pShadowSurf);

pd3dDevice->SetRenderTarget (0, pShadowSurf);

SAFE_RELEASE (pShadowSurf);

LPDIRECT3DSURFACE9 pOldDS = NULL;

pd3dDevice->GetDepthStencilSurface (&pOldDS);

pd3dDevice->SetDepthStencilSurface (g_pDSShadow);

pd3dDevice->SetDepthStencilSurface (pOldDS);

pOldDS->Release ();

pd3dDevice->SetRenderTarget (0, pOldRT);

SAFE_RELEASE (pOldRT);

Get old drawing target (back buffer)and replace by the shadow map

texture. AFTER, restore it

Get old frame buffer (full screen)and replace by the shadow map frame buffer. AFTER, restore it

RenderScene (pd3dDevice, true, fElapsedTime, &mLightView, &g_mShadowProj);

bRenderShadow

Page 26: Wilf LaLonde ©2012 Comp 4501 95.4501 Shadows. Wilf LaLonde ©2012 Comp 4501 How Do I Draw a Pixel? Shadows volumes: Used in Doom (Expensive) From lights,

Wilf LaLonde ©2012Comp 4501

Draw the scene seen by the camera (without error checks)Draw the scene seen by the camera (without error checks)

//Setup 2 shader parameters…

g_pEffect->SetTexture ("g_txShadow", g_pShadowMap);

g_pEffect->SetMatrix ("g_mViewToLightProj", &...); // CMV-1 * LMV * LP

g_pEffect->SetTexture ("g_txShadow", NULL);

RenderScene (pd3dDevice, false, fElapsedTime, g_VCamera.GetViewMatrix(), g_VCamera.GetProjMatrix ());

bRenderShadow

in camera’s model view space

in model space

in light’s model view space

in light’s clip space

code on next slide

Page 27: Wilf LaLonde ©2012 Comp 4501 95.4501 Shadows. Wilf LaLonde ©2012 Comp 4501 How Do I Draw a Pixel? Shadows volumes: Used in Doom (Expensive) From lights,

Wilf LaLonde ©2012Comp 4501

Camera model view TO light clip spaceCamera model view TO light clip space

// CMV-1 * LMV * LP

//Compute the matrix to transform from view space to light projection space. This //consists of the inverse of view matrix * view matrix of light * light projection matrix

D3DXMATRIXA16 mViewToLightProj;

mViewToLightProj = *pmView;

D3DXMatrixInverse (&mViewToLightProj, NULL, &mViewToLightProj );

D3DXMatrixMultiply (&mViewToLightProj, &mViewToLightProj, &mLightView );

D3DXMatrixMultiply (&mViewToLightProj, &mViewToLightProj, &g_mShadowProj );

V (g_pEffect->SetMatrix ("g_mViewToLightProj", &mViewToLightProj ))

in camera’s model view space

in model space

in light’s model view space

in light’s clip space

code on next slide

CMV

CMV-1

LMV

LP

Easier to understand if you call it M.

Page 28: Wilf LaLonde ©2012 Comp 4501 95.4501 Shadows. Wilf LaLonde ©2012 Comp 4501 How Do I Draw a Pixel? Shadows volumes: Used in Doom (Expensive) From lights,

Wilf LaLonde ©2012Comp 4501

• We duplicated it as shadowMapOriginal.fx”.• It contains 3 techniques (3 shaders):

RenderShadow for drawing the scene from the point of view of the light (pass 1).

RenderScene for drawing the lit scene with shadows (pass 2)

RenderLight for drawing the “spotlight mesh (looks like a lamp shade)” (this is irrelevant and part of pass 1).

Uses 1 shader file “shadowMap.fx”Uses 1 shader file “shadowMap.fx”

Page 29: Wilf LaLonde ©2012 Comp 4501 95.4501 Shadows. Wilf LaLonde ©2012 Comp 4501 How Do I Draw a Pixel? Shadows volumes: Used in Doom (Expensive) From lights,

Wilf LaLonde ©2012Comp 4501

void RenderScene (IDirect3DDevice9* pd3dDevice, bool bRenderShadow, float fElapsedTime, const D3DXMATRIX* pmView, const D3DXMATRIX* pmProj) {//Setup shader variables g_mProj, g_vLightPos, g_vLightDir…

//Clear the frame buffer.pd3dDevice->Clear (0L, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER,

0x000000ff, 1.0f, 0L ) );//Choose whether drawing from perspective of light or camera.g_pEffect->SetTechnique (bRenderShadow ? "RenderShadow" : "RenderScene";

//Draw the scene…pd3dDevice->BeginScene ();

//Draw the scene objects.

//Not shown: if not rendering shadow, code for rendering "lamp shade object", // text, and User interface stuff.

pd3dDevice->EndScene ();

The RenderScene RoutineThe RenderScene Routine

See later slide

See later slide

Page 30: Wilf LaLonde ©2012 Comp 4501 95.4501 Shadows. Wilf LaLonde ©2012 Comp 4501 How Do I Draw a Pixel? Shadows volumes: Used in Doom (Expensive) From lights,

Wilf LaLonde ©2012Comp 4501

• The light is actually a “CFirstPersonCamera”.D3DXMATRIX * GetWorldMatrix () {return &m_mCameraWorld;}const D3DXVECTOR3* GetWorldAhead () const {//toLight (via back)

return (D3DXVECTOR3 *) &m_mCameraWorld._31;}

Do We Get “toLight” or “fromLight”?Do We Get “toLight” or “fromLight”?

[_31,_32,_33] = [0,0,1] *_11 _12 _13_21 _22 _23_31 _32 _33

[0,0,1] is back in right-handed system

[0,0,-1] is ahead in right-handed system

fromLight would actually be [0,0,-1] transformed

toLight would actually be [0,0,1] transformed

I didn’t want to change the code to avoid breaking their stuff… We get a toLight…

Page 31: Wilf LaLonde ©2012 Comp 4501 95.4501 Shadows. Wilf LaLonde ©2012 Comp 4501 How Do I Draw a Pixel? Shadows volumes: Used in Doom (Expensive) From lights,

Wilf LaLonde ©2012Comp 4501

//Setup the projection matrix... g_pEffect->SetMatrix ("g_mProj", pmProj); //Parameter that came in…

//Setup the light position (in camera or view space)... D3DXVECTOR3 v = *g_LCamera.GetEyePt (); D3DXVECTOR4 v4; D3DXVec3Transform (&v4, &v, pmView); //Transform to CS. g_pEffect->SetVector ("g_vLightPos", &v4);

//Setup the light direction (in camera or view space)... *(D3DXVECTOR3 *) &v4 = *g_LCamera.GetWorldAhead(); //toLight v4.w = 0.0f; //Set w to 0 so that the translation part doesn't come to play. D3DXVec4Transform (&v4, &v4, pmView); //Transform toLight to CS D3DXVec3Normalize ((D3DXVECTOR3 *) &v4, (D3DXVECTOR3 *) &v4); g_pEffect->SetVector ("g_vLightDir", &v4);

Setup shader variables g_mProj, g_vLightPos, g_vLightDir…Setup shader variables g_mProj, g_vLightPos, g_vLightDir…

the light

Page 32: Wilf LaLonde ©2012 Comp 4501 95.4501 Shadows. Wilf LaLonde ©2012 Comp 4501 How Do I Draw a Pixel? Shadows volumes: Used in Doom (Expensive) From lights,

Wilf LaLonde ©2012Comp 4501

//Render the objects in the scene..for (int obj = 0; obj < NUM_OBJ; ++obj) {

D3DXMATRIXA16 mWorldView = g_Obj[obj].m_mWorld;D3DXMatrixMultiply (&mWorldView, &mWorldView, pmView);g_pEffect->SetMatrix ("g_mWorldView", &mWorldView);

LPD3DXMESH pMesh = g_Obj[obj].m_Mesh.GetMesh();UINT cPass; g_pEffect->Begin (&cPass, 0); //It will return 1.

for (UINT p = 0; p < cPass; ++p ) {g_pEffect->BeginPass (p);

for (DWORD i = 0; i < g_Obj[obj].m_Mesh.m_dwNumMaterials; ++i) {

D3DXVECTOR4 vDif (

g_Obj[obj].m_Mesh.m_pMaterials[i].Diffuse.r,

g_Obj[obj].m_Mesh.m_pMaterials[i].Diffuse.g,

g_Obj[obj].m_Mesh.m_pMaterials[i].Diffuse.b,

g_Obj[obj].m_Mesh.m_pMaterials[i].Diffuse.a);g_pEffect->SetVector ("g_vMaterial", &vDif);if (g_Obj[obj].m_Mesh.m_pTextures[i] )

g_pEffect->SetTexture ("g_txScene", g_Obj[obj].m_Mesh.m_pTextures[i];

elseg_pEffect->SetTexture

("g_txScene", g_pTexDef ) ) //It's NULL.g_pEffect->CommitChanges (); pMesh-

>DrawSubset (i);}

g_pEffect->EndPass ();}

g_pEffect->End ();}

Draw the Scene ObjectsDraw the Scene Objects

Page 33: Wilf LaLonde ©2012 Comp 4501 95.4501 Shadows. Wilf LaLonde ©2012 Comp 4501 How Do I Draw a Pixel? Shadows volumes: Used in Doom (Expensive) From lights,

Wilf LaLonde ©2012Comp 4501

• We have MOST of the “.cpp” code worked out.

• We need to look at the shader (RenderShadow) for drawing the scene from the light’s point of view (this is RELATIVELY simple).

• We need to develop the shader (RenderScene) for drawing the scene from the camera’s point of view (this is COMPLEX; we will develop several variations)…

Where Are We?Where Are We?

Page 34: Wilf LaLonde ©2012 Comp 4501 95.4501 Shadows. Wilf LaLonde ©2012 Comp 4501 How Do I Draw a Pixel? Shadows volumes: Used in Doom (Expensive) From lights,

Wilf LaLonde ©2012Comp 4501

//Copyright (c) Microsoft Corporation. All rights reserved.#define SMAP_SIZE 512#define SHADOW_EPSILON 0.00005f

float4x4 g_mWorldView;float4x4 g_mProj;float4x4 g_mViewToLightProj; //Transform from view to light projection spacefloat4 g_vMaterial;texture g_txScene;texture g_txShadow;

float3 g_vLightPos; //Light position in view space (wilf: lightPositionCS)

float3 g_vLightDir; //Light direction in view space (wilf: toLightCS)

float4 g_vLightDiffuse = float4 ( 1.0f, 1.0f, 1.0f, 1.0f ); //Light diffuse colorfloat4 g_vLightAmbient = float4 ( 0.3f, 0.3f, 0.3f, 1.0f ); //Use an ambient light of 0.3float g_fCosTheta; //Cosine of theta of the spot light (wilf: coneAngleAsCosine)

sampler2D g_samScene = sampler_state {Texture = <g_txScene>; …};sampler2D g_samShadow = sampler_state {Texture = <g_txShadow>; …}

The Effect File “ShadowMap.fx”The Effect File “ShadowMap.fx”

Page 35: Wilf LaLonde ©2012 Comp 4501 95.4501 Shadows. Wilf LaLonde ©2012 Comp 4501 How Do I Draw a Pixel? Shadows volumes: Used in Doom (Expensive) From lights,

Wilf LaLonde ©2012Comp 4501

void VertShadow (float4 Pos : POSITION, float3 Normal : NORMAL,out float4 oPos : POSITION, out float2 Depth : TEXCOORD0) {…

}

void PixShadow (float2 Depth : TEXCOORD0, out float4 Color : COLOR) {…

}…

technique RenderShadow { pass p0 { VertexShader = compile vs_2_0 VertShadow(); PixelShader = compile ps_2_0 PixShadow(); }}

The Effect File “ShadowMap.fx” (continued)The Effect File “ShadowMap.fx” (continued)

Page 36: Wilf LaLonde ©2012 Comp 4501 95.4501 Shadows. Wilf LaLonde ©2012 Comp 4501 How Do I Draw a Pixel? Shadows volumes: Used in Doom (Expensive) From lights,

Wilf LaLonde ©2012Comp 4501

void VertShadow (float4 Pos : POSITION, float3 Normal : NORMAL,out float4 oPos : POSITION, out float2 Depth : TEXCOORD0) {//Convert to light clip coordinates (light projection space); oPos = mul (Pos, g_mWorldView); //Pos * g_mWorldView (so far)oPos = mul (oPos, g_mProj); //Pos * g_mWorldView * g_mProj

//Store z and w in our spare texcoord…Depth.xy = oPos.zw;

}

void PixShadow (float2 Depth : TEXCOORD0, out float4 Color : COLOR) {//Compute depth which is z / wColor = Depth.x / Depth.y; //All 4 components rgba get the same value.

}

The RenderShadow Shader DetailsThe RenderShadow Shader Details

All it does is draw the depth AS IF IT WERE A COLOR.

Page 37: Wilf LaLonde ©2012 Comp 4501 95.4501 Shadows. Wilf LaLonde ©2012 Comp 4501 How Do I Draw a Pixel? Shadows volumes: Used in Doom (Expensive) From lights,

Wilf LaLonde ©2012Comp 4501

95.450195.4501

Developing The

Scene Drawing

Shader That

Provides Shadows

Page 38: Wilf LaLonde ©2012 Comp 4501 95.4501 Shadows. Wilf LaLonde ©2012 Comp 4501 How Do I Draw a Pixel? Shadows volumes: Used in Doom (Expensive) From lights,

Wilf LaLonde ©2012Comp 4501

• We want to perform major experiments via minor changes to the original shader...

• Structure of original “ShadowMap.tx”

Instrumenting the Original ShaderInstrumenting the Original Shader

Shader variables

Shader VertScene, PixScene //Use by technique RenderScene Shader VertShadow, PixShadow //Used by technique RenderShadow

technique RenderScene {

pass p0 {VertexShader = compile vs_2_0 VertScene ();PixelShader = compile ps_2_0 PixScene ();

}

}

Page 39: Wilf LaLonde ©2012 Comp 4501 95.4501 Shadows. Wilf LaLonde ©2012 Comp 4501 How Do I Draw a Pixel? Shadows volumes: Used in Doom (Expensive) From lights,

Wilf LaLonde ©2012Comp 4501

New Shader StructureNew Shader Structure

#include “WilfShadowExperiment.choice”

Shader variables

#include “WilfShadowLibrary.all”#include “WilfShadowRenderer.shader”

Shader VertScene, PixSceneShader VertShadow, PixShadow

technique RenderScene {

pass p0 {#if (experiment == ORIGINAL_VERSION)

VertexShader = compile vs_2_0 VertScene ();PixelShader = compile ps_2_0 PixScene ();

#elseVertexShader = compile vs_2_0 WilfsDrawSceneVS ();PixelShader = compile ps_2_0 WilfsDrawScenePS ();

#endif //experiment

}

}

Page 40: Wilf LaLonde ©2012 Comp 4501 95.4501 Shadows. Wilf LaLonde ©2012 Comp 4501 How Do I Draw a Pixel? Shadows volumes: Used in Doom (Expensive) From lights,

Wilf LaLonde ©2012Comp 4501

• WilfShadowExperiment.choice

• WilfShadowLibrary.all

Preliminary SetupPreliminary Setup

#define ORIGINAL_VERSION 0

#define experiment ORIGINAL_VERSION

#define clamp01 saturate

//Define the ambient/diffuse/specular specifics for this application....

float4 Ka () {return g_vMaterial * g_vLightAmbient;}

float4 Kd () {return g_vMaterial * (1 - g_vLightAmbient);}

float4 Ks () {return float4 (0,0,0,0);}

float g_fSpecularExponent = 60.0f;

//Functions from lighting for dummies; i.e. inSpotLight, combinedColor...

//Create 2 variations of combinedColor : 1 without specular color; 1 unlit.

NOT SHOW

N

ALL NON-AMBIENT

Page 41: Wilf LaLonde ©2012 Comp 4501 95.4501 Shadows. Wilf LaLonde ©2012 Comp 4501 How Do I Draw a Pixel? Shadows volumes: Used in Doom (Expensive) From lights,

Wilf LaLonde ©2012Comp 4501

• WilfShadowRenderer.shader switched to using structs instead of parameter lists...

Preliminary SetupPreliminary Setup

//Notation for spaces: CS camera, CPS camera projection, LPS light projection.

struct VS_INPUT {float4 position : POSITION;float3 normal : NORMAL;float2 textureCoordinate : TEXCOORD0;

};

struct VS_OUTPUT {float4 positionCPS : POSITION; //This disappears from PS_INPUT...float2 textureCoordinate : TEXCOORD0;float4 positionCS : TEXCOORD1;float3 normalCS : TEXCOORD2;float4 positionLPS : TEXCOORD3;

};

VS_OUTPUT WilfsDrawSceneVS (VS_INPUT In) {VS_OUTPUT Out; Out.positionCS = ...return Out;

}

Page 42: Wilf LaLonde ©2012 Comp 4501 95.4501 Shadows. Wilf LaLonde ©2012 Comp 4501 How Do I Draw a Pixel? Shadows volumes: Used in Doom (Expensive) From lights,

Wilf LaLonde ©2012Comp 4501

• The Vertex Shader...

Preliminary SetupPreliminary Setup

//Notation for spaces: CS camera, CPS camera projection, LPS light projection.struct VS_INPUT {

float4 position : POSITION;float3 normal : NORMAL;float2 textureCoordinate : TEXCOORD0;

};

struct VS_OUTPUT {float4 positionCPS : POSITION; //This disappears from PS_INPUT...float2 textureCoordinate : TEXCOORD0;float4 positionCS : TEXCOORD1;float3 normalCS : TEXCOORD2;float4 positionLPS : TEXCOORD3;

};

VS_OUTPUT WilfsDrawSceneVS (VS_INPUT In) {VS_OUTPUT Out;Out.positionCS = mul (In.position, g_mWorldView); Out.normalCS = mul (In.normal, (float3x3) g_mWorldView); Out.textureCoordinate = In.textureCoordinate; //Propagate.Out.positionCPS = mul (Out.positionCS, g_mProj); Out.positionLPS = mul (Out.positionCS, g_mViewToLightProj);return Out;

}

Not clear why we compute

all these variables yet,

Page 43: Wilf LaLonde ©2012 Comp 4501 95.4501 Shadows. Wilf LaLonde ©2012 Comp 4501 How Do I Draw a Pixel? Shadows volumes: Used in Doom (Expensive) From lights,

Wilf LaLonde ©2012Comp 4501

• The Pixel Shader...

Preliminary SetupPreliminary Setup

//Notation for spaces: CS camera, CPS camera projection, LPS light projection.struct VS_OUTPUT {

float4 positionCPS : POSITION; //This disappears from PS_INPUT...float2 textureCoordinate : TEXCOORD0;float4 positionCS : TEXCOORD1;float3 normalCS : TEXCOORD2;float4 positionLPS : TEXCOORD3;

};

struct PS_INPUT {float2 textureCoordinate : TEXCOORD0;float4 positionCS : TEXCOORD1;float3 normalCS : TEXCOORD2;float4 positionLPS : TEXCOORD3;

};

float4 WilfsDrawScenePS (PS_INPUT In) : COLOR0 {#if (experiment == drawRed)

return float4 (1,0,0,1);#endif //experiment

}

positionCPS is gone

Page 44: Wilf LaLonde ©2012 Comp 4501 95.4501 Shadows. Wilf LaLonde ©2012 Comp 4501 How Do I Draw a Pixel? Shadows volumes: Used in Doom (Expensive) From lights,

Wilf LaLonde ©2012Comp 4501

95.450195.4501

Experiments

Page 45: Wilf LaLonde ©2012 Comp 4501 95.4501 Shadows. Wilf LaLonde ©2012 Comp 4501 How Do I Draw a Pixel? Shadows volumes: Used in Doom (Expensive) From lights,

Wilf LaLonde ©2012Comp 4501

• WilfShadowExperiment.choice

• Yup... It draws a screen full of red....

Lets Perform Experiment 1Lets Perform Experiment 1

#define ORIGINAL_VERSION 0#define drawRed 1

//#define experiment ORIGINAL_VERSION

#define experiment drawRed

Page 46: Wilf LaLonde ©2012 Comp 4501 95.4501 Shadows. Wilf LaLonde ©2012 Comp 4501 How Do I Draw a Pixel? Shadows volumes: Used in Doom (Expensive) From lights,

Wilf LaLonde ©2012Comp 4501

• WilfShadowExperiment.choice

• And the pixel shader....

Lets Perform Experiment 2Lets Perform Experiment 2

#define drawBrightTexture 2

#define experiment drawBrightTexture

float4 WilfsDrawScenePS (PS_INPUT In) : COLOR0 {#elif (experiment == drawBrightTexture)

return tex2D (g_samScene, In.textureCoordinate);#endif //experiment

}

Page 47: Wilf LaLonde ©2012 Comp 4501 95.4501 Shadows. Wilf LaLonde ©2012 Comp 4501 How Do I Draw a Pixel? Shadows volumes: Used in Doom (Expensive) From lights,

Wilf LaLonde ©2012Comp 4501

• WilfShadowExperiment.choice

• And the pixel shader....

Lets Perform Experiment 3Lets Perform Experiment 3

#define drawDarkTexture 3

#define experiment drawDarkTexture

float4 WilfsDrawScenePS (PS_INPUT In) : COLOR0 {#elif (experiment == drawDarkTexture)

return tex2D (g_samScene, In.textureCoordinate) * (Ka ());#endif //experiment

}

Page 48: Wilf LaLonde ©2012 Comp 4501 95.4501 Shadows. Wilf LaLonde ©2012 Comp 4501 How Do I Draw a Pixel? Shadows volumes: Used in Doom (Expensive) From lights,

Wilf LaLonde ©2012Comp 4501

• WilfShadowExperiment.choice

• Two issues to deal with if we are to be LIT.• Are we in the cone of the light?• If so, are we facing the light?

Lets Perform Experiment 4Lets Perform Experiment 4

#define drawLitTextureIgnoringShadows 4

#define experiment drawLitTextureIgnoringShadows

TWO SEPARATE SLIDES

Page 49: Wilf LaLonde ©2012 Comp 4501 95.4501 Shadows. Wilf LaLonde ©2012 Comp 4501 How Do I Draw a Pixel? Shadows volumes: Used in Doom (Expensive) From lights,

Wilf LaLonde ©2012Comp 4501

• Shader global variables and parameters

• Existing Spotlight Routine

• Easy to Compute First Parameter...

What Do We Have To Work With LIGHTS IN CAMERA SPACE?What Do We Have To Work With LIGHTS IN CAMERA SPACE?

//We have the following renamed globals.float3 lightPositionCS;float3 toLightCS;float coneAngleAsCosine;

float3 pixelToLightCS = normalize (lightPositionCS - In.positionCS.xyz);

bool inSpotLight (float3 normalizedPixelToLight, float3 normalizedToLightVector, float coneAngleAsCosine) {//Negating each of the above unit directions A and B makes them point away from the light. //Note: -A.-B = A.B = |A||B| cos angle = cos angle. cos 45° = 0.707, cos 0° = 1 return dot (normalizedPixelToLight , normalizedToLightVector ) > coneAngleAsCosine;

}

struct PS_INPUT {float2 textureCoordinate : TEXCOORD0;float4 positionCS : TEXCOORD1;float3 normalCS : TEXCOORD2;float4 positionLPS : TEXCOORD3;

};

Just make sure all parameters are in CS?

Page 50: Wilf LaLonde ©2012 Comp 4501 95.4501 Shadows. Wilf LaLonde ©2012 Comp 4501 How Do I Draw a Pixel? Shadows volumes: Used in Doom (Expensive) From lights,

Wilf LaLonde ©2012Comp 4501

• Utility Routines so we can ask “Is the pixel normal aligned with the pixelToLight vector”...

• And finally

Utility Routines Utility Routines

bool alignedWith (float3 a, float3 b) {//Vectors pointing in the same direction.//Since A.B = |A||B|cos angle and cos (0 to 90 degrees) is positive; otherwise negative.//In degrees, cos (0) = 1, cos (45) = 0.707, cos (90) = 0, cos (135) = -0.707, cos (180) = -1.

return dot (a, b) > 0.0;}

bool alignedAgainst (float3 a, float3 b) {return !alignedWith (a, b);}

Are We Facing the Light?

bool canSeeLight (ifloat3 pixelToLightCS, float3 pixelNormalCS, float3 normalizedToLightVector, float coneAngleAsCosine) {

//Ignoring blockers, RETURN TRUE if in the spot light's cone angle and facing the light...

return inSpotLight (pixelToLightCS, normalizedToLightVector, coneAngleAsCosine) &&alignedWith (pixelToLightCS, pixelNormalCS);

}

> 0< 0

Page 51: Wilf LaLonde ©2012 Comp 4501 95.4501 Shadows. Wilf LaLonde ©2012 Comp 4501 How Do I Draw a Pixel? Shadows volumes: Used in Doom (Expensive) From lights,

Wilf LaLonde ©2012Comp 4501

• WilfShadowExperiment.choice

• And the pixel shader....

Putting It All TogetherPutting It All Together

#define drawLitTextureIgnoringShadows 4

#define experiment drawLitTextureIgnoringShadows

float4 WilfsDrawScenePS (PS_INPUT In) : COLOR0 {#elif (experiment == drawLitTextureIgnoringShadows)

In.normalCS = normalize (In.normalCS); //Interpolation changes length.float3 pixelToLightCS = normalize (lightPositionCS - In.positionCS.xyz);float shadowBrightness =

canSeeLight (pixelToLightCS, In.normalCS, toLightCS, coneAngleAsCosine)? 1.0 : 0.0;

return combinedColor (tex2D (g_samScene, In.textureCoordinate),In.normalCS, pixelToLightCS, shadowBrightness);

#endif //experiment}

Page 52: Wilf LaLonde ©2012 Comp 4501 95.4501 Shadows. Wilf LaLonde ©2012 Comp 4501 How Do I Draw a Pixel? Shadows volumes: Used in Doom (Expensive) From lights,

Wilf LaLonde ©2012Comp 4501

And it Looks LikeAnd it Looks Like

Page 53: Wilf LaLonde ©2012 Comp 4501 95.4501 Shadows. Wilf LaLonde ©2012 Comp 4501 How Do I Draw a Pixel? Shadows volumes: Used in Doom (Expensive) From lights,

Wilf LaLonde ©2012Comp 4501

• WilfShadowExperiment.choice

Finally, Lets Perform Experiment 5Finally, Lets Perform Experiment 5

#define drawMostBasicLitTextureWithShadows 5

#define experiment drawMostBasicLitTextureWithShadows

float4 WilfsDrawScenePS (PS_INPUT In) : COLOR0 {#elif (experiment == drawMostBasicLitTextureWithShadows)

In.normalCS = normalize (In.normalCS); //Interpolation changes length.float3 pixelToLightCS = normalize (lightPositionCS - In.positionCS.xyz);float shadowBrightness =

canSeeLight (pixelToLightCS, In.normalCS, toLightCS, coneAngleAsCosine) &&

!isInShadow (...)? 1.0: 0.0;

return combinedColor (tex2D (g_samScene, In.textureCoordinate),In.normalCS, pixelToLightCS, shadowBrightness);

#endif //experiment}

Page 54: Wilf LaLonde ©2012 Comp 4501 95.4501 Shadows. Wilf LaLonde ©2012 Comp 4501 How Do I Draw a Pixel? Shadows volumes: Used in Doom (Expensive) From lights,

Wilf LaLonde ©2012Comp 4501

• We have shadow map “g_txShadow” from which we can get

shadow map depth• We have

position [x,y,z,w] in light clip (projection) space

which allows us to getclip space texture coordinates clip space depth

What Do We Have and What Do We Need?What Do We Have and What Do We Need?

struct PS_INPUT {float2 textureCoordinate : TEXCOORD0;float4 positionCS : TEXCOORD1;float3 normalCS : TEXCOORD2;float4 positionLPS : TEXCOORD3;};

in shadow if “clip space depth” > “shadow map depth”

Page 55: Wilf LaLonde ©2012 Comp 4501 95.4501 Shadows. Wilf LaLonde ©2012 Comp 4501 How Do I Draw a Pixel? Shadows volumes: Used in Doom (Expensive) From lights,

Wilf LaLonde ©2012Comp 4501

• Clip coordinates c range from [-1,+1].• Texture coordinates t range from [0,1].

Transform via t = c * 0.5 + 0.5

Recall: Clip to Texture CoordinatesRecall: Clip to Texture Coordinates

Why? Consider c ranges from -1 to +1 c*0.5 ranges from -0.5 to +0.5c*0.5+0.5 ranges from 0 to 1.0

#define usingDirectX true

float2 asShadowMapTextureCoordinates (float4 positionLPS) {

float2 uv = 0.5 * (positionLPS.xy / positionLPS.w) + 0.5;

if (usingDirectX) uv.y = 1.0 - uv.y; //Y goes down...return uv;

}

Page 56: Wilf LaLonde ©2012 Comp 4501 95.4501 Shadows. Wilf LaLonde ©2012 Comp 4501 How Do I Draw a Pixel? Shadows volumes: Used in Doom (Expensive) From lights,

Wilf LaLonde ©2012Comp 4501

Finally, isInShadowFinally, isInShadow

bool isInShadow (sampler2D shadowMap, float4 positionLPS) {//Remember: depth 0 is near, depth 1 is far...

float pixelDepth = positionLPS.z / positionLPS.w;

float2 uv = asShadowMapTextureCoordinates (positionLPS);float mapDepth = tex2D (shadowMap, uv).r;

return pixelDepth > mapDepth; //Pixel is blocked by something...}

Page 57: Wilf LaLonde ©2012 Comp 4501 95.4501 Shadows. Wilf LaLonde ©2012 Comp 4501 How Do I Draw a Pixel? Shadows volumes: Used in Doom (Expensive) From lights,

Wilf LaLonde ©2012Comp 4501

This is Called Surface Acne or Z-FightingThis is Called Surface Acne or Z-Fighting

Page 58: Wilf LaLonde ©2012 Comp 4501 95.4501 Shadows. Wilf LaLonde ©2012 Comp 4501 How Do I Draw a Pixel? Shadows volumes: Used in Doom (Expensive) From lights,

Wilf LaLonde ©2012Comp 4501

Eliminating AcneEliminating Acne

From “Real-time shadows” handouts, precursor to Book, Eisemann et al, CRC Press, 2012, page 34

From “Real-time shadows” handouts, precursor to Book, Eisemann et al, CRC Press, 2012, page 34

Note: For the same pixel in the shadow map, some parts of the scene are above the pixel height

and some are below. FORCE MORE ABOVE WITH A BIAS.

Doesn’t always work.

Page 59: Wilf LaLonde ©2012 Comp 4501 95.4501 Shadows. Wilf LaLonde ©2012 Comp 4501 How Do I Draw a Pixel? Shadows volumes: Used in Doom (Expensive) From lights,

Wilf LaLonde ©2012Comp 4501

• Let d be pixel depth and sd be the depth sampled from the texture; z is perspective space depth...

Which Way Should We Pull Away?Which Way Should We Pull Away?

d sd

The 2 values are rarely equal; suppose equal to 10-128.

Near (z = 0)

Far (z = 1)

Page 60: Wilf LaLonde ©2012 Comp 4501 95.4501 Shadows. Wilf LaLonde ©2012 Comp 4501 How Do I Draw a Pixel? Shadows volumes: Used in Doom (Expensive) From lights,

Wilf LaLonde ©2012Comp 4501

• Wrong Way (EVERY pixel points in shadow)

Which Way Should We Pull Away By EPSILON E?Which Way Should We Pull Away By EPSILON E?

d sd

OR

Esd

Near (z = 0)

Far (z = 1)

sd-E

• Right Way (spread d and sd apart)

d sd

E

d

d-Ed sd

Esd

sd+E

ADD E to bigger number

SUBTRACT E get smaller number

Move d nearer. Move sd further.

Move sd nearer.

Page 61: Wilf LaLonde ©2012 Comp 4501 95.4501 Shadows. Wilf LaLonde ©2012 Comp 4501 How Do I Draw a Pixel? Shadows volumes: Used in Doom (Expensive) From lights,

Wilf LaLonde ©2012Comp 4501

Finally, isInShadow (Experiment 6)Finally, isInShadow (Experiment 6)

#define SHADOW_EPSILON 0.00005

bool isInShadow (sampler2D shadowMap, float4 positionLPS, float epsilon) {//Remember: depth 0 is near, depth 1 is far...

float pixelDepth = positionLPS.z / positionLPS.w;

float2 uv = asShadowMapTextureCoordinates (positionLPS);float mapDepth = tex2D (shadowMap, uv).r + epsilon;

return pixelDepth > mapDepth; //Pixel is blocked by something...}

• WilfShadowExperiment.choice

• And the library

#define drawLitTextureWithBias 6

#define experiment drawLitTextureWithBias

WORKED (No picture supplied)

Page 62: Wilf LaLonde ©2012 Comp 4501 95.4501 Shadows. Wilf LaLonde ©2012 Comp 4501 How Do I Draw a Pixel? Shadows volumes: Used in Doom (Expensive) From lights,

Wilf LaLonde ©2012Comp 4501

But Constant Offset (Bias) Doesn’t Always WorkBut Constant Offset (Bias) Doesn’t Always Work

surface being rendered

surface definedby depth map

(in yellow)

constant offset

inside

gets pulled out to

Page 63: Wilf LaLonde ©2012 Comp 4501 95.4501 Shadows. Wilf LaLonde ©2012 Comp 4501 How Do I Draw a Pixel? Shadows volumes: Used in Doom (Expensive) From lights,

Wilf LaLonde ©2012Comp 4501

But Constant Offset (Bias) Doesn’t Always WorkBut Constant Offset (Bias) Doesn’t Always Work

surface being rendered

surface definedby depth map

(in yellow)

constant offset

(needs to be larger here)

original constant

offset

Page 64: Wilf LaLonde ©2012 Comp 4501 95.4501 Shadows. Wilf LaLonde ©2012 Comp 4501 How Do I Draw a Pixel? Shadows volumes: Used in Doom (Expensive) From lights,

Wilf LaLonde ©2012Comp 4501

Normal Offset Shadows: Daniel HolbertNormal Offset Shadows: Daniel Holbert

• shadowPosition = position + normal * epsilon

constant bias slope-scale normal offset

this is calledpeter-panning

this is calledshadow acne

perfect shadows

See poster

Page 65: Wilf LaLonde ©2012 Comp 4501 95.4501 Shadows. Wilf LaLonde ©2012 Comp 4501 How Do I Draw a Pixel? Shadows volumes: Used in Doom (Expensive) From lights,

Wilf LaLonde ©2012Comp 4501

if (bScaleNormalOffsetByShadowDepth) {float4 Plv = mul (Pw, gLampViewXf);float shadowFOVFactor = max (gLampProjX [0].x, gLampProjX [1].y);shadowMapTexelSize *= abs (Plv.z) * shadowFOVFactor;

}

float4 Plp;float3 ToLight = normalize (gSpotLamp0Pos - Pw);float cosLightAngle = dot (ToLight, WorldSpaceNormal);

float NormalOffsetScale = bNormalOffsetSlopeScale ? saturate (1 - cosLightAngle) : 1.0;NormalOffsetScale *= ShadowlNormalOffset * shadowMapTexelSize;float ShadowOffset = float4 (WorldSpaceNormal.xyz * NormalOffsetScale, 0);if (bOnlyUVNormalOffset) {

Plp = mul (Pw, ShadowViewProjXf); //"P" in light coordinates.float4 shadowPwUVOnly = Pw + ShadowOffset;float4 UVOffsetPlp = mul (shadowPwUVOnly, ShadowViewProjXf); //”P” in light

coor.Plp.xy = UVOffsetPlp.xy;

} else {float4 shadowPW = Pw + ShadowOffset;Plp = mul (shadowPw, ShadowViewProjxf); //"P" in light coordinates.

}

Holbert’s GDC Poster Session CodeHolbert’s GDC Poster Session Code

SOME VARIABLES NOT

INITIALIZED

Page 66: Wilf LaLonde ©2012 Comp 4501 95.4501 Shadows. Wilf LaLonde ©2012 Comp 4501 How Do I Draw a Pixel? Shadows volumes: Used in Doom (Expensive) From lights,

Wilf LaLonde ©2012Comp 4501

float variableLengthShadowEpsilon (float3 normalCS, float4 positionLPS) {//Compute how long to make the normal offset in projection space (0 to

1); //longer if close, smaller if far...//Note: In logarithmic space, close up, z varies fast (use large epsilon); //far away, z varies very little (use small)..float offsetLength = lerp (0.00001, 0.000001, positionLPS.z /

positionLPS.w);

//At grazing angles (along a wall), the normal is almost perpendicular and

//needs a longer offset...float t = dot (normalCS, toLightCS); //t = 0 if perpendicular, 1 if parallel)...offsetLength *= lerp (10.0, 1.0, t); //10 if perpendicular, 1 if parallel...

return offsetLength;}

Using Some of Holbert’s IdeasUsing Some of Holbert’s Ideas

Holbert’s code worked in camera space (demo was in small room; z depth is accurate when near).

By working in projection space, more likely to handle large depths of 1000 meters...

Page 67: Wilf LaLonde ©2012 Comp 4501 95.4501 Shadows. Wilf LaLonde ©2012 Comp 4501 How Do I Draw a Pixel? Shadows volumes: Used in Doom (Expensive) From lights,

Wilf LaLonde ©2012Comp 4501

variable length epsilon

With Fixed Versus Variable LengthWith Fixed Versus Variable Length

epsilon of 0.00001

Essentially the same

The Peter Panning Artifact

Page 68: Wilf LaLonde ©2012 Comp 4501 95.4501 Shadows. Wilf LaLonde ©2012 Comp 4501 How Do I Draw a Pixel? Shadows volumes: Used in Doom (Expensive) From lights,

Wilf LaLonde ©2012Comp 4501

95.450195.4501

Percentage Closer

Filtering (PCF)

Page 69: Wilf LaLonde ©2012 Comp 4501 95.4501 Shadows. Wilf LaLonde ©2012 Comp 4501 How Do I Draw a Pixel? Shadows volumes: Used in Doom (Expensive) From lights,

Wilf LaLonde ©2012Comp 4501

• Given a filter and a position to sample, choose a reference depth for that position; e.g., the pixel depth.

• For each filter sample, determine if it is closer than the reference (a boolean result).

• Determine the percentage of pixels closer than the reference value.

• Use this percentage to attenuate the light.

100% closer white (in light)0% closer black (in shadow)p% closer gray (interpolate via p as a

fraction from black to white)

What Percentage-Closer Filtering (PCF) is? What Percentage-Closer Filtering (PCF) is?

Page 70: Wilf LaLonde ©2012 Comp 4501 95.4501 Shadows. Wilf LaLonde ©2012 Comp 4501 How Do I Draw a Pixel? Shadows volumes: Used in Doom (Expensive) From lights,

Wilf LaLonde ©2012Comp 4501

• 4 probes for sample [x,y]...

An Example 2x2 FilterAn Example 2x2 Filter

Weight 0.25

Weight 0.25

Weight 0.25

Weight 0.25

[x, y] [x+1, y]

[x, y+1] [x+1, y+1]

Page 71: Wilf LaLonde ©2012 Comp 4501 95.4501 Shadows. Wilf LaLonde ©2012 Comp 4501 How Do I Draw a Pixel? Shadows volumes: Used in Doom (Expensive) From lights,

Wilf LaLonde ©2012Comp 4501

Using a 2x2 PCF FilterUsing a 2x2 PCF Filter

float lightIntensityVia2x2PCF (

//1 if visible; 0 if in shadow; in between if partially in shadow...

//4 canSeeLight parameters...float3 pixelToLightCS, float3 pixelNormalCS, float3 normalizedToLightVector, float coneAngleAsCosine,

//3 isInShadow parameters...sampler2D shadowMap, float4 positionLPS, float epsilon) {

//Percentage closer filtering averages the boolean inShadow results (not the depths).

if (!canSeeLight (pixelToLightCS, pixelNormalCS, normalizedToLightVector, coneAngleAsCosine)) return 0.0;

float2 uv = asShadowMapTextureCoordinates (positionLPS);float pixelDepth = positionLPS.z / positionLPS.w; float dt = 1.0 / SMAP_SIZE;

float mapDepth0 = tex2D (g_samShadow, uv).r + epsilon;float mapDepth1 = tex2D (g_samShadow, clamp01 (uv+float2(dt,0))).r + epsilon;float mapDepth2 = tex2D (g_samShadow, clamp01 (uv+float2(0,dt))).r + epsilon;float mapDepth3 = tex2D (g_samShadow, clamp01 (uv+float2(dt,dt))).r + epsilon;

//Note: mapDepth0 > pixelDepth means it's further back (so reference pixel is VISIBLY in front).

return 0.25 * (mapDepth0 > pixelDepth) +0.25 * (mapDepth1 > pixelDepth) +0.25 * (mapDepth2 > pixelDepth) +0.25 * (mapDepth3 > pixelDepth);

}

Page 72: Wilf LaLonde ©2012 Comp 4501 95.4501 Shadows. Wilf LaLonde ©2012 Comp 4501 How Do I Draw a Pixel? Shadows volumes: Used in Doom (Expensive) From lights,

Wilf LaLonde ©2012Comp 4501

1 Sample 4 Sample PCF

ResultsResults

Page 73: Wilf LaLonde ©2012 Comp 4501 95.4501 Shadows. Wilf LaLonde ©2012 Comp 4501 How Do I Draw a Pixel? Shadows volumes: Used in Doom (Expensive) From lights,

Wilf LaLonde ©2012Comp 4501

How About Interpolating The ResultsHow About Interpolating The Results

float lightIntensityVia2x2InterpolatedPCF (

... SAME AS BEFORE ...

//Interpolate 0/1 results in terms of where uv is inside the pixel...float2 t = frac (uv * SMAP_SIZE); //T-value for where we are inside the pixel (for x, 0=>left, 1=>right; similar for y).

return

lerp (

lerp ((mapDepth0 > pixelDepth), (mapDepth1 > pixelDepth), t.x), //x interpolate

lerp ((mapDepth2 > pixelDepth), (mapDepth3 > pixelDepth), t.x), //x interpolate

t.y); //y interpolate}

Weight 0.25

Weight 0.25

Weight 0.25

Weight 0.25

[x, y] [x+1, y]

[x, y+1] [x+1, y+1]

mapDepth0 mapDepth1

mapDepth2 mapDepth3

note

float2 t

Page 74: Wilf LaLonde ©2012 Comp 4501 95.4501 Shadows. Wilf LaLonde ©2012 Comp 4501 How Do I Draw a Pixel? Shadows volumes: Used in Doom (Expensive) From lights,

Wilf LaLonde ©2012Comp 4501

4 Sample Interpolated PCF

ResultsResults

Page 75: Wilf LaLonde ©2012 Comp 4501 95.4501 Shadows. Wilf LaLonde ©2012 Comp 4501 How Do I Draw a Pixel? Shadows volumes: Used in Doom (Expensive) From lights,

Wilf LaLonde ©2012Comp 4501

• 5 probes for sample [x,y]... different weights

How About a 5 probe Symmetric FilterHow About a 5 probe Symmetric Filter

Weight 1/6

Weight 2/6

Weight 1/6

[x, y] [x+1, y]

[x, y+1]

Weight 1/6

[x, y-1]

Weight 1/6

[x-1, y]

Page 76: Wilf LaLonde ©2012 Comp 4501 95.4501 Shadows. Wilf LaLonde ©2012 Comp 4501 How Do I Draw a Pixel? Shadows volumes: Used in Doom (Expensive) From lights,

Wilf LaLonde ©2012Comp 4501

How About Interpolating The ResultsHow About Interpolating The Results

float lightIntensityVia2x2InterpolatedPCF (...#define useSymmetricProbes 0

#if (useSymmetricProbes) float mapDepthCenter = tex2D (g_samShadow, uv).r + epsilon;float mapDepth0 = tex2D (g_samShadow, clamp01 (uv+float2(-dt,0))).r + epsilon;float mapDepth1 = tex2D (g_samShadow, clamp01 (uv+float2(dt,0))).r + epsilon;float mapDepth2 = tex2D (g_samShadow, clamp01 (uv+float2(0,-dt))).r + epsilon;float mapDepth3 = tex2D (g_samShadow, clamp01 (uv+float2(0,dt))).r + epsilon;

#elsefloat mapDepth0 = tex2D (g_samShadow, uv).r + epsilon;float mapDepth1 = tex2D (g_samShadow, clamp01 (uv+float2(dt,0))).r + epsilon;float mapDepth2 = tex2D (g_samShadow, clamp01 (uv+float2(0,dt))).r + epsilon;float mapDepth3 = tex2D (g_samShadow, clamp01 (uv+float2(dt,dt))).r + epsilon;

#endif //useSymmetricProbes...#if (useSymmetricProbes)

return (lerp ((mapDepth0 > pixelDepth), (mapDepth1 > pixelDepth), t.x) + //x interpolatelerp ((mapDepth2 > pixelDepth), (mapDepth3 > pixelDepth), t.y) + //y interpolate 2 * (mapDepthCenter > pixelDepth)) //Double weight for center

* 0.16; //Divide by 6 (1/6 = 0.16)#else

return lerp (

lerp ((mapDepth0 > pixelDepth), (mapDepth1 > pixelDepth), t.x), //x interpolatelerp ((mapDepth2 > pixelDepth), (mapDepth3 > pixelDepth), t.x), //x interpolatet.y); //y interpolate

#endif //useSymmetricProbes}

Weight 1/6

Weight 2/6

Weight 1/6

[x, y] [x+1, y]

[x, y+1]

Weight 1/6

[x, y-1]

Weight 1/6

[x-1, y]

Page 77: Wilf LaLonde ©2012 Comp 4501 95.4501 Shadows. Wilf LaLonde ©2012 Comp 4501 How Do I Draw a Pixel? Shadows volumes: Used in Doom (Expensive) From lights,

Wilf LaLonde ©2012Comp 4501

5 Probe Symmetric PCF

ResultsResults

Page 78: Wilf LaLonde ©2012 Comp 4501 95.4501 Shadows. Wilf LaLonde ©2012 Comp 4501 How Do I Draw a Pixel? Shadows volumes: Used in Doom (Expensive) From lights,

Wilf LaLonde ©2012Comp 4501

95.450195.4501

Variance Shadow

Maps

Page 79: Wilf LaLonde ©2012 Comp 4501 95.4501 Shadows. Wilf LaLonde ©2012 Comp 4501 How Do I Draw a Pixel? Shadows volumes: Used in Doom (Expensive) From lights,

Wilf LaLonde ©2012Comp 4501

• Supports hardware filtering (mipmapping + anisotropic filtering).

• Probabilistically inspired and so difficult to understand.

• Claims to reduce aliasing.• Requires no changes to the game engine

architecture…

Variance Shadow MapsVariance Shadow Maps

1-Sample VarianceMap is Equivalent to Many-Sample PCF with large filters

1-Sample VarianceMap is Equivalent to Many-Sample PCF with large filters

Page 80: Wilf LaLonde ©2012 Comp 4501 95.4501 Shadows. Wilf LaLonde ©2012 Comp 4501 How Do I Draw a Pixel? Shadows volumes: Used in Doom (Expensive) From lights,

Wilf LaLonde ©2012Comp 4501

• Instead of rendering just the depth as seen by a light into a shadow texture, render both depth and squared depth.

• Allow the card to anti-alias or blur the shadow map texture.

• If the depth test for a pixel indicates that it is in shadow, say it’s black just like PCF.

• If it is not in shadow, compute the “percentage in shadow” from the filtered data (from this ONE SAMPLE). Few people understand this part…

Variance Shadow MapsVariance Shadow Maps

Page 81: Wilf LaLonde ©2012 Comp 4501 95.4501 Shadows. Wilf LaLonde ©2012 Comp 4501 How Do I Draw a Pixel? Shadows volumes: Used in Doom (Expensive) From lights,

Wilf LaLonde ©2012Comp 4501

Gaussian FiltersGaussian Filters

• In terms of mean and variance 2.

Page 82: Wilf LaLonde ©2012 Comp 4501 95.4501 Shadows. Wilf LaLonde ©2012 Comp 4501 How Do I Draw a Pixel? Shadows volumes: Used in Doom (Expensive) From lights,

Wilf LaLonde ©2012Comp 4501

• Let x be a random variable drawn from a distribution with mean and variance 2. Then for t >

P(x t) pmax(t) =

Chebychev’s InequalityChebychev’s Inequality

2

2 + (t-)2

We want to re-interpret this in terms of pixel depth d; i.e. t = d.

We want to re-interpret this in terms of pixel depth d; i.e. t = d.

m is what you expect x to be; i.e. E(x)

2 is how you expect x to change; i.e. E(x2) - E(x)2

Page 83: Wilf LaLonde ©2012 Comp 4501 95.4501 Shadows. Wilf LaLonde ©2012 Comp 4501 How Do I Draw a Pixel? Shadows volumes: Used in Doom (Expensive) From lights,

Wilf LaLonde ©2012Comp 4501

• Let x be a random variable drawn from a distribution with mean and variance 2. Then for t >

P(x t) pmax(t) =

Chebychev’s InequalityChebychev’s Inequality

2

2 + (t-)2

We want to re-interpret this in terms of pixel depth d; i.e. t = d.We want to re-interpret this in terms of pixel depth d; i.e. t = d.

m is what you expect x to be; i.e. E(x)

2 is how you expect x to change; i.e. E(x2) - E(x)2

For depth samples sd and sd

2 and pixel depth d

If d > sd, theorem applies

pixel is partially shadowed

pixel is behind

Page 84: Wilf LaLonde ©2012 Comp 4501 95.4501 Shadows. Wilf LaLonde ©2012 Comp 4501 How Do I Draw a Pixel? Shadows volumes: Used in Doom (Expensive) From lights,

Wilf LaLonde ©2012Comp 4501

• Let x be a random variable drawn from a distribution with mean and variance 2. Then for t >

P(x t) pmax(t) =

Chebychev’s InequalityChebychev’s Inequality

2

2 + (t-)2

We want to re-interpret this in terms of pixel depth d ; i.e. t = d.We want to re-interpret this in terms of pixel depth d ; i.e. t = d.

m is what you expect x to be; i.e. E(x)

2 is how you expect x to change; i.e. E(x2) - E(x)2

For depth samples sd and sd

2 and pixel depth d

= sd

2 = sd2 - 2

Probability that random depth is greater than d; i.e., of d being in front; i.e. of pixel being visible

d > sd

d

percentage visible

pixel behind(but with some probability)

Page 85: Wilf LaLonde ©2012 Comp 4501 95.4501 Shadows. Wilf LaLonde ©2012 Comp 4501 How Do I Draw a Pixel? Shadows volumes: Used in Doom (Expensive) From lights,

Wilf LaLonde ©2012Comp 4501

• For depth samples sd and sd2 and pixel depth

d, the percentage visible is

Chebychev’s InequalityChebychev’s Inequality

2

2 + (d-)2

where = sd and 2 = sd2 - 2

visible means unaccluded

Page 86: Wilf LaLonde ©2012 Comp 4501 95.4501 Shadows. Wilf LaLonde ©2012 Comp 4501 How Do I Draw a Pixel? Shadows volumes: Used in Doom (Expensive) From lights,

Wilf LaLonde ©2012Comp 4501

• An occluder at depth d1 that casts a shadow on a surface at depth d2 where p is the percentage that is visible (unoccluded).

To validate Chebychev’s inequality, considerTo validate Chebychev’s inequality, consider

d1

d2

Top view with p unoccluded

p

Let’s compute everything from 1st principles and see if Chebychev’s inequality gives us p?

2

2 + (d2-)2

Page 87: Wilf LaLonde ©2012 Comp 4501 95.4501 Shadows. Wilf LaLonde ©2012 Comp 4501 How Do I Draw a Pixel? Shadows volumes: Used in Doom (Expensive) From lights,

Wilf LaLonde ©2012Comp 4501

First compute and 2 (average depth + depth variance).First compute and 2 (average depth + depth variance).

d1

d2

p unaccluded, 1- p occluded

p

= E(x) = p d2 + (1-p) d1

E(x2) = p d22 + (1-p) d1

2

2 = E(x2) - E(x)2 = p d22 + (1-p) d1

2 - (p d2 + (1-p) d1)2

1-p

Expected depth is d2 with some probabilityand d1 with some probability

Page 88: Wilf LaLonde ©2012 Comp 4501 95.4501 Shadows. Wilf LaLonde ©2012 Comp 4501 How Do I Draw a Pixel? Shadows volumes: Used in Doom (Expensive) From lights,

Wilf LaLonde ©2012Comp 4501

Simplify 2 Simplify 2

2 = p d22 + (1-p) d1

2 - (p d2 + (1-p) d1)2

= p d22 + (1-p) d1

2 - (p2d22 + (1-p)2d1

2 + 2p(1-p)d1d2)

= (p - p2) d22 + ((1-p) - (1-p)2) d1

2 - 2(p-p2)d1d2)

= (p - p2) d22 + (p - p2) d1

2 - 2(p - p2)d1d2)

= (p - p2) (d22 + d1

2 - 2d1d2)

= (p - p2) (d2 - d1)2

= p(1-p) (d2 - d1)2

(1-p) - (1-p)2

= 1-p - (1-2p+p2)

= 1-p -1+2p-p2

= p-p2

Page 89: Wilf LaLonde ©2012 Comp 4501 95.4501 Shadows. Wilf LaLonde ©2012 Comp 4501 How Do I Draw a Pixel? Shadows volumes: Used in Doom (Expensive) From lights,

Wilf LaLonde ©2012Comp 4501

Also, compute and simplify (d2-)2 (needed too)Also, compute and simplify (d2-)2 (needed too)

(d2-)2

= (d2 2 - 2d2 - 2)

= d22 - 2d2 (pd2 + (1-p)d1)

+ p2d22 + 2p d2(1-p) d1 + (1-p)2d1

2

= d22 - 2pd2

2 - 2(1-p)d1d2

+ p2d22 + 2p d2(1-p) d1 + (1-p)2d1

2

= d22 (1-2p+p2) - 2d2d1 (1-p - p(1-p)) + d1

2(1-p)2

= (d22 - 2d2d1 + d1

2) (1-p)2

= (d2 - d1)2 (1-p)2 = (1-p)2 (d2

- d1)2

1-2p+p2 = (1-p)2

Page 90: Wilf LaLonde ©2012 Comp 4501 95.4501 Shadows. Wilf LaLonde ©2012 Comp 4501 How Do I Draw a Pixel? Shadows volumes: Used in Doom (Expensive) From lights,

Wilf LaLonde ©2012Comp 4501

Percentage visible via Chebychev’s inequalityPercentage visible via Chebychev’s inequality

Since 2 = p(1-p)(d2-d1)2, and (d2-)2 = (1-p)2(d2-d1)2, percentage visible

2

2 + (d2-)2

p(1-p) (d2 - d1)2

p(1-p)(d2 - d1)2 + (1-p)2 (d2 - d1)2

p

p + (1-p)

=

=

= = p

Cancel (d2 - d1)2

Cancel (1-p)

Chebychev’s inequality works!

Page 91: Wilf LaLonde ©2012 Comp 4501 95.4501 Shadows. Wilf LaLonde ©2012 Comp 4501 How Do I Draw a Pixel? Shadows volumes: Used in Doom (Expensive) From lights,

Wilf LaLonde ©2012Comp 4501

• Let x be a random variable drawn from a distribution with mean and variance 2. Then for t >

P(x t) pmax(t) =

Programming Chebychev’s InequalityProgramming Chebychev’s Inequality

2

2 + (t-)2

With t = d, denote E(x) by z, and E(x2) by zSquared

With t = d, denote E(x) by z, and E(x2) by zSquared

m is what you expect x to be; i.e. E(x)

2 is how you expect x to change; i.e. E(x2) - E(x)2

Page 92: Wilf LaLonde ©2012 Comp 4501 95.4501 Shadows. Wilf LaLonde ©2012 Comp 4501 How Do I Draw a Pixel? Shadows volumes: Used in Doom (Expensive) From lights,

Wilf LaLonde ©2012Comp 4501

• Let x be a random variable drawn from a distribution with mean and variance 2. Then for t >

P(x t) pmax(t) =

Programming Chebychev’s InequalityProgramming Chebychev’s Inequality

2

2 + (d-z)2

With t = d, denote E(x) by z, and E(x2) by zSquared

With t = d, denote E(x) by z, and E(x2) by zSquared

m is what you expect x to be; i.e. z

2 is how you expect x to change; i.e. zSquared - z2

Page 93: Wilf LaLonde ©2012 Comp 4501 95.4501 Shadows. Wilf LaLonde ©2012 Comp 4501 How Do I Draw a Pixel? Shadows volumes: Used in Doom (Expensive) From lights,

Wilf LaLonde ©2012Comp 4501

• Start with a pixel at depth d with mean and variance [z, zSquared] retrieved from the 2-component shadow map (because of harware anti-aliasing or blurring, zSquared z2; i.e., the 2 components average to values that don’t match).

• If the depth test for a pixel indicates that it is visible (d z), return 100% visible.

• If not visible (d > z), compute percentage visible p from the following.

Variance Shadow MapsVariance Shadow Maps

2

2 + (d-z)2If (d > z) p = 2 = zSquared - z2

Page 94: Wilf LaLonde ©2012 Comp 4501 95.4501 Shadows. Wilf LaLonde ©2012 Comp 4501 How Do I Draw a Pixel? Shadows volumes: Used in Doom (Expensive) From lights,

Wilf LaLonde ©2012Comp 4501

//Create the shadow map texture... V_RETURN ( pd3dDevice->CreateTexture (

SHADOWMAP_SIZE, SHADOWMAP_SIZE,1, D3DUSAGE_RENDERTARGET,D3DFMT_R32F, D3DPOOL_DEFAULT,&g_pShadowMap, NULL ) );

Changing What We Can Write Into ShadowMapChanging What We Can Write Into ShadowMap

const bool usingVarianceShadowMap = experiment == drawVarianceShadow1Sample;

usingVarianceShadowMap ? D3DFMT_A32B32G32R32F : D3DFMT_R32F

Allowing the texture to contains MORE than just a depth

Page 95: Wilf LaLonde ©2012 Comp 4501 95.4501 Shadows. Wilf LaLonde ©2012 Comp 4501 How Do I Draw a Pixel? Shadows volumes: Used in Doom (Expensive) From lights,

Wilf LaLonde ©2012Comp 4501

Time for an Experiment (Experiment 8)Time for an Experiment (Experiment 8)

void PixShadow (float2 Depth : TEXCOORD0, out float4 Color : COLOR) {

//Depth is z / w#if (experiment == drawVarianceShadow1Sample)

float pixelDepth = Depth.x / Depth.y; Color = float4 (pixelDepth, pixelDepth * pixelDepth, 0, 0);

#elseColor = Depth.x / Depth.y;

#endif //experiment}

• WilfShadowExperiment.choice

• Add to “ShadowMap.cpp” file.

• Changes to the ShadowMap Writing Shader...

#define drawVarianceShadow1Sample 8

#define experiment drawVarianceShadow1Sample

#include "WilfShadowExperiment.choice"

Page 96: Wilf LaLonde ©2012 Comp 4501 95.4501 Shadows. Wilf LaLonde ©2012 Comp 4501 How Do I Draw a Pixel? Shadows volumes: Used in Doom (Expensive) From lights,

Wilf LaLonde ©2012Comp 4501

Time for an Experiment (Experiment 8)Time for an Experiment (Experiment 8)

sampler2D g_samShadow = sampler_state {

Texture = <g_txShadow>; #if (experiment == drawVarianceShadow1Sample)

MinFilter = Anisotropic;MagFilter = Anisotropic;MipFilter = Anisotropic; Filter = Anisotropic; MaxAnisotropy = 16;

#elseMinFilter = Point;MagFilter = Point;MipFilter = Point;

#endif //experiment

AddressU = Clamp;AddressV = Clamp;

};

• Changes to Get Hardware Anti-Aliasing...

Page 97: Wilf LaLonde ©2012 Comp 4501 95.4501 Shadows. Wilf LaLonde ©2012 Comp 4501 How Do I Draw a Pixel? Shadows volumes: Used in Doom (Expensive) From lights,

Wilf LaLonde ©2012Comp 4501

Simplifying a bitSimplifying a bitfloat lightIntensityViaVariance (

//1 if visible; 0 if in shadow; in between if partially in shadow...

//4 canSeeLight parameters...

float3 pixelToLightCS, float3 pixelNormalCS, float3 normalizedToLightVector, float coneAngleAsCosine,

//3 isInShadow parameters... sampler2D shadowMap, float2 textureCoordinateSS, in float4 positionLPS, float epsilon) {

//Immediately deal with the situations where variance does NOT apply...if (!canSeeLight (pixelToLightCS, pixelNormalCS, normalizedToLightVector, coneAngleAsCosine)) return 0.0;

float2 uv = asShadowMapTextureCoordinates (positionLPS);float pixelDepth = positionLPS.z / positionLPS.w - epsilon;

float2 sample = tex2D (g_samShadow, uv).rg; float z = sample.r; float zSquared = sample.g;if (pixelDepth <= z) return 1.0; //Completely visible...

//Variance applies...

float varianceSquared = zSquared - z * z;float depthDelta = pixelDepth – z;float probabilityOfBeingVisible = varianceSquared / (varianceSquared + depthDelta * depthDelta );

return probabilityOfBeingVisible;

}

2

2 + (d-z)2If (d > z) p = 2 = zSquared - z2

still there

Page 98: Wilf LaLonde ©2012 Comp 4501 95.4501 Shadows. Wilf LaLonde ©2012 Comp 4501 How Do I Draw a Pixel? Shadows volumes: Used in Doom (Expensive) From lights,

Wilf LaLonde ©2012Comp 4501

Looks A Little Strange (Pixelation Visible)Looks A Little Strange (Pixelation Visible)

Page 99: Wilf LaLonde ©2012 Comp 4501 95.4501 Shadows. Wilf LaLonde ©2012 Comp 4501 How Do I Draw a Pixel? Shadows volumes: Used in Doom (Expensive) From lights,

Wilf LaLonde ©2012Comp 4501

Taking Suggestions Into Account (Even worse)Taking Suggestions Into Account (Even worse)return pow (probabilityOfBeingVisible, 4); //Suggestion by a number of papers...

Page 100: Wilf LaLonde ©2012 Comp 4501 95.4501 Shadows. Wilf LaLonde ©2012 Comp 4501 How Do I Draw a Pixel? Shadows volumes: Used in Doom (Expensive) From lights,

Wilf LaLonde ©2012Comp 4501

Another Addition (Many other failures tried)Another Addition (Many other failures tried)float varianceSquared = zSquared - z * z; varianceSquared = abs (varianceSquared);

Page 101: Wilf LaLonde ©2012 Comp 4501 95.4501 Shadows. Wilf LaLonde ©2012 Comp 4501 How Do I Draw a Pixel? Shadows volumes: Used in Doom (Expensive) From lights,

Wilf LaLonde ©2012Comp 4501

• Implementers typically use the linearized depth (i.e., depth in meters) rather than the clip coordinate depth which is in the range 0 to 1 (not a very big range when subtracting numbers and squaring them).

Search of the Literature Also RevealsSearch of the Literature Also Reveals

Just Need the position in camera (view) space when drawing the shadowMap via shaders VertShadow and PixShadow.

Page 102: Wilf LaLonde ©2012 Comp 4501 95.4501 Shadows. Wilf LaLonde ©2012 Comp 4501 How Do I Draw a Pixel? Shadows volumes: Used in Doom (Expensive) From lights,

Wilf LaLonde ©2012Comp 4501

Will Using Linear Depth Fix ItWill Using Linear Depth Fix It

Details for the reader (NOT DONE)

• WilfShadowExperiment.choice

• Other details left for the reader.

#define drawVarianceShadow1SampleViaLinearDepth 9

#define experiment drawVarianceShadow1SampleViaLinearDepth

Page 103: Wilf LaLonde ©2012 Comp 4501 95.4501 Shadows. Wilf LaLonde ©2012 Comp 4501 How Do I Draw a Pixel? Shadows volumes: Used in Doom (Expensive) From lights,

Wilf LaLonde ©2012Comp 4501

Nvidia SDK 10 has a Demo But It’s Using 9x9 VSMNvidia SDK 10 has a Demo But It’s Using 9x9 VSM

Also using MSAA

Page 104: Wilf LaLonde ©2012 Comp 4501 95.4501 Shadows. Wilf LaLonde ©2012 Comp 4501 How Do I Draw a Pixel? Shadows volumes: Used in Doom (Expensive) From lights,

Wilf LaLonde ©2012Comp 4501

• There are soft shadow demos that might be simpler and more effective...

Perhaps We Need to Look ElsewherePerhaps We Need to Look Elsewhere

By determining “how much” of a filter rectangle around the pixel is visible

all visible 69% visible 0% visible

3x3 filter

Page 105: Wilf LaLonde ©2012 Comp 4501 95.4501 Shadows. Wilf LaLonde ©2012 Comp 4501 How Do I Draw a Pixel? Shadows volumes: Used in Doom (Expensive) From lights,

Wilf LaLonde ©2012Comp 4501

95.450195.4501

Soft Shadows

Page 106: Wilf LaLonde ©2012 Comp 4501 95.4501 Shadows. Wilf LaLonde ©2012 Comp 4501 How Do I Draw a Pixel? Shadows volumes: Used in Doom (Expensive) From lights,

Wilf LaLonde ©2012Comp 4501

Soft ShadowsSoft Shadows

Page 107: Wilf LaLonde ©2012 Comp 4501 95.4501 Shadows. Wilf LaLonde ©2012 Comp 4501 How Do I Draw a Pixel? Shadows volumes: Used in Doom (Expensive) From lights,

Wilf LaLonde ©2012Comp 4501

Fuzzy Compared To Hard ShadowsFuzzy Compared To Hard Shadows

total darkness

partial darkness

Page 108: Wilf LaLonde ©2012 Comp 4501 95.4501 Shadows. Wilf LaLonde ©2012 Comp 4501 How Do I Draw a Pixel? Shadows volumes: Used in Doom (Expensive) From lights,

Wilf LaLonde ©2012Comp 4501

Nvidia DX10 SDK has Percentage Closer Soft

Shadows (25x25 Poison Filter)

Nvidia DX10 SDK has Percentage Closer Soft

Shadows (25x25 Poison Filter)

Let’s skip this one

Page 109: Wilf LaLonde ©2012 Comp 4501 95.4501 Shadows. Wilf LaLonde ©2012 Comp 4501 How Do I Draw a Pixel? Shadows volumes: Used in Doom (Expensive) From lights,

Wilf LaLonde ©2012Comp 4501

Inspired By SoftShadows Demo of Nvidia SDK 10Inspired By SoftShadows Demo of Nvidia SDK 10

Fast Version Works Well

Accurate technique implementsReal-Time Soft Shadow Mapping by Back Projection, Guennebaud et all

Page 110: Wilf LaLonde ©2012 Comp 4501 95.4501 Shadows. Wilf LaLonde ©2012 Comp 4501 How Do I Draw a Pixel? Shadows volumes: Used in Doom (Expensive) From lights,

Wilf LaLonde ©2012Comp 4501

• Hard shadows 1x1 filter (0 or 1 value)• Softer shadows 2x2 filter in range [0,1]• Even softer shadows 16x16 filter• Softer shadows yet 64x64 filter

Recall: PCF Shadows From Point LightsRecall: PCF Shadows From Point Lights

Per Pixel Large filters very slow shaders…Per Pixel Large filters very slow shaders…

Page 111: Wilf LaLonde ©2012 Comp 4501 95.4501 Shadows. Wilf LaLonde ©2012 Comp 4501 How Do I Draw a Pixel? Shadows volumes: Used in Doom (Expensive) From lights,

Wilf LaLonde ©2012Comp 4501

• If we use a 32x32 filter only where its needed• Otherwise, a 16x16 filter if its needed• Otherwise, a 8x8 filter if its needed• Otherwise, a 4x4 filter if its needed• Otherwise, a 2x2 filter if its needed• Otherwise, a 1x1 filter if its needed

But We Can Be Efficient...But We Can Be Efficient...

How Do We Decide? Key Insight: a MIN/MAX shadow mapHow Do We Decide? Key Insight: a MIN/MAX shadow map

Page 112: Wilf LaLonde ©2012 Comp 4501 95.4501 Shadows. Wilf LaLonde ©2012 Comp 4501 How Do I Draw a Pixel? Shadows volumes: Used in Doom (Expensive) From lights,

Wilf LaLonde ©2012Comp 4501

Consider Lower Detail Pixels (1 red/blue = 4 black)Consider Lower Detail Pixels (1 red/blue = 4 black)

Min (low detail depth)

Max (low detail depth)

High detail depth

Technically, a low resolution pixel is divided into 4 higher resolution pixels...

Page 113: Wilf LaLonde ©2012 Comp 4501 95.4501 Shadows. Wilf LaLonde ©2012 Comp 4501 How Do I Draw a Pixel? Shadows volumes: Used in Doom (Expensive) From lights,

Wilf LaLonde ©2012Comp 4501

• Build a hierarchy of shadow texture resolutions; specifically, a min-max shadow map hierarchy.

• Access the shadow map with a recursive shader starting at 1-pixel version and recursively go to lower levels if needed.

• Find a way to implement the recursive shader NON-RECURSIVELY.

Basic IdeaBasic Idea

Page 114: Wilf LaLonde ©2012 Comp 4501 95.4501 Shadows. Wilf LaLonde ©2012 Comp 4501 How Do I Draw a Pixel? Shadows volumes: Used in Doom (Expensive) From lights,

Wilf LaLonde ©2012Comp 4501

Considering ONLY Lower Detail Pixels, What Can We Deduce?Considering ONLY Lower Detail Pixels, What Can We Deduce?

VISIBLE (Pz Min)

HIDDEN (Pz Max)

DON’T KNOW

brightness = 1

brightness = 0

need more detail

Page 115: Wilf LaLonde ©2012 Comp 4501 95.4501 Shadows. Wilf LaLonde ©2012 Comp 4501 How Do I Draw a Pixel? Shadows volumes: Used in Doom (Expensive) From lights,

Wilf LaLonde ©2012Comp 4501

95.450195.4501

Where Do We Start

Page 116: Wilf LaLonde ©2012 Comp 4501 95.4501 Shadows. Wilf LaLonde ©2012 Comp 4501 How Do I Draw a Pixel? Shadows volumes: Used in Doom (Expensive) From lights,

Wilf LaLonde ©2012Comp 4501

• The Nvidia DX SDK 10 has a good DirectX10 demo called “SoftShadow” from 2007.

• Placed a copy of the demo in “C:\School\4501.2012\From June 2010 DX SDK – Wilf” even though it’s NOT a Microsoft demo.

• It runs either a FAST demo or an ACCURATE demo (which is very complicated; it is based on the following paper).

Where Do We StartWhere Do We Start

BUT there is a problem. I Can’t DECIPHER the FAST demo...

Real-time soft shadow mapping by backprojection, Guennebaud, Barthe, and Paulin, Eurographics Symposium

on Rendering, 2006.Paper in the notes

Both Use a MIN-MAX Shadow Map Hierarchy

Wilf: see FastShadow in SoftShadowsORIGINAL.fx

Page 117: Wilf LaLonde ©2012 Comp 4501 95.4501 Shadows. Wilf LaLonde ©2012 Comp 4501 How Do I Draw a Pixel? Shadows volumes: Used in Doom (Expensive) From lights,

Wilf LaLonde ©2012Comp 4501

A Quick Look At The Drawing OrganizationA Quick Look At The Drawing Organization

Render the shadow map from light point (whole scene)

Technique "RenderDepth“ (whole scene) //Standard z-depth

Set “DepthTex0” as input texture for Standard z-depthTechnique "ConvertDepth“ (large triangle) //Top level min-max shadow

map

LOOP I FROM 1 /* SKIP 0 */ to nMips-1

Set viewport width /= 2; height /= 2Set “DepthMip2” as input texture for min-map depth [I – 1]Technique "CreateMip“ (large triangle) //Lower level min-map

shadow map

ENDLOOP

Render whole scene from the camera just to get the camera z-buffer.Technique “RenderDepth” (whole scene) ) //Standard z-depth

Render whole scene from the camera for textured and shadowed color Technique "RenderFast“ //Uses min-max shadow map

Render the light object with technique "RenderNoShadows“ Render GUI...

not interesting

understand as implemented

new implementation

DepthTex0

DepthMip2[0]

DepthMip2 [I – 1]

DepthMip2

DepthMip2 [I]

Page 118: Wilf LaLonde ©2012 Comp 4501 95.4501 Shadows. Wilf LaLonde ©2012 Comp 4501 How Do I Draw a Pixel? Shadows volumes: Used in Doom (Expensive) From lights,

Wilf LaLonde ©2012Comp 4501

• The mipmap textures 0, 1, 2, ... of DepthMip2 are accessed as texture resources. To the shader, they look like ordinary textures.

• The code for doing this is very messy. It’s shown on the next 2 slides but YOU SHOULD PROBABLY SKIP IT...

Important PointImportant Point

IT’S ALSO WIDER THAN THE SCREEN

Page 119: Wilf LaLonde ©2012 Comp 4501 95.4501 Shadows. Wilf LaLonde ©2012 Comp 4501 How Do I Draw a Pixel? Shadows volumes: Used in Doom (Expensive) From lights,

Wilf LaLonde ©2012Comp 4501

//1. Create the texture...D3D10_TEXTURE2D_DESC rtDesc = {

DEPTH_RES, //UINT Width;DEPTH_RES, //UINT Height;1,//UINT MipLevels;1,//UINT ArraySize;DXGI_FORMAT_R32_TYPELESS,//DXGI_FORMAT Format;{1, 0}, //DXGI_SAMPLE_DESC SampleDesc;D3D10_USAGE_DEFAULT, //D3D10_USAGE Usage;D3D10_BIND_SHADER_RESOURCE | D3D10_BIND_DEPTH_STENCIL,//UINT

BindFlags;0,//UINT CPUAccessFlags;0,//UINT MiscFlags;

};

//The texture with subtexture resourcesV(pDev10->CreateTexture2D (&rtDesc, NULL, &m_pDepthMip2));

Creates a texture with Mipmaps as ResourcesCreates a texture with Mipmaps as Resources

m_pDepthMip2 is the texture

m_pDepthMip2SRViews[im] is the subtexture (shader resource view)

m_pDepthMip2RTViews[im] is the render target view

WILF Note: MipLevels from SDK help => Use 1 for a multisampled texture; or 0 to generate a full set of subtextures

(this creates the texture but it’s NOT finished, I think).

Page 120: Wilf LaLonde ©2012 Comp 4501 95.4501 Shadows. Wilf LaLonde ©2012 Comp 4501 How Do I Draw a Pixel? Shadows volumes: Used in Doom (Expensive) From lights,

Wilf LaLonde ©2012Comp 4501

//2Set up a description for shader resource and render target...D3D10_SHADER_RESOURCE_VIEW_DESC srViewDesc; srViewDesc.Format = DXGI_FORMAT_R32_FLOAT; srViewDesc.ViewDimension = D3D10_SRV_DIMENSION_TEXTURE2D; srViewDesc.Texture2D.MostDetailedMip = 0; srViewDesc.Texture2D.MipLevels = nMips;srViewDesc.Format = DXGI_FORMAT_R32G32_FLOAT;V(pDev10->CreateShaderResourceView (m_pDepthMip2, &srViewDesc, &m_pDepthMip2SRView));

srViewDesc.Texture2D.MipLevels = 1;D3D10_RENDER_TARGET_VIEW_DESC rtViewDesc;rtViewDesc.ViewDimension = D3D10_RTV_DIMENSION_TEXTURE2D;

//3. Create the view and render targets for the mipmaps...for (int im = 0; im < nMips; ++im) {

srViewDesc.Texture2D.MostDetailedMip = im;srViewDesc.Format = DXGI_FORMAT_R32G32_FLOAT;V(pDev10->CreateShaderResourceView (m_pDepthMip2, &srViewDesc, &m_pDepthMip2SRViews[im]));rtViewDesc.Texture2D.MipSlice = im;rtViewDesc.Format = DXGI_FORMAT_R32G32_FLOAT;V(pDev10->CreateRenderTargetView (m_pDepthMip2, &rtViewDesc, &m_pDepthMip2RTViews[im]));

}

Creates a texture with Mipmaps as ResourcesCreates a texture with Mipmaps as Resources

m_pDepthMip2 is the texture

m_pDepthMip2SRViews[im] is the subtexture (shader resource view)

m_pDepthMip2RTViews[im] is the render target view

creates the mipmaps

gives you access to the mipmaps

same for the render targets of the mipmaps

Page 121: Wilf LaLonde ©2012 Comp 4501 95.4501 Shadows. Wilf LaLonde ©2012 Comp 4501 How Do I Draw a Pixel? Shadows volumes: Used in Doom (Expensive) From lights,

Wilf LaLonde ©2012Comp 4501

technique10 RenderNoShadows {pass P0 {RenderSceneNoShadows VS and PS}}technique10 RenderFast {pass P0 {RenderSceneFast VS and PS}} technique10 RenderAcc {pass P0 {RenderSceneAcc VS and PS}}

technique10 RenderDepth {pass P0 {RenderDepthVS and NULL}}technique10 ReworkDepth2 { pass ConvertDepth {ConvertDepthVS and ConvertDepth2PS}} pass CreateMip {ConvertDepthVS and CreateMip2PS}}

pass ConvertToBig {ConvertDepthVS and ConvertToBigPS}}}

The ShadersThe Shaders

don’t need

redo

study

ID3D10Effect *g_pEffect; //From D3DX10CreateEffectFromFile

ID3D10EffectTechnique *pDReworkTechnique2 = g_pEffect >GetTechniqueByName ("ReworkDepth2");

pDReworkTechnique2->GetPassByName ("CreateMip")->Apply(0);

ignore

Page 122: Wilf LaLonde ©2012 Comp 4501 95.4501 Shadows. Wilf LaLonde ©2012 Comp 4501 How Do I Draw a Pixel? Shadows volumes: Used in Doom (Expensive) From lights,

Wilf LaLonde ©2012Comp 4501

95.450195.4501

Existing Shader Code

Page 123: Wilf LaLonde ©2012 Comp 4501 95.4501 Shadows. Wilf LaLonde ©2012 Comp 4501 How Do I Draw a Pixel? Shadows volumes: Used in Doom (Expensive) From lights,

Wilf LaLonde ©2012Comp 4501

RasterizerState RStateMSAAON {MultisampleEnable = FALSE;} //MSAA too slow

Texture2D<float> DepthTex0;Texture2D<float2> DepthMip2;Texture2D DiffuseTex;

SamplerComparisonState DepthCompare; //WILF: NOT USEDSamplerState DepthSampler {

Filter = MIN_MAG_MIP_POINT;AddressU = Clamp; AddressV = Clamp;

};SamplerState DiffuseSampler {

Filter = MIN_MAG_MIP_LINEAR;AddressU = Wrap; AddressV = Wrap;

};

Shader Header: Part 1 of 2Shader Header: Part 1 of 2

Note DX10 versus DX9 differences (texture and filter)...

Page 124: Wilf LaLonde ©2012 Comp 4501 95.4501 Shadows. Wilf LaLonde ©2012 Comp 4501 How Do I Draw a Pixel? Shadows volumes: Used in Doom (Expensive) From lights,

Wilf LaLonde ©2012Comp 4501

cbuffer cb0 : register (b0) {float4 g_vMaterialKd;float3 g_vLightPos; ///< light in world CSfloat4 g_vLightFlux;float g_fFilterSize, g_fDoubleFilterSizeRev;row_major float4x4 mViewProj;row_major float4x4 mLightView;row_major float4x4 mLightViewProjClip2Tex;row_major float4x4 mLightProjClip2TexInv;bool bTextured;};

#define N_LEVELS 10#define DEPTH_RES 1024cbuffer cb1 : register (b1) {

float g_fResRev[N_LEVELS] = {1./1024, 1./512, 1./256, 1./128, 1./64, 1./32, 1./16, 1./8, 1./4, 1./2};};

Shader Header: Part 2 of 2Shader Header: Part 2 of 2

No special code in demo to load cbuffers (done as shown above)

V(g_pEffect->GetVariableByName ("mViewProj")->AsMatrix()->SetMatrix ((float *)&ssmap.mLightViewProj));

V(g_pEffect->GetVariableByName("DepthTex0")->AsShaderResource ()->SetResource (m_pDepthSRView[0]));

Page 125: Wilf LaLonde ©2012 Comp 4501 95.4501 Shadows. Wilf LaLonde ©2012 Comp 4501 How Do I Draw a Pixel? Shadows volumes: Used in Doom (Expensive) From lights,

Wilf LaLonde ©2012Comp 4501

float4 RenderDepthVS (float3 vPos: POSITION) : SV_Position { return mul (float4 (vPos, 1), mViewProj);}

Shader Technique RenderDepthShader Technique RenderDepth

Nothing special (convert to projection (or clip) space)

FROM SDK: In Direct3D 10, the SV_Position semantic (when used in the context of a pixel shader) specifies screen space coordinates (offset by 0.5).

SV_Position can be specified as an input to a vertex shader as well as an output.

When drawn into an 800x800 texture, [x,y] ranges from 0.5, 1.5, ..., 799.5 (actuality)

rather than 0, 1, 2, 3, ..., 798, 799 (better way to remember it)

Wilf: Microsoft must mean texture units; e.g. 0-799, not clip units; e.g., -1.0-+1.0.

Page 126: Wilf LaLonde ©2012 Comp 4501 95.4501 Shadows. Wilf LaLonde ©2012 Comp 4501 How Do I Draw a Pixel? Shadows volumes: Used in Doom (Expensive) From lights,

Wilf LaLonde ©2012Comp 4501

float4 ConvertDepthVS (uint iv : SV_VertexID) : SV_Position {return float4((iv << 1) & 2, iv & 2, 0.5, 1) * 2 - 1; //Wilf: For 0 => (0,0,0.5,1)*2-1 = (-1,-1,0,1)//Wilf: For 1 => (2,0,0.5,1)*2-1 = (3,-1,0,1) //clockwise//Wilf: For 2 => (0,2,0.5,1)*2-1 = (-1,3,0,1)//Wilf: Triangle encompases [-1,-1,0,1] to [1,1,0,1]

}

float2 ConvertDepth2PS (float4 vPos : SV_Position) : SV_Target0 {float fDepth = DepthTex0.Load (uint3 (vPos.x, vPos.y, 0)); //Wilf: Depth at

[x,y]return float2 (fDepth, fDepth); //Wilf: Let Min = Max = depth...

}

Shader Technique ConvertDepth To Create Min-Max Shadow MapShader Technique ConvertDepth To Create Min-Max Shadow Map

ID3D10Buffer *pNullVBuf[] = {NULL};unsigned pStrides[] = {0}; unsigned pOffsets[] = {0};pDev10->IASetVertexBuffers (0, 1, pNullVBuf, pStrides, pOffsets);pDev10->IASetInputLayout (NULL);pDev10->Draw (3, 0); Since there are NO vertices,

draw 3 VERTEX Ids.

+3

-1 0 +1 +2 +3

+2

+1

0

-1

How ConvertDepth Draw is done

Page 127: Wilf LaLonde ©2012 Comp 4501 95.4501 Shadows. Wilf LaLonde ©2012 Comp 4501 How Do I Draw a Pixel? Shadows volumes: Used in Doom (Expensive) From lights,

Wilf LaLonde ©2012Comp 4501

SAME ConvertDepthVS AND DRAW as previous slide

float2 CreateMip2PS (float4 vPos : SV_Position) : SV_Target0 {uint3 iPos = uint3 ((int) vPos.x << 1, (int) vPos.y << 1, 0); float2 vDepth = DepthMip2.Load (iPos), vDepth1; //Wilf: [2u,2v]++iPos.x;vDepth1 = DepthMip2.Load (iPos); //Wilf: [2u+1,2v]vDepth = float2 (min (vDepth.x, vDepth1.x), max (vDepth.y, vDepth1.y));++iPos.y;vDepth1 = DepthMip2.Load (iPos); //Wilf: [2u+1,2v+1]vDepth = float2 (min (vDepth.x, vDepth1.x), max (vDepth.y, vDepth1.y));--iPos.x;vDepth1 = DepthMip2.Load (iPos); //Wilf: [2u,2v+1]vDepth = float2 (min (vDepth.x, vDepth1.x), max (vDepth.y, vDepth1.y));return vDepth;

}

Shader Technique CreateMipShader Technique CreateMip

[u,v] [2u,2v]

Taking the MIN of the 4 pixels and the MAX of the 4 pixels

Each LOAD is already MIN (x) and MAX (y)

Compute MIN of the mins and MAX of the maxes...

Page 128: Wilf LaLonde ©2012 Comp 4501 95.4501 Shadows. Wilf LaLonde ©2012 Comp 4501 How Do I Draw a Pixel? Shadows volumes: Used in Doom (Expensive) From lights,

Wilf LaLonde ©2012Comp 4501

RECALL: Drawing OrganizationRECALL: Drawing Organization

Render the shadow map from light point (whole scene)

Technique "RenderDepth“ (whole scene) //Standard z-depth

Set “DepthTex0” as input texture for Standard z-depthTechnique "ConvertDepth“ (large triangle) //Top level min-max shadow

map

LOOP I FROM I to nMips-1

Set viewport width /= 2; height /= 2Set “DepthMip2” as input texture for min-map depth [I – 1]Technique "CreateMip“ (large triangle) //Lower level min-map

shadow map

ENDLOOP

Render whole scene from the camera just to get the z-buffer.Technique “RenderDepth” (whole scene) ) //Standard z-depth

Render whole scene from the camera for textured and shadowed color Technique "RenderFast“ //Uses min-max shadow map

Render the light object with technique "RenderNoShadows“ Render GUI...

not interesting

understand as implemented

new implementation

what’s le

ft

Page 129: Wilf LaLonde ©2012 Comp 4501 95.4501 Shadows. Wilf LaLonde ©2012 Comp 4501 How Do I Draw a Pixel? Shadows volumes: Used in Doom (Expensive) From lights,

Wilf LaLonde ©2012Comp 4501

95.450195.4501

Preliminaries for the

new Shader

Functionality

Page 130: Wilf LaLonde ©2012 Comp 4501 95.4501 Shadows. Wilf LaLonde ©2012 Comp 4501 How Do I Draw a Pixel? Shadows volumes: Used in Doom (Expensive) From lights,

Wilf LaLonde ©2012Comp 4501

• Goal is to determine how much of an area around a pixel is visible...

RecallRecall

So we’ll need to encode rectangles...

all visible 69% visible 0% visible

3x3 filter

Page 131: Wilf LaLonde ©2012 Comp 4501 95.4501 Shadows. Wilf LaLonde ©2012 Comp 4501 How Do I Draw a Pixel? Shadows volumes: Used in Doom (Expensive) From lights,

Wilf LaLonde ©2012Comp 4501

• We will start with the map rectangle for the entire 1024x1024 shadow and work our way down (dividing by 4) to a 1x1 map.

• When it intersects with the sample rectangle of the pixel, we try to determine how much of the area is visible to get soft shadows..

Basic PremiseBasic Premise

sample rectangle

map rectangle

Page 132: Wilf LaLonde ©2012 Comp 4501 95.4501 Shadows. Wilf LaLonde ©2012 Comp 4501 How Do I Draw a Pixel? Shadows volumes: Used in Doom (Expensive) From lights,

Wilf LaLonde ©2012Comp 4501

• Need 2 points: minimum and maximum

Representing RectanglesRepresenting Rectangles

minimum

maximum

• float4 rectangle = float4 (0,0, 1023,1023);

rectangle.xy maximum rectangle.zw

#define LEFT(rectangle) rectangle.x

#define RIGHT(rectangle) rectangle.z

#define TOP(rectangle) rectangle.y

#define BOTTOM(rectangle) rectangle.w

#define MINIMUM(rectangle) rectangle.xy

#define MAXIMUM(rectangle) rectangle.zw

Page 133: Wilf LaLonde ©2012 Comp 4501 95.4501 Shadows. Wilf LaLonde ©2012 Comp 4501 How Do I Draw a Pixel? Shadows volumes: Used in Doom (Expensive) From lights,

Wilf LaLonde ©2012Comp 4501

bool areasIntersect (float4 rectangle1, float4 rectangle2, inout float intersectionArea) {//Returns true for an intersection (also computes intersectionArea); false otherwise.

//Eliminate the cases where there is no intersection.if (LEFT (rectangle1) >= RIGHT (rectangle2) || RIGHT (rectangle1) <= LEFT (rectangle2) ||

BOTTOM (rectangle1) <= TOP (rectangle2) || TOP (rectangle1) >= BOTTOM (rectangle2)) return false;

float4 rectangle; //Clamp rectangle1 to lie between min and max of rectangle2.MINIMUM (rectangle) = clamp (MINIMUM (rectangle1), MINIMUM (rectangle2), MAXIMUM

(rectangle2));MAXIMUM (rectangle) = clamp (MAXIMUM (rectangle1), MINIMUM (rectangle2), MAXIMUM

(rectangle2));float2 extentOfArea = EXTENT (rectangle) ; intersectionArea = extentOfArea.x * extentOfArea.y; return true;

}

Rectangle IntersectionRectangle Intersection

#define CENTER(rectangle) (MINIMUM (rectangle) + MAXIMUM (rectangle)) * 0.5

#define EXTENT(rectangle) (MAXIMUM (rectangle) - MINIMUM (rectangle))

#define RECTANGLE(minimum, extent) float4 (minimum, minimum + extent)

Useful utilities

Page 134: Wilf LaLonde ©2012 Comp 4501 95.4501 Shadows. Wilf LaLonde ©2012 Comp 4501 How Do I Draw a Pixel? Shadows volumes: Used in Doom (Expensive) From lights,

Wilf LaLonde ©2012Comp 4501

bool isOutsideShadowMap (float3 uv) {if (uv.x < 0.0 || uv.x > 1.0) return true;if (uv.y < 0.0 || uv.y > 1.0) return true;if (uv.z < 0.0 || uv.z > 1.0) return true;return false;

}

float2 texel (const Texture2D<float2> texture, const SamplerState sampler, float2 uv, float mipmapLevel) {

return texture (sampler, uv, mipmapLevel); }

What if a Pixel Cannot Be Seen By The Light?What if a Pixel Cannot Be Seen By The Light?

Useful utilities

Page 135: Wilf LaLonde ©2012 Comp 4501 95.4501 Shadows. Wilf LaLonde ©2012 Comp 4501 How Do I Draw a Pixel? Shadows volumes: Used in Doom (Expensive) From lights,

Wilf LaLonde ©2012Comp 4501

95.450195.4501

New Shader Functionality

Page 136: Wilf LaLonde ©2012 Comp 4501 95.4501 Shadows. Wilf LaLonde ©2012 Comp 4501 How Do I Draw a Pixel? Shadows volumes: Used in Doom (Expensive) From lights,

Wilf LaLonde ©2012Comp 4501

RECALL: Considering ONLY Lower Detail Pixels, What Can We Deduce?RECALL: Considering ONLY Lower Detail Pixels, What Can We Deduce?

VISIBLE (Pz Min)

OCCLUDED (Pz Max)

DON’T KNOW

brightness = 1

brightness = 0

need more detail

bool inLight (float pixelDepth, float depthMapMinimumDepth) {return pixelDepth <= depthMapMinimumDepth;}

bool inShadow (float pixelDepth, float depthMapMaximumDepth) {return pixelDepth >= depthMapMaximumDepth;}

Page 137: Wilf LaLonde ©2012 Comp 4501 95.4501 Shadows. Wilf LaLonde ©2012 Comp 4501 How Do I Draw a Pixel? Shadows volumes: Used in Doom (Expensive) From lights,

Wilf LaLonde ©2012Comp 4501

float WilfPCFVisibilityUsingHierarchicalShadowMaps (float3 pixelUV) {//Returns 1 if all visible; 0 if all occluded; anywhere in between

otherwise...//Transform from [-1,+1] projection space to [0,1] texture space...pixelUV.xy = asShadowMapTextureCoordinates (pixelUV); //z is

unchangedif (isOutsideShadowMap (pixelUV)) return 0.0;

//Get extent (e.g., 1024x1024) from system rather than hardwired variables...

uint2 extent; uint numberOfLevels; DepthMip2.GetDimensions (0, extent.x, extent.y, numberOfLevels);

//Build a relatively large filter around uv... in [0,1] coodinates...float2 filterCenter = pixelUV.xy; float2 filterExtent = float2 (7, 7) / extent; float2 h = filterExtent * 0.5;float4 filterRectangle = float4 (filterCenter - h, filterCenter + h);

//And assume it's all visible for now...float totalFilterArea = filterExtent.x * filterExtent.y; float visibleArea = totalFilterArea;

//Assumes visibleArea, pixelUV, and filterRectangle are accessible.float4 mapRectangle = RECTANGLE (float2 (0.0, 0.0), float2 (1.0, 1.0));WilfRecursiveDecrementVisibleArea (mapRectangle, numberOfLevels -

1); float visibilityIntensity = visibleArea / totalFilterArea;return visibilityIntensity;

}

Computing If Visible (Opposite of Occluded)Computing If Visible (Opposite of Occluded)

Page 138: Wilf LaLonde ©2012 Comp 4501 95.4501 Shadows. Wilf LaLonde ©2012 Comp 4501 How Do I Draw a Pixel? Shadows volumes: Used in Doom (Expensive) From lights,

Wilf LaLonde ©2012Comp 4501

//Note: visibleArea, pixelUV, and filterRectangle are externally defined...//Mipmap level 0 is the HIGHEST detail (entire map is 1024x1024 pixels) whereas//mipmap level 9 is LOWEST detail (entire map is 1 pixel)...

void WilfRecursiveDecrementVisibleArea (float4 mapRectangle, float mipmapLevel) {//Adjusts visibleArea starting from the map rectangle of the entire texture //and the LOWEST detail mipmap level; mipmap is 1x1... Cuts the rectangle

into 4 //and recursively processes HIGHER (more detailed) mipmap levels; SO//mipmapLevel is decremented by recursion from 9, 8, ..., 2, 1, 0.

//Note: if mipmapLevel 0 is reached, minimum and maximum depths are equal...

//So one of inShadow or inLight will be true...

}

WilfRecursiveDecrementVisibleAreaWilfRecursiveDecrementVisibleArea

Next Slide Repeat Without Comments

Page 139: Wilf LaLonde ©2012 Comp 4501 95.4501 Shadows. Wilf LaLonde ©2012 Comp 4501 How Do I Draw a Pixel? Shadows volumes: Used in Doom (Expensive) From lights,

Wilf LaLonde ©2012Comp 4501

void WilfRecursiveDecrementVisibleArea (float4 mapRectangle, float mipmapLevel) {if (visibleArea <= 0.0) return; //Since there is nothing left to subtract...float2 mapMinMaxDepth = texel (DepthMip2, DepthSampler,

CENTER (mapRectangle), mipmapLevel).xy;

if (inLight (pixelUV.z, mapMinMaxDepth.x)) return; //So don't decrease...

float occludedArea; if (!areasIntersect (mapRectangle, filterRectangle, occludedArea)) return;

if (inShadow (pixelUV.z, mapMinMaxDepth.y)) {//So decrease by intersection...

visibleArea -= occludedArea; return;}

//Otherwise, we can't tell without processing a higher detail shadow map...float2 minimum = MINIMUM (mapRectangle);float2 halfExtent = EXTENT (mapRectangle) * 0.5; #define recur(min) WilfRecursiveDecrementVisibleArea ( \

RECTANGLE (min, halfExtent ), mipmapLevel - 1) recur (minimum);recur (minimum + float2 (halfExtent , 0));recur (minimum + float2 (0, halfExtent ));recur (minimum + halfExtent );#undef recur

}

WilfRecursiveDecrementVisibleAreaWilfRecursiveDecrementVisibleArea

Page 140: Wilf LaLonde ©2012 Comp 4501 95.4501 Shadows. Wilf LaLonde ©2012 Comp 4501 How Do I Draw a Pixel? Shadows volumes: Used in Doom (Expensive) From lights,

Wilf LaLonde ©2012Comp 4501

• Consider the following 16 numbers1 2 4 8 16 32 64 128 256 512 1024 2064 4096 8192 16384 32768

• Stack size 16 can handle a 32768 x 32768 texture provided there is only ONE entry stored per mipmap level...

A simple version needs 16 * 3 + 1 entries

(4 recursive calls per level but the last one is immediately popped)

• An entry could be

struct Stack {float4 rectangle; int level;};

Simulating a Stack: How Big Do We NeedSimulating a Stack: How Big Do We Need

Page 141: Wilf LaLonde ©2012 Comp 4501 95.4501 Shadows. Wilf LaLonde ©2012 Comp 4501 How Do I Draw a Pixel? Shadows volumes: Used in Doom (Expensive) From lights,

Wilf LaLonde ©2012Comp 4501

void WilfRecursiveDecrementVisibleArea (float4 mapRectangle, float mipmapLevel) {

#define recur(min) WilfRecursiveDecrementVisibleArea ( \RECTANGLE (min, halfExtent ), mipmapLevel - 1)

recur (minimum);recur (minimum + float2 (halfExtent , 0));recur (minimum + float2 (0, halfExtent ));recur (minimum + halfExtent );

}

Recall: General structureRecall: General structure

GENERIC CODE WORKING ON mapRectangle AND mipmapLevel

There are 3 returns

Page 142: Wilf LaLonde ©2012 Comp 4501 95.4501 Shadows. Wilf LaLonde ©2012 Comp 4501 How Do I Draw a Pixel? Shadows volumes: Used in Doom (Expensive) From lights,

Wilf LaLonde ©2012Comp 4501

Simple ApproachSimple Approach

void WilfIterativeDecrementVisibleArea (float4 mapRectangle, float mipmapLevel) {declareStack push (mapRectangle, mipmapLevel)while (top >= 0) {

pop (mapRectangle, mipmapLevel)

#define PUSH(x) push (RECTANGLE (x, halfExtent ), mipmapLevel - 1)

PUSH (minimum);PUSH (minimum + float2 (halfExtent , 0));PUSH (minimum + float2 (0, halfExtent ));PUSH (minimum + halfExtent );

}}

GENERIC CODE WORKING ON mapRectangle AND mipmapLevel

Replace 2nd and 3rd return by continue

Since processing order does not matter

#define declareStack \struct Stack {float4 rectangle; int level}; \Stack stack [16 * 3 + 1]; int top = -1;

#define push(a,b) top++; stack [top].rectangle = a; stack [top].level = b;#define pop(a,b) a = stack [top].rectangle; b = stack [top].level; top--;

Page 143: Wilf LaLonde ©2012 Comp 4501 95.4501 Shadows. Wilf LaLonde ©2012 Comp 4501 How Do I Draw a Pixel? Shadows volumes: Used in Doom (Expensive) From lights,

Wilf LaLonde ©2012Comp 4501

Using a Smaller StackUsing a Smaller Stack

void WilfIterativeDecrementVisibleArea (float4 mapRectangle, float mipmapLevel) {declareStack push (mapRectangle, mipmapLevel)while (top >= 0) {

pop (mapRectangle, mipmapLevel)

#define PUSH(x) push (RECTANGLE (x, halfExtent ), mipmapLevel - 1)

PUSH (minimum);PUSH (minimum + float2 (halfExtent , 0));PUSH (minimum + float2 (0, halfExtent ));PUSH (minimum + halfExtent );

}}

GENERIC CODE WORKING ON mapRectangle AND mipmapLevel

Replace 2nd and 3rd return by continue

Since processing order does not matter

• Is there a way to limit stack size to 16 by using the same entry 4 times? Think cases...

case 0

case 1 case 2 case 3

Page 144: Wilf LaLonde ©2012 Comp 4501 95.4501 Shadows. Wilf LaLonde ©2012 Comp 4501 How Do I Draw a Pixel? Shadows volumes: Used in Doom (Expensive) From lights,

Wilf LaLonde ©2012Comp 4501

Use 4 cases via a variable called “child”Use 4 cases via a variable called “child”

void WilfIterativeDecrementVisibleArea (float4 mapRectangle, float mipmapLevel) {declareStack; int child;push (mapRectangle, mipmapLevel, 0)while (top >= 0) {

get (mapRectangle, mipmapLevel, child)if (child == 0) {

}#define PUSH(x) push (RECTANGLE (x, halfExtent ), mipmapLevel – 1, 0)if (child == 0) {update; PUSH (minimum); continue;}if (child == 1) {update; PUSH (minimum + float2 (halfExtent , 0)); continue;} if (child == 2) {update; PUSH (minimum + float2 (0, halfExtent )); continue;} if (child == 3) {update; PUSH (minimum + halfExtent ); pop; continue;}

}}

GENERIC CODE WORKING ON mapRectangle AND mipmapLevel

#define declareStack struct Stack {float4 rectangle; int level; int child}; Stack stack [16]; int top = -1;#define push(a,b,c) top++; stack [top].rectangle = a; stack [top].level = b; stack [top].child = c; #define get(a,b,c) a = stack [top].rectangle ; b = stack [top].level; c = stack [top].child; #define pop top--;#define update stack [top].child++

unnecessary

Page 145: Wilf LaLonde ©2012 Comp 4501 95.4501 Shadows. Wilf LaLonde ©2012 Comp 4501 How Do I Draw a Pixel? Shadows volumes: Used in Doom (Expensive) From lights,

Wilf LaLonde ©2012Comp 4501

• Replace WilfRecursiveDecrementVisibleArea in WilfPCFVisibilityUsingHierarchicalShadowMaps by the contents of WilfIterativeDecrementVisibleArea.

• See actual code for final result... Used the simple approach rather than the more clever one...

Final RoutinesFinal Routines

Page 146: Wilf LaLonde ©2012 Comp 4501 95.4501 Shadows. Wilf LaLonde ©2012 Comp 4501 How Do I Draw a Pixel? Shadows volumes: Used in Doom (Expensive) From lights,

Wilf LaLonde ©2012Comp 4501

• Perspective Shadow Maps: Care and Feeding (subsection Tricks for Better Shadow Maps (subsubsection Blurring)) Kozlov, GPU Gems, pp. 217-244.

• For terrain and sunlight only, draw black or white into the depth texture. Then blur it by redrawing over smaller depth (actually color) map a few times (called ping-pong rendering). Finally, draw this blurred black and white color onto the terrain...

Tricks and ClevernessTricks and Cleverness

Page 147: Wilf LaLonde ©2012 Comp 4501 95.4501 Shadows. Wilf LaLonde ©2012 Comp 4501 How Do I Draw a Pixel? Shadows volumes: Used in Doom (Expensive) From lights,

Wilf LaLonde ©2012Comp 4501

• PCF shadows.• PCF shadows with a filter.• Variance shadows.• Soft shadows using a hierarchical min-max

depth map.• DirectX10 facilities

Mipmaps can be accessed as texturesSV_Position semantic gives you x/y tex coord.Texture extent can be accessed by shader.Can draw with n vertex ids and decode whatto do with it in vertex shader...

Quick ReviewQuick Review

Page 148: Wilf LaLonde ©2012 Comp 4501 95.4501 Shadows. Wilf LaLonde ©2012 Comp 4501 How Do I Draw a Pixel? Shadows volumes: Used in Doom (Expensive) From lights,

Wilf LaLonde ©2012Comp 4501

• Poisson Shadow Blur, Mitchell, pp 403-409, ShaderX3, 2005.• Percentage Closer Soft-shadows, Ferdinando, Siggraph 2005

Sketches, p. 35.• Variance shadow maps, Donnely and Lauritzen, Proc. Of the

Symposium on Interactive 3D Graphics and Games 2006, pp. 161-165.

• Real-time soft shadow mapping by backprojection, Guennebaud, Barthe, and Paulin, Eurographics Symposium on Rendering, 2006.

• Fast Soft Shadows with Temporal Coherance, Scherzer, Schwarzler, and Mattausch, pp. 243-255, , GPU Pro 2, 2011. A Fast, Small-Radius GPU Media Filter, MgGuire, pp. 165-173, ShaderX6, 2008. (Prince of Persia Look)

• Shadow Techniques for OpenGL Es 2.0, Feldstein, pp. 487-504, ShaderX6, 2008.

ReferencesReferences