[클라우디아 도피오슬래시]유니티 물리 기반 셰이더 개발 05
서피스 셰이더
서피스 셰이더는 서피스 라이팅 모델을 계산하는 특수한 유니티 셰이더의 종류 중 하나다.
최고 장점은 상당한 양의 보일러플레이트 코드를 감출 수 있는 것이다.
Pragmas
#pragma surface surf Standerd fullforwardshadows
surf는 서피스 함수 이름이고 Standard는 라이팅 모델이며 fullforwardshadows는 옵션이다.
신규 데이터 구조체
서피스 함수는 Input이라고 하는 데이터 구조체를 취한다.
struct Input{
float2 uv_MainTex;
};
그리고 inout이라는 타입 퀼리파이어가 붙은 SufaceOutputStandard
라는 데이터 구조체를 취한다.
struct SurfaceOutputStandard{
fixed3 Albedo; //기본 색상(디퓨즈 혹은 스펙큘러)
fixed3 Normal; // 탄젠트 공간 노멀
half3 Emission;
half Metallic; // 0=비금속성, 1=금속성
half Smoothness;// 0=거침, 1=부드러움
half Occlusion; // occlusion (default 1)
fixed Alpha; // 투명도 알파
};
서피스 셰이더 편집
두번째 알베도 맵 추가하기
Shader "Custom/SurfaceShaderSecondAlbedo"
{
Properties
{
_Color ("Color", Color) = (1,1,1,1)
_MainTex ("Albedo (RGB)", 2D) = "white" {}
_SecondAlbedo("Second Albedo (RGB)", 2D) = "white"{}
_AlbedoLerp("Albedo Lerp", Range(0,1)) = 0.5
_Glossiness ("Smoothness", Range(0,1)) = 0.5
_Metallic ("Metallic", Range(0,1)) = 0.0
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 200
CGPROGRAM
// 물리 기반 표준 라이팅 모델, 모든 타입의 광원에 그림자 활성화
#pragma surface surf Standard fullforwardshadows
// 라이팅 효과가 더 멋져 보이도록 셰이더 모델 3.0 타깃 사용
#pragma target 3.0
sampler2D _MainTex;
sampler2D _SecondAlbedo;
half _AlbedoLerp;
struct Input
{
float2 uv_MainTex;
};
half _Glossiness;
half _Metallic;
fixed4 _Color;
// 해당 셰이더에 인스턴스 서포트를 추가한다. 이 셰이더를 활요하는 재질에 `인스터싱 활성화`를 체크해야한다.
// 인스터싱에 대한 자세한 정보는 https://docs.unity3d.com/Manual/GPUInstancing.html
// #pragma instancing_options assumeuniformscaling
UNITY_INSTANCING_BUFFER_START(Props)
// 각 인스터스별 프로퍼티는 여기에 넣는다.
UNITY_INSTANCING_BUFFER_END(Props)
void surf (Input IN, inout SurfaceOutputStandard o)
{
// 색상이 스며든 텍스처에는 알베도값을 가져온다.
fixed4 c = tex2D (_MainTex, IN.uv_MainTex);
fixed4 secondAlbedo = tex2D(_SecondAlbedo, IN.uv_MainTex);
o.Albedo = lerp(c, secondAlbedo, _AlbedoLerp) * _Color;
// 금속성과 부드러움정도는 슬라이더 변수에서 가져온다.
o.Metallic = _Metallic;
o.Smoothness = _Glossiness;
o.Alpha = c.a;
}
ENDCG
}
FallBack "Diffuse"
}
노멀 맵 추가하기
Shader "Custom/SurfaceShaderNormalMap"
{
Properties
{
_Color ("Color", Color) = (1,1,1,1)
_MainTex ("Albedo (RGB)", 2D) = "white" {}
_NormalMap("Normal Map", 2D) = "bump" {}
_Glossiness ("Smoothness", Range(0,1)) = 0.5
_Metallic ("Metallic", Range(0,1)) = 0.0
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 200
CGPROGRAM
#pragma surface surf Standard fullforwardshadows
#pragma target 3.0
sampler2D _MainTex;
sampler2D _NormalMap;
struct Input
{
float2 uv_MainTex;
};
half _Glossiness;
half _Metallic;
fixed4 _Color;
UNITY_INSTANCING_BUFFER_START(Props)
UNITY_INSTANCING_BUFFER_END(Props)
void surf (Input IN, inout SurfaceOutputStandard o)
{
fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color;
o.Normal = UnpackNormal(tex2D(_NormalMap, IN.uv_MainTex));
o.Albedo = c.rgb;
o.Metallic = _Metallic;
o.Smoothness = _Glossiness;
o.Alpha = c.a;
}
ENDCG
}
FallBack "Diffuse"
}
그림자 작동 확인하기
맨 마지막 FallBack 값이 있다. 이는 메쉬의 그림자 렌더링에 사용하는 다른 셰이더의 이름이다.
다른 내장 라이팅 모델 사용
지금까지 표준 라이팅 모델을 사용했다. 대신 BlinnPhong
을 사용해보자.
Shader "Custom/SurfaceShaderBlinnPhong"
{
Properties
{
_Color ("Color", Color) = (1,1,1,1)
_MainTex ("Albedo (RGB)", 2D) = "white" {}
_SpecColor ("SpecularMAterial Color", Color) = (1,1,1,1)
_Shininess ("Shiniess", Range (0.03, 1)) = 0.078125
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 200
CGPROGRAM
#pragma surface surf BlinnPhong fullforwardshadows
#pragma target 3.0
sampler2D _MainTex;
float _Shininess;
struct Input
{
float2 uv_MainTex;
};
fixed4 _Color;
UNITY_INSTANCING_BUFFER_START(Props)
UNITY_INSTANCING_BUFFER_END(Props)
void surf (Input IN, inout SurfaceOutput o)
{
fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color;
o.Albedo = c.rgb;
o.Specular = _Shininess;
o.Gloss = c.a;
o.Alpha = 1.0f;
}
ENDCG
}
FallBack "Diffuse"
}
glossiness
와 metallic
에 대한 개념이 존재하지 않기 때문에 Gloss와
Specular
특성을 추가해 줘야한다.
커스텀 라이팅 모델
라이팅 모델 함수 시그니처
- 디퓨즈용 시그니처
half4 Lighting<Name> (SurfaceOutput s, unityGI gi);
- 뷰 종속 관련 시그니처
half4 Lighting<Name> (SurfaceOutput s, half3 viewDir, unityGI gi);
- SurfaceOutput 데이터 구조체
struct SurfaceOutput{ fixed3 Albedo; fixed3 normal; fixed3 Emission; //빛을 스스로 발산하는 객체 half Specular; fixed Gloss; fixed Alpha; };
커스텀 라이팅
Shader "Custom/LightingPhong"
{
Properties
{
_Color ("Color", Color) = (1,1,1,1)
_MainTex ("Albedo (RGB)", 2D) = "white" {}
_SpecColor ("SpecularMAterial Color", Color) = (1,1,1,1)
_Shininess ("Shiniess", Range (0.03, 128)) = 0.078125
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 200
CGPROGRAM
#pragma surface surf Phong fullforwardshadows
#pragma target 3.0
sampler2D _MainTex;
float _Shininess;
fixed4 _Color;
struct Input
{
float2 uv_MainTex;
};
UNITY_INSTANCING_BUFFER_START(Props)
UNITY_INSTANCING_BUFFER_END(Props)
inline void LightingPhong_GI(SurfaceOutput s, UnityGIInput data, inout UnityGI gi) {
gi = UnityGlobalIllumination(data, 1.0, s.Normal);
}
inline fixed4 LightingPhong (SurfaceOutput s, half3 viewDir, UnityGI gi) {
UnityLight light = gi.light;
float nl = max(0.0f, dot(s.Normal, light.dir));
float3 diffuseTerm = nl * s.Albedo.rgb * light.color;
float3 reflectionDirection = reflect(-light.dir, s.Normal);
float3 specularDot = max(0.0, dot(viewDir, reflectionDirection));
float3 specular = pow(specularDot, _Shininess);
float3 specularTerm = specular * _SpecColor.rgb * light.color.rgb;
float3 finalColor = diffuseTerm.rgb + specularTerm;
fixed4 c;
c.rgb = finalColor;
c.a = s.Alpha;
#ifdef UNITY_LIGHT_FUNCTION_APPLY_INDIRECT
c.rgb += s.Albedo * gi.indirect.diffuse;
#endif
return c;
}
void surf (Input IN, inout SurfaceOutput o)
{
fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color;
o.Albedo = c.rgb;
o.Alpha = 1.0f;
}
ENDCG
}
FallBack "Diffuse"
}