CookBook/Shader 2012.04.13 22:57

셰도우건 BRDF 셰이더 분석

약속한대로 유니티가 제공하는 셰도우건의 샘플을 하나씩 분석해보겠습니다.

셰도우 건에서 제공하는 셰이더들은 여러가지가 있지만 가장 Hot 한 이슈는 캐릭터를 표현할 때 사용하는 BRDF 셰이더가 아닌가 생각합니다.

 

BRDF란 Bidirectional Reflectance Distribution Function의 약자로 불투명한 물체에서의 반사율을 나타내는 다양한 기법을 의미합니다.

이를 표현하기 위한 여러가지 모델들이 있으며, 일반적으로는 표면에서 빛이 어떻게 반사되는지를 나타내는 공식을 의미하지만,

셰도우건의 BRDF 셰이더는 이러한 BRDF 모델을 제시하는 것이 아닌, 모바일에서의 퍼포먼스를 위해 결과물을 BRDF처럼 보여지도록 흉내낸 셰이더 입니다.

 

특히 3.5 버젼의 기능을 통해 제공된 풀 글로벌 일루미네이션 ( 라이트맵을 통한 정적인 물체의 GI + 라이트프로브를 통한 동적인 물체의 GI ) 에 이 BRDF 셰이더가 추가됨으로서 표현에 의한 한계를 더욱 넓혀나갈 수 있게 되었다는 점에서 큰 의의가 있다고 하겠습니다.

( 참고로 이 BRDF 셰이더는 라이트와 관련 없이 동작합니다. )

 

이 셰이더에 대해 자세히 살펴보기 전에, 3.5의 라이트 프로브 시스템에 대해 고찰해보면 일반적으로 아래와 같이 유니티가 제공하는 ShadeSH9 함수에 의해 계산되어진 rgb값을 라이팅 연산이 끝난 후, albedo 값과 곱한 결과물을 더하여 최종 컬러를 표현합니다.

 

float3 shlight = ShadeSH9 (float4(worldN,1.0));

c = LightingBlinnPhong (o, IN.lightDir, IN.viewDir, atten);

c.rgb += o.Albedo * shlight;

...  이후는 스페큘러 및 라이트맵 추가 계산 ...

 

이러한 방식에서 아쉬운 점은 BRDF에 대한 계산은 결국 라이팅함수에서 수행해야 하므로, 라이트프로브의 데이터로는 밋밋한 색조만 얹어주는 역할밖에 할 수 없다는 것이겠지요. 따라서 아래 그림과 같이 라이팅이 없는 렌더링은 주위의 GI 요소를 캐릭터에 반영하지만 노멀맵의 반영이 없이 밋밋하게 캐릭터가 표현됩니다.

 

 

 

따라서 캐릭터가 노멀맵의 효과를 누리기 위해서는 BRDF를 사용해야 하고 결국에 모바일에서 PerPixel라이팅 계산을 진행해야 하는 상황이 발생하게 되겠지요.

이를 개선하기 위해 PerPixel이지만 빠르게 BRDF를 표현하는 방법을 고안하였는데 이것이 셰도우건에서 사용하는 Mobile-TexPseudoBRDF-WrapAround-ShadowgunStyleLightProbes (헉헉헉... 이름 참 길다) 셰이더입니다.

이 셰이더를 사용하면 아래 그림을 보면 빛이 없는 경우에도 알통 부근의 울퉁불퉁한 굴곡들이 잘 표현되어 있는 것을 볼 수 있습니다.

 

 

구현된 코드는 아래와 같습니다.

fixed3 halfDir = normalize (lightDir + viewDir); // H 계산

fixed NdotL = dot (s.Normal, lightDir); // N dot L계산

fixed NdotH = dot (s.Normal, halfDir); // N dot H계산

fixed biasNdotL = NdotL * 0.5 + 0.5; // NdotL을 [0,1] 영역으로 재배치

fixed4 l = tex2D (_BRDFTex, fixed2(biasNdotL, NdotH)); // 페이크 BRDF용 라이트 텍스쳐로부터 색상정보 가져옴

c.rgb = s.OcclusionAndAmbientLight * s.Albedo * (l.rgb + s.Gloss * l.a) * 2; // 라이트프로브 값과 텍스쳐에서 뽑은 라이트색상 및 스페큘러 알파를 적용

 

이후에는 디렉셔널 라이트맵 및 라이트 맵 적용과정을 동일하게 거칩니다.

한가지 주목할 정보는 라이팅 계산 단계에서 이미 라이트 프로브값의 계산을 적용하기 때문에, 셰이더랩이 컴파일과정에서 라이트프로브 값을 적용하지 못하게 아래와 같이 noambient 옵션을 추가해줘야 합니다.

#pragma surface surf PseudoBRDF exclude_path:prepass vertex:separateSH nolightmap noforwardadd noambient approxview

 

그리고 직접 vertex 함수를 선언하여 여기서 수동으로 라이트프로브값을 뽑아낸 후, 이를 라이팅 연산에서 활용할 수 있도록 전달합니다.

struct Input {
 float2 uv_MainTex;
 float2 uv_BumpMap;
 fixed3 shOcclusionAndAmbient;
}; 

void separateSH (inout appdata_full v, out Input o)
{
 float3 worldN = mul ((float3x3)_Object2World, SCALED_NORMAL);
 o.shOcclusionAndAmbient = ShadeSH9 (float4(worldN,1.0));
}

 

이를 사용하면 아래 그림과 같이 빛이 없이도 아래와 같이 큰 차이 없는 빠른 BRDF 캐릭터 렌더링을 수행할 수 있게 됩니다.

 

 

마지막으로 가장 핵심이 되는 BRDF계산에 사용하는 텍스쳐는 에디터 확장 기능을 사용하여 직접 제작할 수 있도록 BRDFLookUpTexture라는 컴포넌트로 제공합니다.

상황에 따라 다양한 텍스쳐를 미리 구비해두고 텍스쳐만 바꿔서 적용해주면 다양한 효과가 빠르게 나오겠지요~

 

 

이상 셰도우건의 BRDF 캐릭터 셰이딩을 위한 기법에 대해 간략하게 알아보았습니다.

 

아래는 간단하게 Area라이트3개를 넣어서 만든 씬에 두 명의 뮤턴트 캐릭을 이동시킨 예제입니다.

 

 

왼쪽이 BRDF 페이크 셰이더를 사용한 캐릭터고, 오른쪽이 일반 디렉셔널 라이트를 사용한 캐릭터입니다.

 

 

 

 

'CookBook > Shader' 카테고리의 다른 글

셰도우건 BRDF 셰이더 분석  (2) 2012.04.13
오쏘그래픽 카메라용 툰 셰이더  (0) 2012.04.12
Surface Shader Program 발표 자료  (0) 2012.04.12