Shadow Mapping
- 그림자를 구현하기 위한 방법으로, 우선 빛의 위치와 방향이 모두 일치하는 가상의 카메라를 두어
카메라로 물체들을 찍은 뒤 물체들의 깊이값을 계산한 다음, 그 값을 텍스쳐에 저장해준다. (Shadow Map) - 이후 메인카메라로 찍은 물체들의 ViewPos를 World 좌표계로 변환한 뒤 가상 카메라의 VP 변환 행렬을 곱해 나온
ClipSpace에서 W나누기를 통해 가상 카메라 기준의 Projection 좌표를 구하고, [-1 ~ 1], [1 ~ -1] 사이로 나타난 좌표를 [0 ~ 1], [0 ~ 1] 사이의 uv좌표계로 변환시켜 준다.
다음으로 이 uv 좌표상의 깊이갚과 Shadow Map에 저장된 깊이값들을 비교하여 Shadow Map 상의 깊이가 더 가깝다라고 판별된다면, 그 곳에 그림자를 그려주게 되는 방식이다. - 이는 실제 세상에서 그림자가 지는 방식과 거의 동일하다고 생각할 수 있다.
만약 어떠한 물체보다 빛이 먼저 닿는 물체가 있다면, 그 뒤에 있는 물체는 빛을 받지 못하여 그림자가 지게 되는 방식이다. - Shadow Mapping은 Shadow Map Texture의 해상도가 커지면 커질수록 더욱 고퀄리티의 그림자를 만들어낼 수 있게 된다.
<shadow.fx>
더보기
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
|
#ifndef _SHADOW_FX_
#define _SHADOW_FX_
#include "params.fx"
struct VS_IN
{
float3 pos : POSITION;
};
struct VS_OUT
{
float4 pos : SV_Position;
float4 clipPos : POSITION;
};
VS_OUT VS_Main(VS_IN input)
{
VS_OUT output = (VS_OUT)0.f;
output.pos = mul(float4(input.pos, 1.f), g_matWVP);
output.clipPos = output.pos;
return output;
}
float4 PS_Main(VS_OUT input) : SV_Target
{
return float4(input.clipPos.z / input.clipPos.w, 0.f, 0.f, 0.f);
}
#endif
|
cs |
- Shadow Map의 Texture를 설정해주기 위한 Shader이다.
- 우선 VS_Main에서 각 정점에 WVP 변환 행렬을 곱하여 Clip Space 좌표를 구하고, 이를 clipPos에 저장한 뒤
PS_Main에서 clipPos의 z값에 w를 나눠준 값을 저장하여 RT Texture에 저장해준다.
<lighting.fx>
더보기
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
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
|
#ifndef _LIGHTING_FX_
#define _LIGHTING_FX_
#include "params.fx"
#include "utils.fx"
struct VS_IN
{
float3 pos : POSITION;
float2 uv : TEXCOORD;
};
struct VS_OUT
{
float4 pos : SV_Position;
float2 uv : TEXCOORD;
};
struct PS_OUT
{
float4 diffuse : SV_Target0;
float4 specular : SV_Target1;
};
// [Directional Light]
// g_int_0 : Light index
// g_tex_0 : Position RT
// g_tex_1 : Normal RT
// g_tex_2 : Shadow RT
// g_mat_0 : ShadowCamera VP
// Mesh : Rectangle
VS_OUT VS_DirLight(VS_IN input)
{
VS_OUT output = (VS_OUT)0;
output.pos = float4(input.pos * 2.f, 1.f);
output.uv = input.uv;
return output;
}
PS_OUT PS_DirLight(VS_OUT input)
{
PS_OUT output = (PS_OUT)0;
float3 viewPos = g_tex_0.Sample(g_sam_0, input.uv).xyz;
if (viewPos.z <= 0.f)
clip(-1);
float3 viewNormal = g_tex_1.Sample(g_sam_0, input.uv).xyz;
LightColor color = CalculateLightColor(g_int_0, viewNormal, viewPos);
// 그림자
if (length(color.diffuse) != 0)
{
matrix shadowCameraVP = g_mat_0;
float4 worldPos = mul(float4(viewPos.xyz, 1.f), g_matViewInv);
float4 shadowClipPos = mul(worldPos, shadowCameraVP);
float depth = shadowClipPos.z / shadowClipPos.w;
// x [-1 ~ 1] -> u [0 ~ 1]
// y [1 ~ -1] -> v [0 ~ 1]
float2 uv = shadowClipPos.xy / shadowClipPos.w;
uv.y = -uv.y;
uv = uv * 0.5 + 0.5;
if (0 < uv.x && uv.x < 1 && 0 < uv.y && uv.y < 1)
{
float shadowDepth = g_tex_2.Sample(g_sam_0, uv).x;
if (shadowDepth > 0 && depth > shadowDepth + 0.00001f)
{
color.diffuse *= 0.5f;
color.specular = (float4) 0.f;
}
}
}
output.diffuse = color.diffuse + color.ambient;
output.specular = color.specular;
return output;
}
#endif
|
cs |
- 실험을 위해서 Directional Light 에게만 그림자를 적용시켰기 때문에, 일부 코드만을 발췌해왔다.
- 우선 VS에선 바뀐점이 없고, PS의 그림자 부분부터 새로운 내용이 추가가 된다.
- 메인카메라로 찍은 물체들의 ViewPos에 View 변환 역행렬을 곱해줌으로써 World 좌표계로 변환한 뒤
가상 카메라의 VP 변환 행렬을 곱해 나온 ClipSpace를 추출해주고 ClipSpace의 z값에 w나누기 한 것을 depth로써 저장하며, ClipSpace의 xy값에 w나누기 하여 Projection 좌표를 추출해준 뒤
[-1 ~ 1], [1 ~ -1] 사이로 나타난 Projection 좌표를 [0 ~ 1], [0 ~ 1] 사이의 uv 좌표계로 변환시켜 준다. - 다음으로 Shadow Map을 참고하여 uv 좌표상의 깊이갚을 추출한 값과 depth에 저장된 값을 비교하여 Shadow Map 상의 깊이가 더 가깝다라고 판별된다면, 그 영역의 diffuse와 specular를 조정하여 텍스처를 그림자와 같이 변경시켜 주는 방식이다.
- 메인카메라로 찍은 물체들의 ViewPos에 View 변환 역행렬을 곱해줌으로써 World 좌표계로 변환한 뒤
결과
- 상당의 가장 우측 텍스쳐를 Shadow Map으로 설정하였고,
이 Shadow Map을 참고하여 구체의 상단에서 아래로 비추는 Directional Light의 그림자를 생성하였다.
'Grahpics' 카테고리의 다른 글
[Graphics] DirectX12 - Terrain (0) | 2022.02.17 |
---|---|
[Graphics] DirectX12 - Tessellation (0) | 2022.02.16 |
[Graphics] DirectX12 - Instancing (0) | 2022.02.14 |
[Graphics] DirectX12 - Particle System (0) | 2022.02.14 |
[Graphics] DirectX12 - Compute Shader (0) | 2022.02.13 |