炸殺
카테고리
작성일
2020. 6. 7. 14:58
작성자
炸殺

이전 포스트에서 만들었던 Lambert 쉐이더에는 Specular가 포함되어있지 않습니다.

이번에는 Lambert 쉐이더에 Speculer를 추가해 봅시다.

 

 

 

 

Pong Shading

 

 

Phong Shading이란?

 

1975년 Bui Tuong Phong씨가 개발한 쉐이딩 기술로,

면단위가 아닌 버텍스간의 보간작업을 진행하는 랜더링 방식입니다.

각진 면을 부드럽게 블랜딩 하는 것에 효과적이지만 실제 스페큘러와 흡사하지 않으며,

평평한 면에서는 스페큘러가 제대로 작동되지 않는 단점이 있습니다. (하이라이트가 언제나 동그랗게 맺힌다..) 

 

 

 

퐁 공식에서는 어떻게 Speculer를 구하는 것일까요?

 

 

 

Reflection Vector

 

 

 

Speculer는 '정반사'를 의미합니다.

 

빛이 물체에 반사 되어 눈에 들어올 때,

입사각(Light Vector)과 반사각(Relfection Vertor)의 각도가 일치하면 Speculer가 맺히게 됩니다.

이 각도의 차이가 적으면 적을 수록 물체가 밝게 빛나는 것처럼 보이게 되는 것이죠.

 

Reflection Vector를 구하는 공식은 Normal Vector를 기준으로

입사각(Light Vector)와 반사각(Reflection Vector)이 같은 각도를 가지고 있다고 가정합니다.

 

즉 Reflection Vector는 Normal Vector를 기준으로 Light Vector를 반전(invert)한 값이 되는 것이죠.

 

 

 

 

이렇게 구해진 Reflection Vector와 View Vector를 dot연산 하게 되면

두 벡터의 각도차가 0에 가까울 수록 밝아지고 멀어질 수록 어두워 지는, 

 

즉, 정반사된 빛(Reflection Vector)이 눈(View Vector)에 들어오면 Speculer가 선명해지고,

정반사된 빛의 각도와 바라보는 시선의 각도가 멀어질 수록 Speculer가 흐려지게 됩니다.

 

 

이를 바탕으로 Phong공식을 코드로 구현하면

참고 포스트 - http://rapapa.net/?p=673

다음과 같습니다.

 

주목할 점은 바로 float3 viewDir.

반드시 해당 순서대로 적어주어야 View Vector를 불러올 수 있습니다.

 

lightDir의 마이너스를 넣어준 이유는 시작점을 Normal Vector와 맞추기 위해서입니다.

normalize를 하지 않고 넣으면 마치 버텍스 라이트를 사용한 느낌으로 스페큘러가 맺히더군요....왜일까....

normalize를 해주었을 경우 해당 현상이 많이 완화 되었지만 완벽하게 보간되진 않았습니다.

(노말을 넣으면 티가 안난다)

 

 

 

 

 

 

 

Biln-Phong Shading

 

 

지금이야 하드웨어 성능이 워낙 좋아졌지만, 당시엔 Reflection Vector를 구하는 것이 꽤 무거웠다고 합니다.

이에 대한 대안 책으로 나온것이 기존의 퐁 쉐이딩을 수정하여 만든 Jim Biln씨의 Biln-Phong Shading.

 

해당 공식은 계산이 무겁던 퐁 쉐이딩의 Specular Reflection의 연산을 보다 빠르게 하기 위해

Half Vector와 Normal Vector의 내적 값을 이용하여 Specular를 계산해줍니다.

 

 

 

 

Half Vector

Half Vector는 View Vector와 Light Vector의 정 중앙을 나누는 벡터로,

View Vector와 Light Vector를 더하여 구할 수 있습니다.

(이렇게 구해진 Half Vector의 길이는 1이 넘기 때문에 normalize를 해줍시다.)

 

Biln-Phong Shading은 이렇게 구한 Half Vector와 오브젝트의 Normal Vector를

내적연산(Dot)하여 나온 값을 바탕으로 Specular를 계산하게 됩니다.

 

 

 

강도 조절을 프로퍼티로 빼 주면 밖에서도 조절이 가능하다.

 

 

 

 

 

 

전부 합쳐봅시다.

 

지금까지 배웠던 라이팅 쉐이더를 전부 합쳐보도록 합시다.

벌써부터 기억이 가물가물하지만....지금까지 공부한 걸 복습할 겸, 자신이 무엇을 까먹었는지 체크할 겸

처음 부터 차근차근 짚어봅시다.

 

 

 

 Custom Light 세팅 + Lambert

 

가장 먼저 Custom Light 세팅과

Lambert 공식을 바탕으로 Diffuse를 넣어줍니다.

 

사용하지 않는 구조체들은 확실하게 지워주고

float4 LightingJS 함수를 만들어주었는데요, float4선언이 되어있기에 return을 통해 반드시 float4로 출력해줍시다.

 

 

음영이 부드럽게 지도록 Half Lambert 공식도 사용하였습니다.

 

