이번에 구현할 내용은 blin -phong specular lighting 입니다.
사진속 남자의 몸 부분에 빛의 반사된 영역
흰색으로 표현된 부분이 specular lighting이 반영된 부분입니다.
그런데 빛이 닿지 않는 그림자 부분에도 specular lighting이 입니다.
이 부분은 rim lighting으로 fake specular lighting을 구현한 내용이며, 이것도 구현할 것입니다.
두가지를 구현할 예정입니다.
1) blin phong specular lighting, 2) fake specular lighting using rim
=================================================================
1) blin phong specular lighting
먼저 blin phong을 알아보기전에
phong 라이팅 공식을 먼저 알아 보겠습니다.
Diffuse term + Specular term + Ambient term + Emissive term 입니다.
4개의 rgb 값을 더한 결과가 최종 fragment color 값 입니다.
Specular lighting은 빛의 반사 된 부분을 의미 합니다.
Phong 공식에 의해서 Specular lighting을 구하는 방법은 다음과 같습니다.
L : light Vector
N : Normal Vector
R : Reflection Vector
V : View Vecctor
먼저 Reflection Vector를 구한 다음에,
Reflecction Vector와 View Vector를 내적해서 specular term을 구합니다.
R, V 벡터는 단위 벡터이기 때문에 크기는 1이므로, cos 세타 값이 specular term 이 됩니다.
추가로 두가지를 고려해야 합니다.
1> 음의 값은 검은색이므로 의미가 없으므로, saturate 함수를 통해 양수로 만드는 방법이 있습니다.
saturate를 하게 되면 음수 영역이 전부 0이 되면서, 급격하게 어두워지는 단점이 있습니다.
값에 0.5를 곱하고 0.5를 더하게 되면, 부드럽게 0 ~ 1 사이로 보간이 됩니다.
2> R 벡터 V벡터의 각도가 넓은 경우 너무 넓은 범위의 스펙큘러 라이팅이 생깁니다.
범위를 좁게 만들기 위하여 specular 값에 지수승을 곱해줍니다.
float3 r = 2 * s.Normal * dot(s.Normal, lightDir) - lightDir;
float spec = saturate(dot(r, viewDir));
spec = pow(spec, _SpecPow);
======================================================
Phong Specular 공식을 살펴 보았는데요,
블린이라는 사람이 여기서 좀 더 간략화된 식으로 만든 공식은 blinn - phong 공식 입니다.
L Vector와 V Vector
L : Light Vector
V : View Vector
N : Normal Vector
L, V 더한 H 벡터와 N벡터와 내적을 한 값을 스펙큘러 라이팅으로 사용 합니다.
Phong 에서 적용된 양수로 만드는 방법과 범위를 좁게 하기위한 지수승은 동일하게 사용이 됩니다.
float3 H = normalize(lightDir + viewDir);
spec = saturate(dot(H, s.Normal));
spec = pow(spec, _SpecPow);
===================================================
2) fake specular lighting using rim
앞선 홀로그램 챕터에서 rim lighting을 사용하여 윤곽선을 그린바 있습니다.
이때는 normal과 view 벡터를 내적한 값을 인버트 하여, 카메라가 닿지 않는 곳을 밝게 해주는 원리 였는데요,
https://graphicsimon.tistory.com/42
원 물체에 점 2개가 있습니다. 첫번째 점의 노말은 N1, 두번째 점의 노말을 N2 입니다.
N1은 View와의 각도가 90도(세타1) 이고, N2와 View와의 각도는 30도 (세타2) 정도 됩니다.
여기서 View와 N1의 내적을 구하면
첫번째 점 처럼 외곽에 위치해 있어서 뷰와 노말의 각도가 90에 가까운 것들은 0이 나오고,
두번째 점 처럼 뷰와 가까운곳에 위치해 있으면 1 가까운 값을 얻을 것 입니다.
인버트 (1 - a )를 시키지 않고 그대로 사용하게 되면, 카메라가 바라보는 방향을 밝게 해줍니다.
인버트를 시키면 외곽 부분은 밝게 빛이 날 것 입니다.
이 원리로 fake specular를 사용하게 되어, 빛의 방향과 상관없이 카메라가 보는 방향은 밝게 빛나는 효과를 만들어 냅니다.
fakeSpec = pow(rim, _SpecPow) * _SpecCol.rgb * s.Gloss;
specular term 값에 color rgb 값을 곱하고 더 밝게 빛나게 하기 위한 변수값을 property를 두어 곱해서
색상값을 자유롭게 조절해 보세요
===================================================================
Shader "Custom/BlinPhongSpecular"
{
Properties
{
_MainTex("Albedo (RGB)", 2D) = "white" {}
_BumpMap("BumpMap", 2D) = "white" {}
_SpecCol("SpecCol", Color) = (1, 1, 1, 1)
_SpecPow("SpecPow", Range(10, 200)) = 100
_GlossTex("Gloss Tex", 2D) = "white" {}
_SpecCol("SpecColr2", Color) = (1, 1, 1, 1)
_SpecPow2("Specular Power2", Range(10 , 100)) = 100
_RimCol("Rim Color", Color) = (1, 1, 1, 1)
_RimcPow("Rim Power", Range(1, 10)) = 1
}
SubShader
{
Tags { "RenderType" = "Opaque" }
CGPROGRAM
#pragma surface surf Test noambient
#pragma target 3.0
sampler2D _MainTex;
sampler2D _BumpMap;
sampler2D _GlossTex;
float4 _SpecCol;
float _SpecPow;
float4 _SpecCol2;
float _SpecPow2;
float4 _RimCol;
float _RimcPow;
struct Input
{
float2 uv_MainTex;
float2 uv_BumpMap;
float2 uv_GlossTex;
};
void surf(Input IN, inout SurfaceOutput o)
{
fixed4 c = tex2D(_MainTex, IN.uv_MainTex);
fixed4 m = tex2D(_GlossTex, IN.uv_GlossTex);
fixed3 n = UnpackNormal(tex2D(_BumpMap, IN.uv_BumpMap));
o.Albedo = c.rgb;
o.Normal = n;
o.Gloss = m.a;
o.Alpha = c.a;
}
float4 LightingTest(SurfaceOutput s, float3 lightDir, float3 viewDir, float atten)
{
float4 final;
float3 DiffColor;
float ndotl = saturate(dot(s.Normal, lightDir));
DiffColor = ndotl * s.Albedo * _LightColor0.rgb * atten;
// Phong Specular
float3 SpecColor;
float3 r = 2 * s.Normal * dot(s.Normal, lightDir) - lightDir;
float spec = saturate(dot(r, viewDir));
spec = pow(spec, _SpecPow);
// Blin Phong Specular
float3 H = normalize(lightDir + viewDir);
spec = saturate(dot(H, s.Normal));
spec = pow(spec, _SpecPow);
SpecColor = spec * _SpecCol.rgb * s.Gloss;
//Rim term
float3 rimColor;
float rim = abs(dot(viewDir, s.Normal));
float invrim = 1 - rim;
rimColor = pow(invrim, _RimcPow) * _RimCol.rgb;
// Fake spec term
float3 SpecColor2;
SpecColor2 = pow(rim, _SpecPow) * _SpecCol.rgb * s.Gloss;
final.rgb = DiffColor.rgb + SpecColor.rgb + rimColor.rgb + SpecColor2.rgb;
final.a = s.Alpha;
return final;
}
ENDCG
}
FallBack "Diffuse"
}
'Unity - Surface Shader' 카테고리의 다른 글
5. 알파블렌딩과 컷아웃 (3) | 2023.11.20 |
---|---|
4. Cubemap을 사용하여 반사매질을 표현해보자. (0) | 2023.11.19 |
3. Vertex Shader를 사용하여 Toon Shading 해보기 (0) | 2023.11.14 |
1. Hologram (0) | 2023.11.10 |
시작하면서... (0) | 2023.11.10 |