- Rim Light (림 라이트)
https://photoshopcafe.com/add-rim-light-photo-photoshop/
- Fresnel(프레넬)
- Standard Shader같은 물리 기반 쉐이더는 재질의 특성에 따른 반사율이 BRDF를 이용해 구현되어있음.
- 아래 사진과 같은 반사 공식을 프레넬이라고 한다.
- 기본적으로 게임에서는 배경과 캐릭터의 분리나 강조를 위해 Fresnel 공식을 과장되게 사용하곤 한다.
lightDir
벡터는 버텍스에서 바라보는 조명의 방향을 나타내는 단위벡터였으며, 이 벡터와 노멀 벡터를 내적하여 연산했다.viewDir
와 노멀벡터의 내적을 연산할 것이다.struct Input
{
float2 uv_MainTex;
float3 viewDir; //뷰벡터 받아오기
};
void surf (Input IN, inout SurfaceOutput o)
{
fixed4 c = tex2D (_MainTex, IN.uv_MainTex);
o.Albedo = 0; //순수한 Fresnel의 공식을 보기 위해 검은색으로 설정
//노멀벡터와 뷰벡터의 내적
float rim = dot(o.Normal, IN.viewDir);
o.Emission = rim;
o.Alpha = c.a;
}
o.Emission = 1 - rim;
o.Emission = pow(1 - rim, 3);
//Rim light의 넓이는 제곱으로 조절하고, 색상은 곱해주기
o.Emission = pow(1 - rim, _RimPower) * _RimColor.rgb;
o.Albedo = c.rgb;
o.Normal = UnpackNormal(tex2D(_BumpMap, IN.uv_BumpMap));
float rim = saturate(dot(o.Normal, IN.viewDir));
Shader "Custom/RimShader"
{
Properties
{
_MainTex ("Albedo (RGB)", 2D) = "white" {}
_BumpMap("NormalMap", 2D) = "bump"{}
_RimColor("RimColor", Color) = (1, 1, 1, 1)
_RimPower("RimPower", Range(1, 10)) = 3
}
SubShader
{
Tags { "RenderType"="Opaque" }
CGPROGRAM
#pragma surface surf Lambert
sampler2D _MainTex;
sampler2D _BumpMap;
float4 _RimColor;
float _RimPower;
struct Input
{
float2 uv_MainTex;
float2 uv_BumpMap;
float3 viewDir;
};
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));
float rim = saturate(dot(o.Normal, IN.viewDir));
o.Emission = pow(1 - rim, _RimPower) * _RimColor.rgb;
o.Alpha = c.a;
}
ENDCG
}
FallBack "Diffuse"
}
- 게임에서 캐릭터가 데미지를 입거나 선택할 때 등 자주 사용되는 방식
- 실제 존재하는 Rim 라이트를 구현했다고 하기엔 부족함
- 높은 품질을 위해서는 응용이 필요하다!
void surf(Input IN, inout SurfaceOutput o)
{
fixed4 c = tex2D(_MainTex, IN.uv_MainTex);
//o.Albedo = c.rgb;
float rim = saturate(dot(o.Normal, IN.viewDir));
o.Emission = pow(1 - rim, 3);
o.Alpha = c.a;
}
//1. Tags { "RenderType"="Opaque" }를 아래와 같이 수정
Tags { "RenderType"="Transparent" "Queue"="Transparent" }
//2. alpha:fade 추가
#pragma surface surf Lambert noambient alpha:fade
//3. surf 함수에서 rim제곱값을 rim에 다시 넣고, 알파에 rim 넣기
rim = pow(1 - rim, 3);
o.Alpha = rim;
o.Emission = float3(0, 1, 0);
➕ 색상을 속성으로 받아서 처리
Shader "Custom/HoloShader"
{
Properties
{
_MainTex("Albedo (RGB)", 2D) = "white" {}
_HoloColor("HologramColor", Color) = (0, 1, 0, 1)
}
SubShader
{
Tags { "RenderType" = "Transparent" "Queue" = "Transparent" }
CGPROGRAM
#pragma surface surf Lambert noambient alpha:fade
sampler2D _MainTex;
struct Input
{
float2 uv_MainTex;
float3 viewDir;
};
fixed4 _HoloColor;
void surf(Input IN, inout SurfaceOutput o)
{
fixed4 c = tex2D(_MainTex, IN.uv_MainTex) * _HoloColor;
//o.Albedo = c.rgb;
o.Emission = c.rgb;
float rim = saturate(dot(o.Normal, IN.viewDir));
rim = pow(1 - rim, 3);
o.Alpha = rim;
}
ENDCG
}
FallBack "Diffuse"
}
1. 깜박이는 효과
o.Alpha = rim * sin(_Time.y * 3); //_Time.y에 곱해진 수는 속도를 조절한다
-1
~1
사이를 반복하기 때문에 0 이하로 내려가는 부분이 절반이라 홀로그램이 깜빡일 때 안보이는 시간이 길다.Half Lambert
나 abs()
를 사용하여 0~1사이 값이 되도록 한다.//Half Lambert
o.Alpha = rim * sin(_Time.y * 0.5 + 0.5);
//abs() 함수 - 모든 음수를 양수로 변화
o.Alpha = rim * abs(sin(_Time.y));
float3 worldPos
(월드 공간상의 위치)☑️
worldPos
확인
- 정확한 확인을 위해 오브젝트를 검게 만들기
o.Emission = 0; o.Alpha = 1;
- Input에서 받아온
worldPos
출력- xyz가 각각 rgb에 대입되어 나타나는 모습
o.Emission = IN.worldPos.g;
frac()
함수를 사용해 숫자의 소수점 부분만 반환하면, 숫자가 증가할수록 0
~0.999
가 반복되는 결과가 나온다. o.Emission = frac(IN.worldPos.g);
pow()
함수를 사용해 검정 부분을 확장시킨다.o.Emission = pow(frac(IN.worldPos.g), 30);
worldPos.g
에 수를 곱해주고, _Time.y
를 빼서 위로 흘러가도록 만든다.o.Emission = pow(frac(IN.worldPos.g * 3 - _Time.y), 30);
Shader "Custom/HoloShader"
{
Properties
{
_BumpMap("NormalMap", 2D) = "bump"{} //필요없는 MainTex대신 NormalMap 받기
_RimPower("RimPower", Range(1, 10)) = 3
_HoloColor("Hologram Color", Color) = (0, 1, 0, 1)
_HoloGap("Hologram Gap", Range(1, 50)) = 5
_HoloTrans("Hologram Transparent", Range(0, 100)) = 10
}
SubShader
{
Tags { "RenderType" = "Transparent" "Queue" = "Transparent" }
CGPROGRAM
#pragma surface surf nolight noambient alpha:fade //Lambert 연산이 필요 없으므로 nolight로 수정
sampler2D _BumpMap;
fixed4 _HoloColor;
float _RimPower;
float _HoloGap;
float _HoloTrans;
struct Input
{
float2 uv_BumpMap;
float3 viewDir;
float3 worldPos;
};
void surf(Input IN, inout SurfaceOutput o)
{
o.Normal = UnpackNormal(tex2D(_BumpMap, IN.uv_BumpMap));
o.Emission = _HoloColor.rgb;
float rim = saturate(dot(o.Normal, IN.viewDir));
rim = saturate(pow(1 - rim, _RimPower) + pow(frac(IN.worldPos.g * 3 - _Time.y), _HoloGap) * _HoloTrans*0.01); //_HoloGap으로 줄무늬의 간격 조절, 전체적으로 _HoloTrans*0.01를 곱해 투명도 줄이기
o.Alpha = rim;
}
float4 Lightingnolight(SurfaceOutput s, float3 lightDir, float atten) { //nolight 커스텀 라이트 만들기
return float4(0, 0, 0, s.Alpha); //알파 채널만 리턴
}
ENDCG
}
FallBack "Diffuse"
}