
Blinn-Phong 모델을 배웠었습니다.
Ambient + Diffuse + Specular의 합으로 정의가 되었었죠.
앞서선 는 전부 사전 정의된 constant로 잡았는데,
이제는 Image를 이용해 각 값들을 설정할 수 있습니다.
를 대체하는 Diffuse Mapping,
전체 Specular Component를 image로 바꾸는 Environment Mapping,
을 사전 정의된 이미지에 맞춰 바꾸는 Bump Mapping,
를 바꾸는 Specular Mapping (Rare)가 있습니다.

이 반짝거리는 마리오를 봅시다.
얘는 이동하면서 reflection pattern이 동시에 이동합니다. 그래서 반짝이는 것처럼 느껴지죠.
즉, View Vector에 대한 reflector에 따라서 environment의 색이 surface에 매핑됩니다.
Environment mapping은 물체가 주변 환경을 반사하는 것 처럼 보이는 것이 특징이기 때문에,
360도 omnidirectional image를 사용하여 매핑합니다.
주변 환경을 미리 찍어놓은 이미지를 이용한다 이 뜻이에요.
이걸 구현한 방식으로는
Sphere Mapping, Cube Map, Paraboloid Mapping이 있습니다.

360도 파노라마 이미지를 구형으로 매핑시킨 이미지입니다.

이런 식으로 주변 환경이 사악 비춰지게 매핑시킬 수 있죠.
단점으로는, Sphere의 반대편이 매핑되지 않는다는 점입니다.
만약 카메라를 주전자의 반대편으로 돌린다면 아무것도 안 보이겠죠..

Viewer 기준으로 6면 큐브맵을 만들어서, 그 텍스처를 surface에 갖다 붙입니다.
각 Frame마다 6개의 face를 rendering해서 dynamically generated 한 효과도 낼 수 있습니다.

Paraboloid (포물면)을 이용하여 반사된 내용을 기록합니다.
총 2장만 있으면 360도의 내용을 다 기록할 수 있기 때문에 더 쌉니다.

diffuse map된 surface를 보면 되게 plain합니다.
우리가 complex한 geometry를 써도 되지만, 너무 costly하죠.
사전에 8bit로 [-1,1]인 normal vector를 [0~255]로 encoding한 normal map을 갖고 있다면,
그걸 바탕으로 texture를 입히는게 bump mapping입니다.

normal map만 있다면 4M개의 삼각형을 단순화해도
그 색과 질감을 거의 잃지 않네요,

Bump Map은 surface의 height variation을 기록한 작은 필드입니다.

다음과 같이 눈을 속이는 게 목표입니다. 실제로는 flat surface지만, normal vector만 가져와서 붙여버리는거죠.
Normal Map은 Bump Map으로부터 만들어진 map입니다.
height variation 뿐만이 아니라, normal vector가 RGB Scale로 기록되어 있습니다.

기본 normal vector인 (0,0,1)이 (127,127,255)로 기록되어 있죠.
Bump Map으로 pixel간의 height 정보를 주면, Normal Map을 미리 계산합니다.

이렇게 Height Map 내에스 인접한 pixel끼리 이어서, 기울기를 유추한 후에
해당 pixel에서의 Normal값을 얻어내는 것이죠.
Bump Mapping은 눈을 '속이는', Geometry를 바꾸지 않는 Mapping이었지만,
Displacement Mapping은 'Geometry'를 진짜로 바꾸는 Mapping입니다.

Height map을 먼저 읽어온 후, 실제 Geometry를 Relocate 합니다.

Displacement Mapping은 Mesh의 vertex들을 height에 맞게 밀어내는 거기 때문에,
기존 mesh가 촘촘하지 않으면 좀 못생겨집니다. 당연하죠.
그렇기 때문에 tessellation shader를 이용하여, input mesh를 더 촘촘하게 쪼개고,
거기서 실제 geometry를 변경하는 작업이 필요합니다.

다음과 같이 input patch가 띄엄띄엄한데, 이걸 매우 잘게 쪼갠 후,
displacement mapping을 집어넣으면 정말 실제적인 geometry가 나옵니다.
Shadow란, lights가 occluders에 의해 방해되는 어두운 area를 말합니다.
하나의 점형 light source가 있다면,

이런 형태가 shadow죠.
Shadow Rendering에는 2가지 approach가 쓰입니다.

Geometry-based, 즉 Geometry를 가지고 연산하기 때문에 pixel 해상도가 중요하지 않습니다.
또, 빛이 닿는 곳이면 계산이 가능합니다. 광원 범위따위는 중요하지 않습니다.
다만, 성능 하락이 심해서 scene scalability가 낮구요,
GPU가 화면에 그리는 픽셀 수인 Fill-Rate에 따라 제한됩니다.
Image-based의 경우, 엄청 빠릅니다.
Shadow를 Image처럼 저장해서 쓰면 되기 때문이죠.
또, Post-processing (렌더링 후 처리)로 추가가 가능합니다.
다만, 완벽한 설정을 잡기 (tweak)가 매우 힘듭니다.
Resolution이 중요하구요, 안 그러면 aliasing 문제가 생깁니다. (Shadow aliasing)
또는 자기 그림자에서 생기는 점 찍힘 현상 (Acne)이 생깁니다.

1단계로,
'빛의 시점'에서 바라본, Depth map을 그리는데 그게 Shadow Map입니다.
이 때 광원과 Object가 가까울 수록 Grayscale이 어두워집니다.

2단계로,
각 fragment에 대해 Shadow Factor를 계산합니다.
이 때 (shadow map)과 (current fragment depth value)의 값을 비교,
라면 에는 그림자가 지지 않고,
라면 p에는 그림자가 져야 합니다
단 이렇게 간단하게 Shadow Map을 구현하면,
이런 문제가 생깁니다.

Point Light Source가 만들면 Hard Shadow였는데요.
범위광 (Area Light Source)가 그림자를 만들면 Soft Shadow가 됩니다.

엄청 잘 가려지는 Umbra가 생기고, 애매하게 가려지는 Penumbra가 생기죠.

가장 간단한 Approach로는, Area Light를 Point Light 여러개로 샘플링해서,
각각에 대한 Shadow Map을 그려서 Shadow를 그린 후,
그것들을 다 합치는거죠.
Light Sample이 많을수록 엄청 costly해지구요.
그래서 대안으로
이 소개됩니다.

중앙 pixel로 한번만 shadow map test를 하는게 아니라,
4개의 sample로 쪼개서 PCF (Percentage-Closer Filtering)을 진행합니다.
그럼 Shaded pixel이 되겠죠?

Light Source와 Occluder 사이 관계에 의해 Penumbra가 결정되겠죠.
이 Penumbra의 크기에 따라서 PCF의 Kernel Size를 정하는겁니다.

Soft-Shadow approximation (PCSS)를 적용했더니 훨씬 이뻐졌네요.
Shadow Mapping이 안고 가야 할 숙제들입니다.
Object, Light, Camera Motion에 Robust해져야 합니다.
물체가 '움직여도' 그림자 경계가 유지되어야 하죠.
성능 문제죠?
또, Light Bleeding / Flickering이 없어야 합니다.
Shadow Acne를 생각하면 됩니다.
모든 Light Type과 Dynamic Geometry에도 작동을 해야 하겠습니다.
좋은 Quality와 성능이 동시에 나와야겠습니다.