물리 기반 렌더링(PBR)을 공부하다 보면
자주 사용되는 모델인
Cook-Torrance BRDF 모델을 접하게 되는데
$$f_{s} = \frac{F}{\pi}\frac{DG}{(\vec{N}\cdot \vec{L})(\vec{N}\cdot \vec{V})}$$
최근 모델은 𝝅대신 4를 나눠준다고 합니다.
$$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})}$$
뭐.. 어쨌건..
모델을 보게되면 프레넬 항이 존재합니다.
먼저 프레넬에 대해서 조금 알아보자면
모든 빛은 매질을 만났을때 반사되거나
흡수 되거나 굴절이 되도록 되어있는데
이 과정에서
매질마다 가지는 반사률이나 굴절률이
다르기 때문에 특정한 매질에서 얼마 만큼의 빛이
반사되는지를 알아내는 공식이 프레넬 공식입니다.
이 공식을 통해서 특정 매질이
어떤 각도에서 어느정도의 반사율을 가지는지
알아낼 수 있습니다.
뭔가 복잡한 것 같지만
실생활에서 이러한 효과는 의외로 쉽게
접했을 수도 있을것 같습니다.
아래의 사진을 보면
가까운 곳의 물은 안쪽이
반사되지 않고 내부를 볼 수 있지만
멀리에 있는 물의 표면에는 주변 환경이
반사되는 강도가 강한걸 볼 수 있습니다.
추가로
이 굴절되는 과정에서 조금 특이한 현상이 생기는데
특정 각도에서는 굴절이 되지 않고 정반사가 이루어지는
각도가 생긴다는 것입니다.
이러한 각도를 Critical Angle 이라고 하고
이 각도를 구하는 공식은 아래와 같습니다.
$$\Theta_{c} = arcsin(\frac{n2}{n1})$$
이 현상도 실생활에서 볼 수가 있는데
어두운 곳에서 사진을 촬영할때
뒤에 조명을 놓고 터트려서 피사체의
외곽을 강조하면서 사진을 찍거나
뒷부분에 빛이 비출 때
외곽선이 빛이나는 현상이
이것과 연관된다고 할 수 있겠습니다.
(게임에서는 림라이트라고도 하죠)
이정도 알아보고
물리기반 렌더링을 사용할때
이용되는 프레넬 효과를 유니티에서
한번 구현해 보겠습니다.
일단 적당히 환경을 세팅해 놓고
쉐이더파일과 매터리얼을 만들어서
원형 구에 씌워 줍니다.
유니티에서 기본 제공되는 쉐이더를 입혔는데
옛날에 비해서 뭔가 느낌이 그럴싸(?) 해졌네요.
여기에서 일단 프레넬 효과를
좀 더 잘 확인할 수 있도록 앰비언트를 없애고
색깔을 0으로 세팅했습니다.
#pragma surface surf Lambert noambient
#pragma target 3.0
void surf (Input IN, inout SurfaceOutput o)
{
fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color;
o.Albedo = 0; // c.rgb;
o.Alpha = c.a;
}
주변광없애고 색깔을 없앤 모습.
게임에서 많이 사용되는 프레넬 공식중에
Schlick 공식이 있다는데 이 공식을 사용해 줍니다.
$$R(\Theta) = R_{o} + (1 - R_{o})(1 - cos\Theta)^{5}$$
여기서 R0는 반사율 입니다.
$$R_{o} = (\frac{n1-n2}{n1+n2})^{2}$$
보통 반사율값은 메탈릭값으로 넣어준다고 해서
메탈릭 값으로 넣어 봅니다.
void surf (Input IN, inout SurfaceOutput o)
{
fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color;
o.Albedo = 0; // c.rgb;
o.Alpha = c.a;
// Schlick Approximation Fresnel
// 굴절률값 대신 metalic 값으로 넣음.
float NoV = saturate(dot(o.Normal, IN.viewDir));
float exponential = pow(1 - NoV, 5);
float fresnel = _Metallic + (1 - _Metallic) * exponential;
o.Emission = fresnel;
}
이렇게 해주면
물체의 가장자리가 반사되어 빛이나는
효과를 만들어 볼 수 있습니다.
전체 쉐이더 코드
Shader "Custom/SchlickApproximation"
{
Properties {
_Color ("Color", Color) = (1,1,1,1)
_MainTex ("Albedo (RGB)", 2D) = "white" {}
_Metallic ("Metallic", Range(0,1)) = 0.0
}
SubShader {
Tags { "RenderType"="Opaque" }
LOD 200
CGPROGRAM
#pragma surface surf Lambert
#pragma target 3.0
sampler2D _MainTex;
struct Input {
float2 uv_MainTex;
float3 viewDir;
};
half _Metallic;
fixed4 _Color;
void surf (Input IN, inout SurfaceOutput o)
{
fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color;
o.Albedo = c.rgb;
o.Alpha = c.a;
// Schlick Approximation Fresnel
// 반사율값 대신 metalic 값으로 넣어둠.
float NoV = saturate(dot(o.Normal, IN.viewDir));
float exponential = pow(1 - NoV, 5);
float fresnel = _Metallic + (1 - _Metallic) * exponential;
o.Emission = fresnel;
}
ENDCG
}
FallBack "Diffuse"
}
'Programming > Shader' 카테고리의 다른 글
[Shader] 유니티 쉐이더를 이용한 미세면 분포 효과 구현 (0) | 2021.12.05 |
---|---|
[Shader] 유니티 쉐이더를 이용한 기하학 효과 구현 (0) | 2021.12.05 |