Shadow
- 빛이 있을 때의 발생하는 그림자를 계산해야 한다
- 왜 그림자를 계산해야 할까?
- 그림자가 없으면 물체가 떠있는지, 바닥에 붙어있는지 모른다 → 사실적이지 않은 느낌
- 빛이 어디에 있는지 알 수 있게 된다 → Spatial 인지를 도와준다
Shadow 계산을 위해 필요한 요소
- Light Source
- Shadow Creators, Casters : 빛을 받아서 그림자를 만들게 되는 물체
- Shadow Receiver : 실제 그림자를 받는 물체
Shadow Model : Light Type
- Umbra : 빛을 받지 못하는 부분
- Directinal Light : 한 점을 기준으로 평행하게 빛이 들어오는 것
- Point Light : 한 점으로부터 여러 각도로 빛을 내보낸다
- Area Light : 한 점이 아닌 면적을 가진다
- 빛이 area에서 내려오거나 옆으로 가면서 모든 방향에서 빛이 온다
- PenUmbra : 어느 부분에서는 빛을 받고, 어느 부분에서는 못받으면서 선명하지 않은 애매한 그림자가 생긴다. → 흐릿한 그림자가 생긴다
Shadow Model
- Soft Shadow : PenUmbra 가 있는 그림자
- Hard Shadow : PenUmbra 없이 Umbra 만 있어서 선명한 그림자
Shadow Computation
- 어떤 것을 고려해야 할까?
- Shadow Castors : 그림자의 기본 모양을 결정한다
- Shadow Receivers : 받는 물체가 단순한 평면이 아니라 구나 표면이 거친 면일수도 있다
- Light Sources : 빛에따라 생기는 그림자가 달라진다
- Straight-forward approach
-
Ray Casting : 카메라에서 빛을 쏴서 물체의 어떤 부분에 닿는지 표시하는 방법
→ Direct 계산엔 좋지만 Indirect에는 안좋다
-
Ray Tracing 을 통해 모든 광원에 대해 Ray를 쏴서 스크린에 도달하는 점들을 표시한다. 빈 공간을 Interpolation 하는 방법
→ 엄청 많은 Ray를 쏴야 한다
⇒ 게임은 빨라야하는데 연산이 너무 많다. 더 좋은 방법이 없을까?
Shadow 계산 대안
1) 💡Separate Object
- 오브젝트를 빛으로부터 오는 공간에 잘게 쪼개서 붙여넣는 방식
- 가장 빠르게 그릴수 있는 방법이다
- 단점
- 빛이 어딨는지에 따라 모양이 다르게 나온다. 하나의 모양으로만 물체를 만들기 어렵다
- 많은 물체를 빛이 바뀌는 경우에 따라 계속 새로 그려야 한다 (빛이 안 바뀌는 경우에는 좋다)
→ Baking : 미리 화면에 그림자 값을 저장해놓는다. 정적인 경우에 효율적인 방법
- 언리얼에서는 Static 물체에 대해서는 Baking을 해놓는다
- 또는 텍스쳐에 그림자 값을 저장해놓기 → 이 경우도 움직이는 케이스 처리 못함
2) 💡 Shadow Volume
- 광원으로부터 가려지는 물체의, 그림자가 생길 수 있는 영역
- 캐스터에 의해 생기는 그림자 영역
- Shadow Volume 안에 있는 영역에는 그림자가 생긴다
- 일반적으로 좋은 결과를 보여준다. but 계산이 복잡하다
Stencil Buffer
- 계산을 덜 복잡하게 할 수 없을까?
- Stencil Buffer를 사용하자!
- Stencil Buffer : Mask. 0과 1을 적어두면 1에 해당하는 부분만 그릴 수 있다 → Shadow가 생길 부분에 Shadow를 위한 특정한 값을 넣는다
- 시점에서 특정 지점까지의 직선과 SV 의 교점이 1개면 그림자 안에 있다 (2차원)
- 시점에서 특정 지점까지의 직선과 frontfacing 평면의 내적이 음이면 빛을 못받으므로 그림자를 넣는다. 양이면 Backfacing이므로 빛을 그린다
- Stencil Buffer 에 Shadow 그릴 부분을 그린 후, SV에 덧붙여서 그리는 여러 단계를 거쳐야 한다
3) 💡 Shadow Map
- 내가 그리고자하는 점 P가 광원으로부터 더 가까운 점에서 만난다면 그림자 지점에 있는 것.
- 특정 픽셀 P로부터 광원까지 중간에 어떤 물체가 있다면 보이지 않는 것
- Shadow map 에 그림자를 미리 그려놓는다.
- 프로세스
- 광원의 입장에서 그림을 한번 그린후 , 픽셀의 depth를 저장한다
- 시점의 입장에서 다시 그림을 그리고, 이미 그려진 depth와 비교해서 그림자인지를 판단한다
- 장점 : 쉽고, 빠르다
- 단점
-
해상도 문제 : 픽셀단위로 처리하면서, 많은 점이 한 픽셀로 접근하는 경우 그림자가 너무 깨진다.
-
그림자가 깨끗하게 나타나지 않는다
→ 요즘은 Shadow Volume 방식, Raycasting 방식 을 사용한다
게임 엔진에서의 Shadow Mapping
- Shadow Technique 기법
- Shadow Volume : Stencil Shadow를 사용
- Shadow Texture : 텍스쳐에 그림자 값을 넣어 미리 Baking 된 값을 사용한다
- 실제 그림자 그리는 방식
- Modulative : 모든 그림자를 다 어둡게 표현한다
- Additive Light Masking : 광원에 대해서 그림자를 만들고 섀도우에 입혀지는 색상을 다 더해준다 → 광원마다 계산해야하므로 복잡해진다
Ogre3D 엔진에서의 Shadow
- 1) Shadow 방법을 설정한다
- 2) casting 할 물체와 하지 않을 물체를 지정한다
setCastShadows
를 사용해서 설정해준다
- 3) Recevier를 직접 설정한다
- 따로 Dynamic 한 경우 등을 계산하지는 않는다.
UE4 에서의 Shadow
- Static Lights
- static 물체에 대해서만 그림자를 만든다
- 실시간으로 계산하기엔 계산량이 많아서 미리 계산된 빛, 그림자 값을 사용한다
- Directional Lights
- dynamic object가 캐스팅한 빛은 그림자를 많이 만들지 않고, Shadow map을 응용한 방법을 사용한다
- Per Object Shadow
- 물체별로 실시간으로 빛을 계산함. Shadow map 문제로 근처에 있는 물체만
- Movable Lights
- 모든 물체에 대해 다 계산한다. SV 를 사용한다.
- map을 쓰면 cast면서 receiver인 물체의 계산은 SV를 쓰는 게 더 좋을수있다
- Movable 과 Directional 을 함께 쓴다