Diffuse
+ BlinnPhong의 Specular
+ Ambient
색상 이다.☑️ Phong 공식
- 내가 보는 방향으로부터 반사된 방향에 조명이 있으면 그 부분의 하이라이트가 가장 높다.
- Specular가 흰 동그라미로 표현됨 (태양을 상정한 간략화된 표현)
- 이 하이라이트는 내 시선 벡터
V
를 노말 방향N
을 기준으로 반사하는 반사 벡터와R
조명 벡터L
의 내적(R·L)으로 표현된다. 하지만 이 반사 벡터를 연산하기 위해서는 dot 연산이 또 들어간다는 부담이 있다.- dot연산이 두번 들어가는 위 공식을 간략화 한 것이 Blinn-Phong 공식이다!
☑️Blinn-Phong 공식
- 시선 벡터
V
와 조명 벡터L
의 중간값인 하프 벡터H
를 구하고, 이를 노멀벡터와 내적한다(N·H).- 하프벡터는 조명벡터와 시선벡터를 더해서 구하고, 길이를 1로 조절하기 위해
normalize()
함수를 사용한다.
noambient
로 환경광을 끄고, Lambert
연산을 따로 독립시켜 DiffColor
라는 이름으로 만들어 보기 편하게 수정했다.Shader "Custom/Blinn-PhongShader"
{
Properties
{
_MainTex("Albedo (RGB)", 2D) = "white" {}
_BumpMap("NormalMap", 2D) = "bump"{}
}
SubShader
{
Tags { "RenderType" = "Opaque" }
CGPROGRAM
#pragma surface surf Test noambient
sampler2D _MainTex;
sampler2D _BumpMap;
struct Input
{
float2 uv_MainTex;
float2 uv_BumpMap;
};
void surf(Input IN, inout SurfaceOutput o)
{
fixed4 c = tex2D(_MainTex, IN.uv_MainTex);
o.Albedo = c.rgb;
o.Normal = UnpackNormal(tex2D(_BumpMap, IN.uv_BumpMap));
o.Alpha = c.a;
}
float4 LightingTest(SurfaceOutput s, float3 lightDir, float atten)
{
float4 final;
//Lambert영역
float3 DiffColor;
float ndot1 = saturate(dot(s.Normal, lightDir));
DiffColor = ndot1 * s.Albedo * _LightColor0.rgb * atten;
//Specular영역
//final영역
final.rgb = DiffColor.rgb;
final.a = s.Alpha;
return final;
}
ENDCG
}
FallBack "Diffuse"
}
float3 viewDir
매개변수로 받아오고, Specular영역에서 스펙큘러를 연산한다.H
연산float4 LightingTest(SurfaceOutput s, float3 lightDir, float3 viewDir, float atten)
{
···
//Specular영역
float3 H = normalize(lightDir + viewDir);
···
//결과 확인을 위해 하프벡터를 반환
return float4(H,1);
}
2. 하프벡터H
와 노멀벡터s.Normal
의 내적 연산
float3 H = normalize(lightDir + viewDir);
float spec = saturate(dot(H, s.Normal));
spec = pow(spec, 100); //스펙큘러의 넓이 줄이기
···
return spec;
//Specular영역
float3 SpecColor;
float3 H = normalize(lightDir + viewDir);
float spec = saturate(dot(H, s.Normal));
spec = pow(spec, _SpecPow); //properties에서 받아온 값으로 범위 조절
SpecColor = spec * _SpecCol.rgb; //properties에서 받아온 값으로 색 연산
//final영역
final.rgb = DiffColor.rgb + SpecColor.rgb; //Diffuse와 Specular 합
final.a = s.Alpha;
return final; //최종 결과 반환
Shader "Custom/Blinn-PhongShader"
{
Properties
{
_MainTex("Albedo (RGB)", 2D) = "white" {}
_BumpMap("NormalMap", 2D) = "bump"{}
//유니티에 내장되어 있는 BlinnPhong에서 사용하고 있는
//_SpecColor는 Porperties에서 사용하면 안되므로,
//_SpecCol이라고 살짝 이름을 바꿔 사용
_SpecCol("Specular Color", Color) = (1, 1, 1, 1)
_SpecPow("Specular Power", Range(10, 200)) = 100
}
SubShader
{
Tags { "RenderType" = "Opaque" }
CGPROGRAM
#pragma surface surf Test //noambient 삭제로 최종 결과물 확인
sampler2D _MainTex;
sampler2D _BumpMap;
float4 _SpecCol;
float _SpecPow;
struct Input
{
float2 uv_MainTex;
float2 uv_BumpMap;
};
void surf(Input IN, inout SurfaceOutput o)
{
fixed4 c = tex2D(_MainTex, IN.uv_MainTex);
o.Albedo = c.rgb;
o.Normal = UnpackNormal(tex2D(_BumpMap, IN.uv_BumpMap));
o.Alpha = c.a;
}
float4 LightingTest(SurfaceOutput s, float3 lightDir, float3 viewDir, float atten)
{
float4 final;
//Lambert영역
float3 DiffColor;
float ndot1 = saturate(dot(s.Normal, lightDir));
DiffColor = ndot1 * s.Albedo * _LightColor0.rgb * atten;
//Specular영역
float3 SpecColor;
float3 H = normalize(lightDir + viewDir);
float spec = saturate(dot(H, s.Normal));
spec = pow(spec, _SpecPow);
SpecColor = spec * _SpecCol.rgb;
//final영역
final.rgb = DiffColor.rgb + SpecColor.rgb;
final.a = s.Alpha;
return final;
}
ENDCG
}
FallBack "Diffuse"
}
···
//Rim영역
float3 rimColor;
float rim = abs(dot(viewDir, s.Normal));
float invrim = 1 - rim;
rimColor = pow(invrim, _RimPow) * _RimColor.rgb; //_RimPow, _RimColor는 properties에서 받아온 값
//final영역
final.rgb = DiffColor.rgb + SpecColor.rgb + rimColor.rgb; //rim도 더해준다.
final.a = s.Alpha;
return final;
rim
과 invrim
을 구분해둔 이유는 가짜 스펙큘러를 만들기 위해서이다.return pow(rim, 200);
로 제곱한rim
의 결과만 따로 확인해보면 나의 시선 쪽으로 하이라이트가 생긴것을 확인할 수 있다.Shader "Custom/Blinn-PhongShader"
{
Properties
{
_MainTex("Albedo (RGB)", 2D) = "white" {}
_BumpMap("NormalMap", 2D) = "bump"{}
_GlossTex("GlossTex", 2D) = "White"{}
_SpecCol("Specular Color", Color) = (1, 1, 1, 1)
_SpecPow("Specular Power", Range(10, 200)) = 100
_SpecCol2("Specular Color", Color) = (1, 1, 1, 1)
_SpecPow2("Specular Power", Range(10, 200)) = 100
_RimColor("Rim Color", Color) = (0.5, 0.3, 0.2, 1)
//3번의 스펙큘러 강도 조절이 잘 안되서 그런지 부자연스러움이 있어 색을 조정하여 그나마 자연스러워 보이도록 했다
_RimPow("Rim Power", Range(1, 10)) = 7
}
SubShader
{
Tags { "RenderType" = "Opaque" }
CGPROGRAM
#pragma surface surf Test
sampler2D _MainTex;
sampler2D _BumpMap;
sampler2D _GlossTex;
float4 _SpecCol;
float _SpecPow;
float4 _SpecCol2;
float _SpecPow2;
float4 _RimColor;
float _RimPow;
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);
float4 m = tex2D(_GlossTex, IN.uv_GlossTex);
o.Albedo = c.rgb;
o.Normal = UnpackNormal(tex2D(_BumpMap, IN.uv_BumpMap));
o.Gloss = m.a;
o.Alpha = c.a;
}
float4 LightingTest(SurfaceOutput s, float3 lightDir, float3 viewDir, float atten)
{
float4 final;
//Lambert영역
float3 DiffColor;
float ndot1 = saturate(dot(s.Normal, lightDir));
DiffColor = ndot1 * s.Albedo * _LightColor0.rgb * atten;
//Specular영역
float3 SpecColor;
float3 H = normalize(lightDir + viewDir);
float spec = saturate(dot(H, s.Normal));
spec = pow(spec, _SpecPow);
SpecColor = spec * _SpecCol.rgb * s.Gloss;
//Rim영역
float3 rimColor;
float rim = abs(dot(viewDir, s.Normal));
float invrim = 1 - rim;
rimColor = pow(invrim, _RimPow) * _RimColor.rgb;
//Fake Specualr 영역
float3 SpecColor2;
SpecColor2 = pow(rim, _SpecPow2) * _SpecCol2 * s.Gloss;
//final영역
final.rgb = DiffColor.rgb + SpecColor.rgb + rimColor.rgb + SpecColor2.rgb;
final.a = s.Alpha;
return final;
}
ENDCG
}
FallBack "Diffuse"
}