물리 기반 렌더링(PBR)을 공부하다 보면
자주 사용되는 모델인
Cook-Torrance BRDF 모델을 접하게 되는데
f(l,v)=F(l,h)4D(h)G(l,v,h)(→N⋅→L)(→N⋅→V)
이전 포스팅에서는 프레넬에
관련된 항을 살펴 봤었는데
https://lhh3520.tistory.com/380
[Shader] 유니티 쉐이더를 이용한 프레넬 효과 구현
물리 기반 렌더링(PBR)을 공부하다 보면 자주 사용되는 모델인 Cook-Torrance BRDF 모델을 접하게 되는데 fs=FπDG(→N⋅→L)(→N⋅→V)
lhh3520.tistory.com
이번에는 기하학에 관련된
항을 살펴 봅니다.
G(l,v,h)
이 부분입니다.
물리기반 렌더링을 살펴보다보면
미세면 이론(Microfacet Theory)
을 접하게 되는데 우리가 보는 표면이
미시적으로 들어가 보면 굴곡들이 있다는 이론이죠.

그렇다 보니 미세표면에서 같은 빛을 받아도
특정 부분은 그림자가 생길수도 있다는 것입니다.

물리기반 렌더링에서는
이런 기하학적으로 생기는 그림자 마스킹
효과를 스펙큘러에 포함합니다.
현재 가장 많이 쓰이고 있는
근사식은 스미스 기하항이고 식은 아래와 같다.
G(l,v,h)=G1(l)G1(v)
G1
부분은 아래의 값으로 구할 수 있다.
G1=n⋅v(n⋅v)(1−k)+k
여기서 변수 k는 아래와 같이 자주 쓰이는듯 합니다.
k=(Roughness+1)28
여기까지 보면 물리기반 렌더링기준
기하학적인 관점에서 Roughness(거칠기)가
늘어나면 빛의 감쇠도 증가한다는걸 알 수 있습니다.
이 현상을 확인해 보기위해
유니티에서 쉐이더 코드를 만들어 적용해 봅니다.
유니티 버전은 현재 최신기준
2021.2.5f1
으로 테스트 했습니다.
새로운 프로젝트를 만들고
아래와 같이 대충 세팅해 줍니다.

그리고 쉐이더 만들고
매터리얼에 씌워서 구에 입혀 줍니다.

필요 없는 항목들은 제거합니다.
(Metalic, Glossiness 같은거..)
Properties { _Color ("Color", Color) = (1,1,1,1) _MainTex ("Albedo (RGB)", 2D) = "white" {} }
빛의 입사각을 알아야 하기 때문에
커스텀 라이트를 달고 스펙큘러는 잠시 제외하고
디퓨즈만 램버트로 넣어 줍니다.
CGPROGRAM #pragma surface surf CustomLight fullforwardshadows #pragma target 3.0 inline void LightingCustomLight_GI (SurfaceOutput s, UnityGIInput data, inout UnityGI gi) { gi = UnityGlobalIllumination (data, 1.0, s.Normal); } inline fixed4 LightingCustomLight (SurfaceOutput s, float3 viewDir, UnityGI gi) { UnityLight light = gi.light; float NoL = saturate( dot(s.Normal, light.dir) ); float3 diffuseTerm = NoL * s.Albedo.rgb * light.color; finalColor.rgb = diffuseTerm.rgb; return finalColor; } void surf (Input IN, inout SurfaceOutput o) { fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color; o.Albedo = c.rgb; o.Alpha = c.a; }
위 쉐이더를 적용시킨 결과.

이제 스펙큘러에 스미스 기하학을
코드로 구현해서 넣어 줍니다.
// 제곱 float sqr(float value) { return value * value; } // 스미스 기하학의 G1 구하는 함수 float G1 (float k, float x) { return x / (x * (1 - k) + k); } // 스미스 기하항 식을 코드로 구현 inline float3 GetSmithGeometry(float NoL, float NoV) { float r = _Roughness + 1; float k = sqr(r) / 8; float g1L = G1 (k, NoL); float g1V = G1 (k, NoV); return g1L * g1V; }
이제 이걸 아까 만들었던
커스텀 라이팅 함수내에 스펙큘러로 넣어줍니다.
inline fixed4 LightingCustomLight (SurfaceOutput s, float3 viewDir, UnityGI gi) { UnityLight light = gi.light; float NoL = saturate( dot(s.Normal, light.dir) ); float NoV = saturate( dot(s.Normal, viewDir) ); float3 diffuseTerm = NoL * s.Albedo.rgb * light.color; float3 spec = GetSmithGeometry(NoL, NoV); fixed4 finalColor = fixed4(0, 0, 0, 1); finalColor.rgb = diffuseTerm.rgb + spec; return finalColor; }
스펙큘러도 생기고 주변부에는
빛이 감쇠가 되는 효과도 볼 수 있습니다.

빛이 너무 강해서 강도를 좀 줄이고
거칠기를 조절해서 비교해 보면..

거칠기에 따라서 빛이 얼마나
감쇠되는지를 확인할 수 있습니다.
전체 쉐이더 코드
Shader "Custom/SmithGeometry" { Properties { _Color ("Color", Color) = (1,1,1,1) _MainTex ("Albedo (RGB)", 2D) = "white" {} _Roughness ("Roughness", Range(0, 1)) = 1 } SubShader { Tags { "RenderType"="Opaque" } LOD 200 CGPROGRAM #pragma surface surf CustomLight fullforwardshadows #pragma target 3.0 struct Input { float2 uv_MainTex; }; sampler2D _MainTex; fixed4 _Color; float _Roughness; UNITY_INSTANCING_BUFFER_START(Props) UNITY_INSTANCING_BUFFER_END(Props) float sqr(float value) { return value * value; } float G1 (float k, float x) { return x / (x * (1 - k) + k); } inline float3 GetSmithGeometry(float NoL, float NoV) { float r = _Roughness + 1; float k = sqr(r) / 8; float g1L = G1 (k, NoL); float g1V = G1 (k, NoV); return g1L * g1V; } inline void LightingCustomLight_GI (SurfaceOutput s, UnityGIInput data, inout UnityGI gi) { gi = UnityGlobalIllumination (data, 1.0, s.Normal); } inline fixed4 LightingCustomLight (SurfaceOutput s, float3 viewDir, UnityGI gi) { UnityLight light = gi.light; float NoL = saturate( dot(s.Normal, light.dir) ); float NoV = saturate( dot(s.Normal, viewDir) ); float3 diffuseTerm = NoL * s.Albedo.rgb * light.color; float3 specular = GetSmithGeometry(NoL, NoV); fixed4 finalColor = fixed4(0, 0, 0, 1); finalColor.rgb = diffuseTerm.rgb + specular; return finalColor; } void surf (Input IN, inout SurfaceOutput o) { // Albedo comes from a texture tinted by color fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color; o.Albedo = c.rgb; o.Alpha = c.a; } ENDCG } FallBack "Diffuse" }
'Programming > Shader' 카테고리의 다른 글
[Shader] 유니티 쉐이더를 이용한 미세면 분포 효과 구현 (0) | 2021.12.05 |
---|---|
[Shader] 유니티 쉐이더를 이용한 프레넬 효과 구현 (0) | 2021.12.03 |
댓글을 사용할 수 없습니다.