[DirectX11] 13일차 (스페큘러 구하기 / 림 라이트 구현하기)
어제는 디퓨즈(난반사)를 하프램버트 공식으로 구현해보았었다.
오늘할 것은
1. 디퓨즈를 이용해서 툰 쉐이더 느낌을 내보기.
2. 스페큘러(정반사)를 퐁, 블린 퐁 공식으로 구현해보기.
3. 림 라이트도 구현해보기. (조명 연산이랑은 상관 없음.)
툰 쉐이더 느낌 내보기
지난 12일차에 구현했던 디퓨즈는 하프램버트 방법을 사용했다.
이 그래프를 수식적으로 좀 갖고 놀면 툰 쉐이더 느낌을 낼 수 있다.
툰 쉐이더 느낌 내기
만화 느낌을 내기 위해서는 음영이 계단처럼 뚝뚝 끊어지는 느낌이 나야한다.
1. 디퓨즈 연산을 곱해서 길게 늘리고, (나는 3을 곱함.)
2. 올림. (내림, 반올림 이런 것들 중에서 올림을 사용.)
3. 늘린만큼 다시 줄여줌. (나는 3을 곱했었으니, 3을 나누었음.)
코드로는 이렇게 간단하다.
처음 공식을 만든 사람은 무에서 유를 창조했으니 힘들었겠지만, 지금에서는 이해하고보니 쉬워보이는 코드이다.
NdotL = pow((NdotL * 0.5f) + 0.5f, 2); // 하프 램버트.
// 툰 쉐이더 느낌내기.
float step = 3;
NdotL = ceil(NdotL * step) / step;
스페큘러(정반사) 구하기
오늘은 스페큘러를 구현해볼 것이다.
스페큘러를 구현할 때는 대표적으로
퐁(Phong), 블린-퐁(Blinn-Phong) 공식이 있다.
퐁(Phong) 구현하기.
우리에게 주어진 것은 Light 방향과, Normal 방향, View 방향이다.
1. 빛 방향과 노멀 방향을 이용해서 반사 벡터를 구한다. (반사벡터 구하는 공식은 아직 이해 안함.)
2. 반사 벡터와 뷰 방향(뷰 방향은 뒤집음.)을 내적한다. (Normalize하고 내적하면, Cosθ 값만 남음.)
그대로 코드로 나타내면, 이렇게 된다.
// 스페큘러.
float3 reflection = reflect(lightDir, worldNormal); // 반사 벡터 구하기.
float3 cameraDirection = normalize(input.cameraDirection); // cameraDirection = ViewDirection
// 퐁 쉐이더. 너무 동글동글함.
float specular = 0;
if (NdotL > 0)
{
float RdotV = dot(reflection, -cameraDirection); // View 방향 뒤집고, 내적하기.
specular = saturate(RdotV);
specular = pow(specular, 15.0f); // 강도 조절하기.
}
블린-퐁 (Blinn-Phong) 구현하기.
마찬가지로 우리에게 주어진 것은 빛 방향, 노멀 방향, 뷰 방향이다.
연구를 하다보니, 노멀 방향이 필요없이 Light, View 만으로도 비슷한 효과를 흉내낼 수 있는 공식이 나왔다.
반사벡터를 구하는 식이 사라지니 성능이 가벼워졌다는 장점이 있다. (그 대신 완전 정확하지는 않음.)
1. Light 방향과 View 벡터를 뒤집는다.
2. 두 벡터를 더해서 Half Vector를 구하고, Normal과 내적을 한다.
Half Vector와 아까 구했던 반사 벡터와 값이 비슷하다.
그대로 코드로 나타내면,
// 블린-퐁(Blinn-Phong)
float3 halfVector = normalize((-cameraDirection) + (-lightDir)); // 방향 뒤집고, 더하기.
float specular = 0;
if (NdotL > 0)
{
float HdotN = saturate(dot(halfVector, worldNormal));
specular = pow(HdotN, 100.0f); // 강조 조절하기.
}
왼쪽 이미지는 퐁 (15 제곱함.)
오른쪽 이미지는 블린-퐁 (100 제곱함.)
너무 비슷하당. ㄷㄷ;;;
림 라이트 구현하기
림 라이트는
뒤에서 빛이 들어올 때, 오브젝트에 생기는 테두리 빛? 이다.
구현 공식은
normal 벡터 : 오브젝트의 노멀 방향 단위 벡터.
view 벡터 : 카메라가 보고 있는 방향 단위 벡터.
1 - max(0, dot(normal, -view))
구현 코드
// 림 라이트.
float rim = 0;
rim = 1 - saturate(dot(worldNormal, -cameraDirection));
rim = pow(rim, 3.0f); // 강도 조정.
// 색 입히기.
float3 rimColor = float3(0.8f, 0.2f, 0.1f);
rimColor = rim * rimColor;
return float4(rimColor, 1);
1. 그냥 림 라이트
2. 강도 조정한(3 제곱) 림 라이트.
3. 림 라이트 색상 적용하기.
4. 이전에 구현했던 조명 연산들 조합함. (하프 램버트 + 스페큘러 + 림 라이트)
오늘 작성한 DirectX 코드
- 디퓨즈를 이용해서 툰 쉐이더 느낌내기.
- 스페큘러(정반사)를 퐁, 블린 퐁 공식으로 구현.
- 림 라이트 구현.
계속 느끼지만, 벡터 연산이 진짜 많이 쓰이는 것 같다.
숨 쉬듯이 쓰인다.
게임 수학에서 삼각함수, 벡터, 행렬은 정말 중요한 듯 하다.