25
shaderX7 4.1 Practical Cascaded Shadow Maps http://cafe.naver.com/shader.cafe http://ohyecloudy.com 2010.01.25

[shaderx7] 4.1 Practical Cascaded Shadow Maps

Embed Size (px)

DESCRIPTION

 

Citation preview

Page 1: [shaderx7] 4.1 Practical Cascaded Shadow Maps

shaderX7 4.1

Practical Cascaded Shadow Maps

http://cafe.naver.com/shader.cafe

http://ohyecloudy.com 2010.01.25

Page 2: [shaderx7] 4.1 Practical Cascaded Shadow Maps

CSM

Page 3: [shaderx7] 4.1 Practical Cascaded Shadow Maps

Overview

• CSM 기본 구현에 대핚 내용이 아니다.

• 실무에 적용할 때, 생기는 문제들 해결 방법을 알려주는 글.

Page 4: [shaderx7] 4.1 Practical Cascaded Shadow Maps

flickering of shadow quality storage strategy

non-optimized split selection

correct computation of texture coordinates

filter across splits

Page 5: [shaderx7] 4.1 Practical Cascaded Shadow Maps

• 그림자 품질이 다음 프레임에 급격히 변핚다.

• light, viewer 위치가 변하면 발생.

Page 6: [shaderx7] 4.1 Practical Cascaded Shadow Maps
Page 7: [shaderx7] 4.1 Practical Cascaded Shadow Maps

Exact Solution

• split을 감싸는 bounding sphere 사용

– shadow map size가 stable

– scale, offset 문제 해결

Page 8: [shaderx7] 4.1 Practical Cascaded Shadow Maps

// step 1 : calculate the light basis

vector3 direction = normalize(WORLD.getColumn3(2));

vector3 side = normalize(WORLD.getColumn3(1));

vector3 up = normalize(WORLD.getColumn3(0));

// update all splits

for (int i = 0; i < numSplits; i++)

{

// Step 2 : Calculate the bounding sphere “bs” of each split

// Step 3 : update the split-specific view matrix

// ‘matrixView[i]’ and projection matrix ‘matrixProjections[i]’

}

Page 9: [shaderx7] 4.1 Practical Cascaded Shadow Maps

// step 1 : calculate the light basis

direction, side, up

// update all splits

for (int i = 0; i < numSplits; i++)

{

// Step 2 : Calculate the bounding sphere “bs” of each split

Sphere bs = CalculateBoundingSphere(split[i]);

vector3 center = INVWORLDVIEW * bs.getCenter();

float radius = bs.getRadius();

// adjust the sphere’s center to make sure it is transformed into the center of the shadow map

float x = ceilf(dot(center,up)*sizeSM/radius)*radius/sizeSM;

float y = ceilf(dot(center,side)*sizeSM/radius)*radius/sizeSM;

center = up*x + side*y + direction * dot(center, direction);

// Step 3 : update the split-specific view matrix

// ‘matrixView[i]’ and projection matrix ‘matrixProjections[i]’

}

Page 10: [shaderx7] 4.1 Practical Cascaded Shadow Maps

// step 1 : calculate the light basis

direction, side, up

// update all splits

for (int i = 0; i < numSplits; i++)

{

// Step 2 : Calculate the bounding sphere “bs” of each split

// Step 3 : update the split-specific view matrix

// ‘matrixView[i]’ and projection matrix ‘matrixProjections[i]’

matrixProjections[i]

= MatrixOrthoProjection(

-radius, radius, radius, -radius, near, far);

vector3 origin = center – direction * far;

matrixView[i] = MatrixLookAt(origin, center, up);

}

Page 11: [shaderx7] 4.1 Practical Cascaded Shadow Maps
Page 12: [shaderx7] 4.1 Practical Cascaded Shadow Maps

Approximated Solution

• sphere를 사용하니 그림자 텍스쳐 낭비 심함. – bounding-box 사용. – scale, offset 직접 계산. – 구한 scale, offset을 여러 프레임에 걸쳐 부드럽게 보간.

Page 13: [shaderx7] 4.1 Practical Cascaded Shadow Maps

// calculate scale values

float scaleX = 2.0f / (maxX – minX);

float scaleY = 2.0f / (maxY – minY);

// the scale values will be quantized into 64 discrete levels

float scaleQuantizer = 64.0f;

// quantize scale

scaleX = scaleQuantizer / ceilf(scaleQuantizer / scaleX);

scaleY = scaleQuantizer / ceilf(scaleQuantizer / scaleY);

// calculate offset values

1,0,0

scaleX

izerscaleQuantceilizerscaleQuantscaleX

0

scaleX

izerscaleQuantceil

izerscaleQuantizerscaleQuant

Page 14: [shaderx7] 4.1 Practical Cascaded Shadow Maps

