Post Processing을 정리하겠다.
내가 정리하려는 Post Processing은
- Gamma Correction
- HDR
- Bloom
- SSAO
이다.
4개다 한 번씩 깊게 공부했지만, 다시 까먹었다. 항상 어려운 것은 여러번 공부하면서 정리해야한다. 나는 위의 4개를 명확하게 누군가에게 설명할 수 있도록 정리할 것이다. 내용은 길지 않으면서 간단하고 명확하게!
Gamma Correction
1) Gamma Correction을 하는 이유 :
- 사람이 스크린을 통해 이미지를 보았을 때 보기 편하게 하기 위해.
- 카메라가 사진을 찍었을 때 사람이 본 것처럼 만들기 위해.
- 우리가 스크린을 통해 가상 세계를 볼 때 그 가상 세계의 컬러가
우리가 현실에서 보는 컬러들과 비슷하게 만들기 위해.
2) 사람이 컬러를 인식하는 방법 :
- 어두운 컬러를 더 잘 구별해낸다.
- 밝은 컬러를 잘 구별해내지 못한다.
3) 카메라가 컬러를 나타내는 방법 :
- 카메라는 제한된 정밀도를 갖는다
- 카메라는 자신이 가진 정밀도 안에서 컬러 값을 받아낼 것이다.
4) 컴퓨터가 컬러를 나타내는 방법:
- 컴퓨터도 제한된 정밀도를 갖는다
- 카메라에 찍힌 이미지를 사람이 봤던 것 처럼 컴퓨터에 나타내야 한다.
5) 카메라에 찍힌 이미지를 컴퓨터에 저장할 때 사람이 봤던 장면처럼 저장하는 방법:
- 사람이 봤던 장면처럼 보는데, 우선 우리는 또한 다시 모니터에서 그 장면을 본다.
- 모니터는 카메라에 저장된 컬러 값을 보통 2.2승을 곱한 그래프로 나타낸다.
- 즉, y = x 그래프의 정의역 0 ~ 1 구간에 2.2승을 곱한 그래프를 보아라.
- 카메라의 입력 값(x)가 2.2승이 곱해져서 모니터에 우리에게 표현된다.
- y = x 그래프의 정의역 0 ~ 1구간에 1/2.2승을 곱한 그래프를 보아라.
- 그 그래프는 x가 작을 때, y의 변화도가 크다 -> 사람이 컬러를 인식하는 그래프
- 그래서 카메라에 찍힌 값을 1/2.2승을 곱하여 저장하자 (Gamma Encoding)
- 그렇다면 Gamma Encoding을 하면 data가 어두운 영역의 구분이 된 채로 저장이 되고,
- 그 구분이 되는채로 2.2승이 되어 모니터에 표현된다.
마지막 5번이 잘 이해가 되질 않았는데, 페이스북의 답변을 통해 예시를 통해 이해했다.
Camera Raw Input : [0, 1, 2, ...., 99]
Compute File Format이 10단위로 표현할 수 있다고 가정 :
[0, 1, 2, ... 11, 12, 13, ...21, 22, 23 ...., 99]
[0, 0, 0, .. 1, 1, 1, .........2, 2, 2 ..........., 9]
이렇게 저장 한다면 사람이 컬러를 인식하는 어두운 색 구분이 잘 안될 거임.
정밀도가 많이 떨어짐 게다가 그 변환된 것을 컴퓨터에 출력한다 헀을 때 (컴퓨터 Gamma가 2라고 가정)
[0, 0, 0,,, 1, 1, 1, ....... 4, 4, 4,............. 81] 이 된다.
그런데 컴퓨터 Gamma에 맞추어 그 역수를 통해 Camera Raw Input을 변형한다면
(1/2 승 한다고 가정)
[0, 1, 2, ... 11, 12, 13, ...21, 22, 23 ...., 99]
[0, 1, 1, .. 3, 3, 3, .........4, 4, 4 ..........., 9]
이렇게 저장 한다면 좀 더 사람이 컬러를 인식하는 방법처럼 어두운 색 구분이 잘된다.
그리고 마지막으로 그 변환된 것이 컴퓨터로 출력한다 했을 때
[0, 1, 1,... 9, 9, 9, ....... 16, 16, 16, ........ 81]
좀 더 어두운 영역이 잘 표현된다.
6) 그렇다면 그래픽스에서?
우리가 텍스쳐를 불러오지 않고, 그냥 코드에서 숫자로 컬러 값을 가지고 놀고, 1/2.2승을 취하면 된다. 그런데, 텍스쳐를 불러올 때 문제가 생긴다. 왜냐면 위에서 설명했듯이, 이미지는 1/2.2승이 이미 취해져있다.
그래서 우리가 1/2.2승을 그대로 취한다면, 두 번 한 거나 마찬가지이기 때문에, 텍스쳐가 더 밝게 보인다.
따라서 OpenGL에서는 SRGB 포맷으로 이미지를 불러오면 2.2승을 한 번해서 linear로 바꾸어서 우리가 모든 처리를 한 다음에 마지막 post-processing 쉐이더에서 1/2,2승을 하면 우리가 원하는대로 어두운 영역이 사람이 보는 것처럼 잘 구분이 될 것이다.
HDR
- 컴퓨터에서 컬러를 표현하는 것은 [0 ~ 1]으로 제한되어 있다.
- 그런데 lighting equation에는 제한이 없다.
- 그래서 이러한 0 ~ 1의 제한을 풀고 더 넓은 color range를 가져서 color를 표현하고 싶다.
- 사람의 눈이 밝은 곳에서나 어두운 곳에서 시간에 따라 눈을 적응하여 디테일을 구분한다.
- HDR도 이렇게 작동한다.
- 모든 lighting equation의 범위에 제한을 두지 않고 계산한다. (HDR 범위에 있다)
- 마지막에 0 ~ 1의 범위로 LDR 범위로 바꾼다.
- HDR -> LDR를 Tone Mapping이라고 부른다.
- 이러한 HDR은 우리에게 더 많은 디테일을 보존하고, light source의 intensity를 명시할 수 있게 해준다.
- Reinhard Tone Mapping이 가장 간단한 tone mapping이다.
Bloom
- 최종 FrameBuffer의 color buffer에서 밝은 color를 골라내고 아니면 blakc으로 둔다
- 밝은 컬러를 골라내는 방법은 한 vec3에 rgb마다 가중치를 두어서 dot으로
scalar값의 brighness를 구한다
- brightness의 threshold를 설정하여 그것을 넘기면 밝은 fragment라 가정하고
그 원래 fragment color를 가져온다
- 가져온 bright fragment color buffer를 가지고, Two Pass Gaussian Blur를 실행한다.
- 처음에는 bright color가지고 horizontal
- 이전 실행된 결과에 vertical
- horizontal <-> vertical 이렇게 왔다갔다 명시된 iteration 수 만큼 실행
- 최종적으로 Blur가 된 결과를 다시 최종 Framebuffer color buffer에 더하면 Bloom 효과 완성
SSAO
* SSAO 방법
1) fragment를 둘러싸는 depth values들을 기반으로 occlusion factor를 구함
2) 그 occlusion factor가 fragment의 ambient lighting component를 없애는데 사용됨
* 1)의 방법 분해
- fragmet position을 둘러싸는 sphere sample kernerl에서 여러 depth samples를 취함
- 그 샘플 각각과 현재 fragment의 depth value를 비교
- fragment의 depth보다 더 높은 depth value를 가진 샘플들의 개수가 occlusion factor를 나타낸다.
* 1)의 방법으로 부터 오는 장단점
- 효과의 quailty와 정밀도가 우리가 취하는 샘플들의 개수와 관련
- 샘플 개수가 너무 작으면 정밀도가 떨어져 banding이라는 artifact가 나타남
- 샘플 개수가 너무 많으면 성능이 떨어짐
* 샘플 커널을 취하는 방법
- sample kernel에 랜덤 값을 도입하여 샘플의 양을 줄일 수 있다
- sample kernel를 랜덤하게 회전하여, 더 작은 양의 샘플의 양으로 좋은 퀄리티 결과를 얻 을 수 있다.
- 이것은 비용이 조금 있는데, 랜덤은 눈에 띌만한 noise pattern을 주기 때문에
- 그래서 이 noise pattern을 해결하기 위해 그 결과를 다시 blur한다.
- Crytek이 개발한 SSAO는 sample을 취할 때 sphere을 사용해서 했는데, 이것은
flat wall이gray하게 보이게 만든다. 왜냐하면 주변 geometry depth를 다 취하기 때문에
- 그래서 표면의 normal를 따라서 hemisphere를 사용해서 sample을 취할 것이다
- 이렇게 hemisphere를 사용해서 하면 fragment 밑에 깔려있는 다른 도형들을 고려하지 않음. 그리고 이것은 ambient occlusion의 gray-feel을 줄이고 좀 더 현실적인 결과를 만든다.
* Coordinate Space
- 나는 현재 World Space에서 Light Calculation을 하고 있다.
- 그런데, SSAO는 Screen-Space에서하기 때문에, View Space에서 계산을 한다.
- 왜냐하면 Viewer의 위치와 바라보는 방향을 기준으로, 그 해당 pixel의 depth를 비교하기 때문이다.
- 나는 World Space Light Calculation을 다시 View Space Light Calculation으로 바꿀 수
없기 때문에 다음과 같은 것을 생각했다.
- Gbuffer는 똑같이 World Space 좌표를 받아온다.
- SSAO Shader는 View Space에서 작업을한다.
그 말은 World Space 좌표들을 다 View SPace로 전환하는 작업이 필요하다는 것이다.
따라서 Position과 Normal를 View Space로 바꿔야 하는 행렬 연산이 추가 된다.
이것은 Texture Image에 따라서 연산 가중치가 이제 더 높아지게 될 것이다.
- 그리고 나의 최종적인 Deferred Lighting은 어차피 각 픽셀별로 접근하고, 해당 픽셀에 대해서 ambient occlusion이 결정되었기 때문에, 그 값을 사용하면 된다.
댓글 없음:
댓글 쓰기