오다기리 박의 알고리즘 노트

[셰이더 프로그래밍 입문] 10. 그림자매핑 본문

컴퓨터 그래픽스/쉐이더

[셰이더 프로그래밍 입문] 10. 그림자매핑

오다기리 박 2018. 7. 1. 23:41

10. 그림자매핑

  • 그림자 생성단계:

    • 렌더링 결과(빛을 가로막는 첫 번째 물체의 깊이)를 저장할 렌더타깃을 정해준다.

    • 카메라를 광원의 위치에 두고 물체들을 그린다.

    • 픽셀셰이더에서 빛으로부터 현재 픽셀까지의 깊이를 반환한다.

  • 그림자 적용단계:

    • 렌더링 결과(일반 장면 렌더링)를 화면(백버퍼)에 저장한다.

    • 카메라를 눈의 위치에 두고 물체들을 그린다.

    • 빛으로부터 현재 픽셀까지의 깊이를 그림자맵에 담겨있는 결과와 비교한다. (현재깊이>그림자맵의깊이) 라면 그림자를 씌운다.


그림자매핑에서 발전한 고급 기법

  • 캐스케이드 그림자맵 : 그림자매핑의 해상도 문제점을 해결하기 위하여 영역별로 여러 개의 그림자맵을 만드는 기법.

  • 퍼센티지 클로저 필터링 : 그림자의 외곽선을 부드럽게 필터링 해주기 위해 사용.

  • 배리언스 그림자맵 : 하드웨어 텍스처 필터링이 가능하도록 깊이 값을 저장하는 방법.



▶렌더몽키 코드 

- 버텍스 셰이더
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
struct VS_INPUT 
{
   float4 mPosition: POSITION;
   float3 mNormal: NORMAL;
};
 
struct VS_OUTPUT 
{
   float4 mPosition: POSITION;
   float4 mClipPosition: TEXCOORD1;
   float mDiffuse: TEXCOORD2;
};
 
float4x4 gWorldMatrix;
float4x4 gLightViewMatrix;
float4x4 gLightProjectionMatrix;
 
float4 gWorldLightPosition;
 
float4x4 gViewProjectionMatrix;
 
VS_OUTPUT vs_main( VS_INPUT Input )
{
   VS_OUTPUT Output;
  
   float4x4 lightViewMatrix = gLightViewMatrix;
 
   float3 dirZ = -normalize(gWorldLightPosition.xyz);
   float3 up = float3(0,1,0);
   float3 dirX = cross(up, dirZ);
   float3 dirY = cross(dirZ, dirX);
   
   lightViewMatrix = float4x4(
      float4(dirX, -dot(gWorldLightPosition.xyz, dirX)),
      float4(dirY, -dot(gWorldLightPosition.xyz, dirY)),
      float4(dirZ, -dot(gWorldLightPosition.xyz, dirZ)),
      float4(0001));
   lightViewMatrix = transpose(lightViewMatrix);
 
   float4 worldPosition = mul(Input.mPosition, gWorldMatrix);
   Output.mPosition = mul(worldPosition, gViewProjectionMatrix);
 
   Output.mClipPosition = mul(worldPosition, lightViewMatrix);
   Output.mClipPosition = mul(Output.mClipPosition, gLightProjectionMatrix);
   
   float3 lightDir = normalize(worldPosition.xyz - gWorldLightPosition.xyz);
   float3 worldNormal = normalize(mul(Input.mNormal, (float3x3)gWorldMatrix));
   Output.mDiffuse = dot(-lightDir, worldNormal);
   
   return Output;
}
cs


- 픽셀 셰이더
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
sampler2D ShadowSampler;
float4 gObjectColor;
 
struct PS_INPUT
{
   float4 mClipPosition:TEXCOORD1;
   float mDiffuse:TEXCOORD2;
};
 
float4 ps_main(PS_INPUT Input):COLOR
{
   float3 rgb=saturate(Input.mDiffuse)*gObjectColor;
   
   float currentDepth=Input.mClipPosition.z/Input.mClipPosition.w;
   
   float2 uv=Input.mClipPosition.xy/Input.mClipPosition.w;
   uv.y=-uv.y;
   uv=uv*0.5+0.5;
   
   float shadowDepth=tex2D(ShadowSampler,uv).r;
   
   if(currentDepth>shadowDepth+0.0000125f)
   {
      rgb*=0.5f;
   }
   return float4(rgb,1.0f);
}
cs

- 결과

그림자 기법을 평정한 그림자매핑입니다. 다음과 같은 순서로 셰이더를 만듭니다.

  1. 그림자생성단계
    • 렌더링 결과(빛을 가로막는 첫 번째 물체의 깊이)를 저장할 렌더타겟을 정해 준다.
    • 카메라를 광원의 위치에 두고 물체들을 그린다.
    • 픽셀셰이더에서 빛으로부터 현재 픽셀까지의 깊이를 반환한다.
  2. 그림자 적용단게
    • 렌더링 결과(일반 장면 렌더링)을 화면(백버퍼)에 저장한다.
    • 카메라를 눈의 위치에 두고 물체들을 그린다.
    • 빛으로부터 현재 픽셀까지의 깊이를 그림자맵에 담겨있는 결과와 비교한다. 현재 깊이가 그림자맵의 깊이보다 크면 그림자를 씌운다.

img