// calculate scale values

// calculate offset values

float offsetX = -0.5f * (maxX + minX) * scaleX;

float offsetY = -0.5f * (maxY + minY) * scaleY;

// quantize offset

float halfTextureSize = 0.5f * sizeSM;

offsetX = ceilf(offsetX * halfTextureSize) / halfTextureSize;

offsetY = ceilf(offsetY * halfTextureSize) / halfTextureSize;

Page 15: [shaderx7] 4.1 Practical Cascaded Shadow Maps

flickering of shadow quality

storage strategy non-optimized split selection

correct computation of texture coordinates

filter across splits

Page 16: [shaderx7] 4.1 Practical Cascaded Shadow Maps

• Texture arrays

– Direct3D 10.1 이상

• Cube maps

–항상 6개 shadow map 텍스쳐

• Multiple textures

– CSM 마다 텍스쳐 핚 장씩.

• Texture atlas

Page 17: [shaderx7] 4.1 Practical Cascaded Shadow Maps

flickering of shadow quality

storage strategy

non-optimized split selection

correct computation of texture coordinates

filter across splits

Page 18: [shaderx7] 4.1 Practical Cascaded Shadow Maps

• fragment P

– Z 값을 따지면 split-1

– X,Y 값을 따지면 split-0

– 품질을 극대화 하려면 split-0를 사용하는 게 맞다.

Page 19: [shaderx7] 4.1 Practical Cascaded Shadow Maps

float shadow = 0.0; // get the potential texture coordinates in the first shadow map float4 texcoord = mul(matrixWorld2Texture[0], PS_Input.world_position); // projective coordinates texcoord.xyz = texcoord.xyz / texcoord.w; // 1st SM x,y:[0,0.5] if (max(abs(texcoord.x – 0.25), abs(texcoord.y – 0.25)) >= 0.25) { texcoord = mul(matrixWorld2Texture[1], PS_Input.world_position); texcoord.xyz = texcoord.xyz / texcoord.w; // 2nd SM, x:[0,0.5], y:[0.5,1] if(max(abs(texcoord.x – 0.25), abs(texcoord.y – 0.75)) >= 0.25) { shadow = 1.0; } } if (shadow != 1.0) { shadow = tex2D(samplerAtlas, texcoord); }

Page 20: [shaderx7] 4.1 Practical Cascaded Shadow Maps
Page 21: [shaderx7] 4.1 Practical Cascaded Shadow Maps

flickering of shadow quality

storage strategy

non-optimized split selection

correct computation of texture coordinates

filter across splits

Page 22: [shaderx7] 4.1 Practical Cascaded Shadow Maps

기존 방법

• Vertex Shader

– split 번호를 판단 X.

–가능핚 모든 정보를 PS에 넘긴다.

• Pixel Shader

– split 번호를 계산.

–해당하는 shadow maps에서 depth 샘플링.

Page 23: [shaderx7] 4.1 Practical Cascaded Shadow Maps

struct VS_OUTPUT { float4 position : POSITION; float4 tex0 : TEXCOORD0; // CSM0 float4 tex1 : TEXCOORD0; // CSM1 float4 tex2 : TEXCOORD0; // CSM2 } // VS float4 posWorldSpace = mul(VSInput.position, WORLD); VSOutput.position = mul(posWorldSpace, matrixViewProj); VSOutput.tex0 = mul(posWorldSpace, matrixTexture[0]); VSOutput.tex1 = mul(posWorldSpace, matrixTexture[1]); VSOutput.tex2 = mul(posWorldSpace, matrixTexture[2]); // PS float shadow; int split = .....; if (split < 1) shadow = tex2DProj(samplerCSM0, PSInput.tex0); else if (split < 2) ...

Page 24: [shaderx7] 4.1 Practical Cascaded Shadow Maps

개선된 방법

• Vertex Shader – split 번호를 판단 X

– 월드 좌표를 PS에 넘겨준다.

• Pixel Shader – split 번호를 계산

– world texcoord 트랜스폼

– 해당하는 shadow maps에서 depth 샘플링.

• 수학적으로 틀렸으나 빠르고 눈에 띄는 artifacts X

Page 25: [shaderx7] 4.1 Practical Cascaded Shadow Maps

struct VS_OUTPUT { float4 position : POSITION; float4 tex0 : TEXCOORD0; } // VS VSOutput.position = mul(VSInput.position, WORLDVIEWPROJ); VSOutput.tex0 = mul(VSInput.position, WORLD); // PS float shadow; float4 texCoords; int split = ...; if (split < 1) { texCoords = mul(PSInput.tex0, matrixWorld2Texture[split]); shadow = tex2DProj(samplerCSM0, texCoords); } else if (split < 2) ...