Half Lambert 공식을 사용하기 위해선 음수값이 필요하므로 Saturate를 하지 않았지만,

만약 Half Lambert공식을 사용하지 않는다면 반드시 Saturate를 해줍시다. 저는 취향껏 몇번 더 제곱해주었습니다.

 

또한 이렇게 연산한 Diffuse가 환경광과 라이트 컬러, 강도 등등의 영향을 받도록 _LightColor0.rgb를 곱해줍니다.

 

 

 

 

 

 

 

Normal map

 

이제 여기에 Normal map을 더해봅시다.

 

Normal map은 프로퍼티에 MainTex를 불러왔을 때와 마찬가지로 작성해줍니다.

대신 초기값은 "bump"를 넣어줍시다! (white를 넣어도 에러는 안나지만 노말을 안넣었을 때 이상한 값이 나옵니다.)

 

나머지도 MainTex와 마찬가지로 sampler2D 선언, Input에 작성하여 불러와 주고 변수 선언도 해준 뒤....

 

Normal을 불러올 때는 반드시 UnpackNormal 함수를 이용하여 출력해주어야 합니다.

 

 

 

 

 

Rim Light (Fresnel)

 

Rim Light는 View Vector와 Normal Vector의 내적 값을 구한 뒤 반전(invert)하여 만들 수 있습니다.

Diffuse는 깨끗하게 정리해줬다.

Rim Light의 강도와 컬러를 프로퍼티에 빼두어 엔진에서도 조절할 수 있게 만들어줍니다.

이때 Color프로퍼티 앞에 [HDR]을 적어주면 1이상의 컬러값을 사용할 수 있게 되며,

Rim Color가 라이트 컬러를 받아와 보다 자연스럽게 맺히도록 _LightColor0.rgb를 곱해주었습니다.

 

 

 

 

 

 

 

 

Specular

 

Specular는 Blin-Phong Shading 방식으로 계산하겠습니다. 

가장 먼저 Hlaf Vector를 구한 뒤 Half Vector와 Normal Vector의 내적 값을 구해줍니다.

 

Rim Light와 마찬가지로 강도와 컬러를 조절할 수 있도록 프로퍼티를 만들어주었으며,

라이팅의 영향에 따라 보다 자연스럽게 Specular Color가 맺히도록 Specular에게도 _LightColor0.rgb를 곱해주었습니다.

 

여기서 프로퍼티에 스페큘러 컬러의 변수명을 지정해줄 때 _SpecColor로 작성할 경우 오류가 나게 됩니다.

이유는 _SpecColor가 Unity의 내장 변수로 이미 선언되어 있기 때문이죠.

 

커스텀 라이트를 만들 땐 해당 변수명은 피해줍시다! 

 

 

 

하지만 대머리가 아니고서야... 머리카락에 피부만큼의 Specular가 맺히진 않습니다...

원하는 곳에만 Specular가 생기도록 텍스쳐를 받아와 마스킹을 해줍시다!

 

https://docs.unity3d.com/kr/2018.4/Manual/SL-SurfaceShaders.html

 

SurfaceOutput 구조체에 들어있는 친구들 중 Gloss라는 친구가 있습니다.

담당업무는 Specular intensity. 스페큘러의 강도로, 스페큘러의 Opacity를 조절하는 것과 비슷합니다. 

 

surf 함수에서 마스킹으로 사용할 텍스쳐를 불러와 사용할 채널을 Gloss에 넣어준 뒤 Specular와 곱해주었습니다.

마스킹 텍스쳐의 값이 0인 부분은 Specular가 표시되지 않고, 1인 부분은 선명하게 나타나게 됩니다.

 

 

이제 머리카락엔 Specular가 들어가지 않는다.

 

 

 

2nd Specular (Fake Specular)

 

여기서 조금 더 나아가 '가짜 Specular'를 만들어봅시다.

테라에서 사용되었던 랜더링 테크닉중 하나로,

물리적으로 옳진 않지만 View Vector를 은은한 조명으로 사용하여 제질의 질감을 도드라지게 해주고

무엇보다 넣으면 예쁩니다. 쉐이더는 궁극적인 목표는 '예쁘게'보이도록 만들기 입니다.

 

View Vector를 조명으로 사용하는 것으로 Rim Light를 One Minuse하기 전 값을 불러와주면 됩니다.

 

rim을 보다 원할하게 불러오기 위해 변수명을 바꿔주었습니다.

Gloss를 곱해주어 Specular 마스킹을 해주고, Specular Color와 강도도 프로퍼티로 빼줍니다.

Fake Specular에도 _LightColor0.rgb를 곱해 조명의 영향을 받아 자연스럽게 올라가도록 해주었습니다.

영역이 넓고 색상이 어두울 수록 반사광을 받은 듯 은은하게 들어갑니다.

 

 

 

 

 

 

 

최종

 

 

림라이트가 보다 부드럽고 자연스럽게 들어가도록 Half Lambert 공식을 적용하였으며,

콧구멍 안에 Rim Light가 들어가는 것이 거슬려 마스킹을 해주었습니다.

Specular도 조금 더 만져주어 최종 완성입니다.