물리기반렌더링( Physically based rendering, PBR, Physically based shading, PBS, 이하 PBR, PBS)이라는 키워드가 떠오른 것은 꽤 오래 되었고 이미 PC나 콘솔에서는 널리 적용되었습니다. 최근 출시한 메탈기어솔리드5는 그 정점을 보여주고 있습니다.
이러한 관점을 기준으로 느낌을 내는 쉐이더를 만져보보았습니다. 그 결과물인 PBR 대응 머티리얼 텍스쳐들을 그대로 사용하는 가벼운 쉐이더를 소개해드릴까 합니다. MatCap 쉐이더에 Metallic factor를 적용해서 PBS같은 느낌을 내는 방식입니다. 다음 스크린샷 이미지에서 렌더링 되고 있는 케릭터와 발판은 PBR 대응으로 만들어진 텍스쳐들을 그대로 사용하고 있으며 실시간 라이팅이 아닌 MatCap 텍스쳐 기반으로 처리되어있습니다. 데모를 구글 플레이스토어에 올려놓았으므로 기기에 설치하여 확인해보실 수 있습니다.
우선, MatCap 쉐이더를 기반으로 하고 있으므로 MatCap 쉐이더에 대해 간략하게 설명을 드리도록 하겠습니다. MatCap 쉐이더는 모바일에서 유용하게 사용할 수 있는 이미지 기반 라이팅 중 하나입니다. MatCap은 Material Capture의 약자인데, Material Capture는 현실 세계의 라이팅을 수집해서 캡쳐하기 위한 구체를 의미합니다. 보통 CG 영상이나 이미지에서 실사 렌더링을 위한 라이팅 참고 자료로 사용하기 위한 용도로 만들어집니다.
PBS에서의 특징중 하나는 금속과 비금속을 수치로 표현하는 것입니다. 빛이 사물에 닿으면 일부는 흡수되었다가 방출되고 나머지는 바로 반사되 튕겨나갑니다. 이 중 흡수되었다가 방출출되는 빛이 일반적으로 말하는 디퓨즈 영역이고 바로 튕겨나가는 빛이 일반적으로 말하는 스페큘라 영역이 되는 것입니다.
금속은 빛이 닿으면 이러한 디퓨즈 영역이 없이 완전 반사가 일어납니다. 즉, 표면이 금속에 가까울수록 반사의 비중이 높고 금속이 아닌 비전도체에 가까울 수록 디퓨즈의 비중이 높아집니다.
또한, AO맵 역시 반영해주어야합니다. AO는 빛이 차폐되는 정도를 나타내주는 것이므로 라이팅 연산 최종 단계에서 곱해주는 것으로 간단히 처리 해주면 됩니다. Emissive는 발광하는 부분이므로 라이팅 연산 최종 단계에서 더해주는 것으로 간단히 처리 해주면 됩니다. 최종적인 파라미터들은 다음과 같습니다.
쉐이더 코드는 다음과 같습니다. 데모에 쓰인 리소스는 에셋스토어에서 유료로 판매되고 있는 리소스이므로 프로젝트를 공유드리지 못하는 점 양해 바랍니다.
Shader "MatCap/Bumped/Textured Metalic" {
Properties {
_MainTex ("Base (RGB)", 2D) = "white" {}
_AOMap ("AO (RGB)", 2D) = "white" {}
_AOFactor ("AO Factor" , Range(0.0,2.0)) =1.0
_BumpMap ("Normal Map", 2D) = "bump" {}
_MetalicMap ("Metallic (RGB)", 2D) = "white" {}
_MetalicMultiply ("Metallic Multiply" , Range(0.0,10.0)) =1.0
_EmissiveTex ("Emissive (RGB)", 2D) = "black" {}
_EmissiveMultiply ("Emissive Multiply" , Range(0.0,10.0)) =1.0
_MatCapDiffuse ("MatCap Diffuse (RGB)", 2D) = "white" {}
_MatCapReflect ("MatCap Reflect (RGB)", 2D) = "white" {}
_MatCapReflectMultiply ("MatCap Reflect Multiply" , Range(0.0,10.0)) =1.0
}
Subshader {
Tags { "RenderType"="Opaque" }
Pass {
Tags { "LightMode" = "Always" }
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma fragmentoption ARB_precision_hint_fastest
#include "UnityCG.cginc"
struct v2f {
float4 pos : SV_POSITION;
float4 uv : TEXCOORD0;
float3 c0 : TEXCOORD1;
float3 c1 : TEXCOORD2;
};
uniform float4 _MainTex_ST;
uniform float4 _BumpMap_ST;
v2f vert (appdata_tan v) {
v2f o;
o.pos = mul (UNITY_MATRIX_MVP, v.vertex);
o.uv.xy = TRANSFORM_TEX(v.texcoord, _MainTex);
o.uv.zw = TRANSFORM_TEX(v.texcoord, _BumpMap);
float3 n = normalize(v.normal).xyz;
float3 t = normalize(v.tangent).xyz;
float3 b = normalize(cross( n, t ) * v.tangent.w);
float3x3 rotation = float3x3( t, b, n );
o.c0 = mul(rotation, normalize(UNITY_MATRIX_IT_MV[0].xyz));
o.c1 = mul(rotation, normalize(UNITY_MATRIX_IT_MV[1].xyz));
return o;
}
uniform sampler2D _MainTex;
uniform sampler2D _BumpMap;
uniform sampler2D _AOMap;
uniform sampler2D _MetalicMap;
uniform sampler2D _MatCapDiffuse;
uniform sampler2D _MatCapReflect;
uniform sampler2D _EmissiveTex;
uniform float _MatCapReflectMultiply;
uniform float _AOFactor;
uniform float _EmissiveMultiply;
uniform float _MetalicMultiply;
fixed4 frag (v2f i) : COLOR {
fixed4 tex = tex2D(_MainTex, i.uv.xy);
fixed4 ao = tex2D(_AOMap, i.uv.xy);
fixed4 metalic = tex2D(_MetalicMap, i.uv.xy);
fixed4 si = tex2D(_EmissiveTex, i.uv.xy);
fixed3 normals = UnpackNormal(tex2D(_BumpMap, i.uv.zw));
half2 capCoord = half2(dot(i.c0, normals), dot(i.c1, normals));
fixed4 diff = tex2D(_MatCapDiffuse, capCoord*0.5+0.5) * tex;
fixed4 refl = tex2D(_MatCapReflect, capCoord*0.5+0.5);
refl.a = 1;
refl *= _MatCapReflectMultiply;
fixed4 ret = lerp( diff, refl, saturate(float4(metalic.rgb * _MetalicMultiply,1)));
return ret * lerp( 1, ao, _AOFactor) + si * _EmissiveMultiply;
}
ENDCG
}
}
Fallback "VertexLit"
}
간단한 쉐이더다보니 많은 한계점을 가지고있습니다. 보시다시피 이는 정확한 PBS는 아닙니다. 많은 부분들이 생략되어 있고 라이팅 재질 느낌은 전적으로 MatCap 텍스쳐에 의지하고 있습니다. 때문에, MatCap 텍스쳐를 어떤 것으로 사용하느냐에 따라 품질이 크게 좌우됩니다. 또한, 씬 별로 라이팅이 다르다면 씬 별로 MatCap 텍스쳐를 만들어 줘야 합니다. MatCap 텍스쳐를 어떻게 만드냐에 따라 결과가 달라질 것입니다.
MatCap 방식은 한 쪽에서 바라보는 라이팅만 표현이 가능하기 때문에 카메라 각도가 고정되어 있는 경우에만 유효하다는 제약이 있습니다.그러나, 대부분의 모바일 게임들은 탑 뷰(Top View), 쿼터 뷰(Quarter View), 백 뷰(Back View) 등 카메라의 방향이 고정된 채로 게임이 진행되는 경우가 대부분이므로 모바일서는 큰 제약 사항이 되지는 않을 것입니다.