HDR 구현
- HDR이란 화면 픽셀들의 평균 휘도 값을 구해서 LDR에서 표현할 수 없는 색의 범위를 표현할 수 있게 해주는 기술이다.
- 우리 게임에서 HDR을 구현하는 이유는 색의 표현 범위를 넓혀서 화면 후처리 기술들을 돋보이게 하기 위함이다.
- HDR은 총 세 단계의 과정을 거치는데, 그 과정은 아래와 같다.
1. 다운스케일 - 다운 스케일을 하는 이유는 이후 블러나 블룸 효과를 넣을 때 성능 향상을 위한 것도 있지만,
- 가장 중요한 목적은 HDR에 꼭 필요한 평균 휘도값을 구하는 것
- DownScale4to4, DownScale1024to4, DownScale4to1 등과 같은 함수들을 통해 다운스케일을 진행하고 평균 휘도값을 계산한다.
- 평균 휘도값을 구할 때 LUM_FACTOR라는 상수값을 이용해서 각 R, G, B 색상에 지정된 값을 곱해주는데, 각각의 값이 사람이 보기에 느끼는 밝기가 다르기 때문이라고 한다.
- 이 모든 과정은 컴퓨트 셰이더로 진행된다.
평균 휘도값을 왜 구해야 하는가?
- 예를 들어 LDR에서 어떤 물체에 강한 빛을 쏘는 경우 물체가 흐릿하게 보이는 등 세부 정보가 날아가는 현상이 생긴다.
- 이 때 평균 휘도값을 사용해서 너무 밝은 부분은 어둡게 조정하고, 너무 어두운 부분은 밝게 조정하는 식으로 화면의 전체적인 균형을 유지할 수 있다.
2. 블룸(Bloom) & 블러(Blur)
- 블룸 효과는 밝은 쪽에서 어두운 쪽으로 빛을 흘리는 효과라고 표현하곤 한다.
- 정확히는 이미지에서 밝은 부분을 추출해서 그 부분을 블러처리한 후 기존 이미지에 입히는 일련의 과정이다.
- 이미지의 밝은 부분은 상수버퍼로 threshold 값을 넘겨주어 특정 밝기 이상이면 RWTexture2D에 저장하는 식으로 구현된다.
- 홍정모 그래픽스 영상 강의를 들을때도 실습한 내용이지만 다운스케일되지않은 이미지를 블러처리를 통해 부드럽게 만들려면 50번의 반복 과정이 필요했지만 다운스케일한 후 블러처리하니 5번만에 50번한만큼의 부드러움이 완성되었다.
- 이 과정 또한 컴퓨트 셰이더를 통해 이루어진다.
3. 톤매핑
- HDR의 넓은 범위의 밝기를 LDR의 범위에 맞고 조정하여 출력한다.
- HDR 색의 범위를 화면에 출력할 수 없기 때문
- 아래 공식을 통해 HDR을 LDR로 변경해서 출력하게 된다.
// 현재 픽셀에 대한 휘도 스케일 계산
// LScale : 스케일 조정된 휘도 값
float LScale = dot(_HDRColor, LUM_FACTOR);
// HDR -> LDR로 변환 공식 : 픽셀의 휘도값을 원하는 중간 휘도 값으로 스케일을 조정한다.
LScale *= MiddleGrey / AvgLum[0];
LScale = (LScale + (LScale * LScale / LumWhiteSqr)) / (1.0 + LScale);
// 휘도 스케일을 픽셀 색상에 적용
return _HDRColor * LScale;
림 라이트 구현
- 림 라이트란 물체의 가장자리 부분에 밝은 테두리를 만드는 기술
- 디퍼드 렌더링 방식으로 각 정점에 저장된 Position, Normal 값이 저장된 텍스쳐를 이용해서 구현했다.
- 가장자리 부분을 밝게 만드려면 가장자리 부분을 인식하는 로직을 짜야 한다.
- 여기서 가장자리 부분이란 내가 보는 시점, 즉 카메라의 시점에서 가장자리이다.
- 각 정점들은 노멀벡터를 가지고 있는데, 노멀벡터의 방향은 가장자리일수록 그 정점이 카메라를 향하는 벡터와 수직에 가깝다는 특성을 이용해서 두 벡터의 내적으로 나오는 cos값을 통해 픽셀에 특정 색상을 더해주는 식으로 구현했다.
- 내가 G-BUFFER에 저장한 Position과 Normal은 모두 뷰 좌표계에서의 값들인데, 뷰 좌표계에서의 Position값은 카메라에서 물체를 향하는 벡터이므로, 이 값을 노말라이징하고 역벡터로 만들면 그대로 카메라를 향하는 노멀벡터가 된다.
그러므로 아래와 같이 쉽게 구현이 가능했다.
struct PS_OUT
{
float4 lim : SV_Target0;
};
PS_OUT PS_Main(VS_OUT _in)
{
PS_OUT output = (PS_OUT) 0;
float3 rimColor = float3(1.f, 1.f, 1.f);
float rimStrength = g_float_0;
float rimPower = g_float_1;
float4 viewPos = g_tex_0.Sample(g_sam_0, _in.uv);
float4 viewNormal = g_tex_1.Sample(g_sam_0, _in.uv);
float isViewNormal = viewNormal.x + viewNormal.y + viewNormal.z;
if (0.0f == isViewNormal)
discard;
float4 viewDir = -normalize(viewPos);
float rim = 1.f - dot(viewDir, viewNormal);
rim = smoothstep(0.f, 1.f, rim);
rim = pow(rim, rimPower);
output.lim = float4(rimColor * rim * rimStrength, 1.f);
return output;
}
- viewDir을 구한 후 1에서 viewDir과 viewNormal을 내적한 결과를 뺀 후 보기 좋게 적절한 처리를 해주었다.
내일 할 일
- 플레이어 공격 모션 수정
- 몬스터 균열 림라이트 적용
'게임 개발 > [D3D_Portfolio] DirectX3D 팀 포트폴리오 작업일지' 카테고리의 다른 글
16. 불러온 FBX 파일 데이터를 파일입출력을 통해 저장 (0) | 2023.08.25 |
---|---|
15. 맵 제작 작업 진행 (0) | 2023.08.22 |
13. 회의 및 짐벌락 문제 해결 & 몬스터 데드 셰이더 구현 (0) | 2023.08.15 |
12. 맵 제작 및 자잘한 버그 수정 (0) | 2023.08.13 |
11. Prefab 기능 구현 및 맵 제작 계획 수립 (0) | 2023.08.10 |
댓글