물리 기반 렌더링(PBR)을 공부하다 보면
자주 사용되는 모델인
Cook-Torrance BRDF 모델을 접하게 되는데
f(l,v)=F(l,h)4D(h)G(l,v,h)(→N⋅→L)(→N⋅→V)
이전 포스팅에서는 기하학에
관련된 항을 살펴 봤었는데
https://lhh3520.tistory.com/381
[Shader] 유니티 쉐이더를 이용한 기하학 효과 구현
물리 기반 렌더링(PBR)을 공부하다 보면 자주 사용되는 모델인 Cook-Torrance BRDF 모델을 접하게 되는데 $$f(l, v) = \frac{F(l, h)}{4}\frac{D(h)G(l,v,h)}{(\vec{N}\cdot \vec{L})(\vec{N}\cdot \vec{V})..
lhh3520.tistory.com
이번에는
D(h)
이부분에 해당하는 미세면 분포 항에
대해 살펴 보겠습니다.
미세면 분포는 쉽게 말해
빛이 입사하고 반사 되면서
생기는 스펙큘러의 분포의 모양이라고
보면 될 것 같습니다.

이렇게 스펙큘러가 어떻게
어떤 모양으로 분포되어서
형성이 되는지에 대한 부분 입니다.
현재 가장 많이 사용되는
근사식은 GGX라고 하는데 식은 아래와 같습니다.
D(h)=a2π((n⋅h)2(a2−1)+1)2
여기서 사용되는 a의 값은
Roughness의 제곱입니다.
a=Roughness2
이 값들을 기준으로 유니티에서
쉐이더 코드를 만들어 적용해 봅니다.
유니티 버전은 현재 최신기준
2021.2.5f1
으로 테스트 했습니다.
새로운 프로젝트를 만들고
아래와 같이 간단히 세팅해 줍니다.

쉐이더 파일과 매터리얼 파일을
생성해 구에 입혀 줍니다.

필요 없는 항목들은 제거해 줍니다.
(Metalic, Glossiness 같은거..)
Properties
{
_Color ("Color", Color) = (1,1,1,1)
_MainTex ("Albedo (RGB)", 2D) = "white" {}
}
빛의 입사각을 알아야 하기 때문에
커스텀 라이트를 달고 스펙큘러는 잠시 제외하고
디퓨즈만 램버트로 넣어 줍니다.
CGPROGRAM
#define PI 3.14159265358979353846f
#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;
}
위 쉐이더를 적용시킨 결과.

여기에 아까 나왔었던
GGX 근사식을 코드로 옮겨 줍니다.
float sqr(float value)
{
return value * value;
}
inline float3 GetGGX(float NoH)
{
float alphasqr = sqr(_Roughness);
float denominator = sqr(NoH) * (alphasqr - 1) + 1;
return alphasqr / (PI * sqr(denominator));
}
이 식을 이용해
커스텀 라이팅 함수내에 스펙큘러에 넣어줍니다.
inline fixed4 LightingCustomLight (SurfaceOutput s, float3 viewDir, UnityGI gi)
{
UnityLight light = gi.light;
float3 halfV = normalize(light.dir + viewDir);
float NoH = saturate( dot(s.Normal, halfV) );
float NoL = saturate( dot(s.Normal, light.dir) );
float3 diffuseTerm = NoL * s.Albedo.rgb * light.color;
float3 specular = GetGGX(NoH);
fixed4 finalColor = fixed4(0, 0, 0, 1);
finalColor.rgb = diffuseTerm.rgb + specular;
return finalColor;
}
스펙큘러 주변에 분포가 생긴 것을 볼 수 있습니다.

거칠기에 따른 분포를 비교해보면..

거친 정도에 따라서
스펙큘러의 하이라이트 모양이
달라지는 것을 확인할 수 있습니다.
전체 쉐이더 코드
Shader "Custom/GGXDistribution"
{
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
#define PI 3.14159265358979353846f
#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;
}
inline float3 GetGGX(float NoH)
{
float alphasqr = sqr(_Roughness);
float denominator = sqr(NoH) * (alphasqr - 1) + 1;
return alphasqr / (PI * sqr(denominator));
}
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;
float3 halfV = normalize(light.dir + viewDir);
float NoH = saturate( dot(s.Normal, halfV) );
float NoL = saturate( dot(s.Normal, light.dir) );
float3 diffuseTerm = NoL * s.Albedo.rgb * light.color;
float3 specular = GetGGX(NoH);
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 |