기존 쉐이더와는 다르게, 주변 환경에 따른 재질 변화를 물리 법칙에 기반하여 실시간으로 재질을 구현해주는 사실적인 쉐이더 표현 기법
Albedo, Normal, Emission, Metallic, Smoothness, Occlusion, Alpha 등 다양한 물리 기반 쉐이더의 요소들이 SurfaceOutputStandard 구조체에 정의되어 있음.
Metallic과 Smoothness
Metallic과 Smoothness 추가
Smoothness
재질이 미끄러운지 거친지 결정
0이면 완벽히 거칠어서 난반사만 일어나며 1이면 완벽히 매끄러워서 정반사만 일어난다. 유니티에서 Standard Shader라는물리 기반 렌더링의 기본 개념은에너지 보존 법칙으로나가는 빛의 양은 들어온 빛의 양을 넘을 수 없다이기 때문에 정반사가 높아질수록 난반사의 비율은 줄어든다. (반대도 해당)
Normal map
Normal map (노멀맵)
텍스쳐를 이용하여 실제 디테일이 없는 부분들 디테일이 있는 것처럼 보이게 만들기 위한 눈속임 맵
벡터 데이터들로 이루어진 텍스쳐 파일
일반적인 게임용 텍스쳐 포맷인 DXT1 혹은 DXT5이 아닌 DXTnm이라는 파일 포맷 (일반적인 텍스쳐 압축에 의한 노멀맵 품질 저하를 막기 위해 만든 AG 파일 포맷임)
노멀맵은 ZBrush나 Mudbox 같은 Sculpting 툴을 이용하거나 3D 프로그램에서 하이 폴리곤 모델링을 한 후에 RTT를 이용하여 추출하는 등 다양한 방식으로 추출할 수 있다.
Unity에서 Normal map 추출하는 방법
Inspector에서 Texture type을 Normalmap 으로 변경 (외부 툴에서 추출되어 이미 파랗게 된 노멀맵이라면 해당 단계까지만 진행)
Create from Grayscale을 선택하고 Bumpiness나 Filtering 조절
Apply
코드 변경을 통한 노멀맵 적용
Occlusion (오클루젼)
Occlusion이란?
Ambient Occlusion : Ambient Color (환경광)이 닿지 못하는 부분
구석진 부분의 추가적인 음영 표현
Occlusion 기능을 사용하는 법은 일반적인 텍스쳐를 받는 방법과 동일하나, 주의해야 할 점은 반드시 _MainTex와 같은 UV를 사용해야 정상적으로 작동한다는 것이다.
응용
위에서 4가지 텍스쳐를 멀티텍스쳐링 해서 제작한 plane에 노멀맵을 적용해보기 위해 스크립트에 노멀맵을 추가하였더니 아래와 같이 쉐이더가 적용되지 않고 오류가 발생했다.
해당 오류는 쉐이더 2.0의 한계를 벗어나는 텍스쳐 인터폴레이션으로 인해 발생한 현상이다. 따라서#pragma target 3.0을 추가하여 해결하면 된다.
Plane에 NormalMap 적용
빛의 각도를 조정하지 않고도 NormalMap을 보이게 하려면? Inspector로 Smoothness와 Metallic을 조절하면 된다.
Smoothness 조절을 통해 땅 젖은 느낌 적용
코드를 다양하게 변경하여 텍스쳐의 질감을 마음대로 조절할 수 있다. 현재 사용한 코드는 다음과 같다.
위에서 제작한 불 이펙트를 업그레이드 해보기 위한 과정이다. 우선, 확실한 효과를 보기 위하여 불 이미지를 아래와 같이 체크 이미지로 변경하였으며 두 번째 이미지는 코드 내에서_MainTex2 ("Albedo (RGB)", 2D) = "black" {}을 추가하여 검정색 이미지를 출력하도록 하였다.
아래와 같이 코드를 변경하여 c의 uv에 d.r을 더해주어도 아무 변화가 일어나지 않는다. d는 float4(0,0,01)이기 때문이다.
void surf (Input IN, inout SurfaceOutputStandard o)
{
fixed4 d = tex2D(_MainTex2, IN.uv_MainTex2);
fixed4 c = tex2D(_MainTex, IN.uv_MainTex + d.r);
o.Emission = c.rgb;
o.Alpha = c.a;
}
ENDCG
d 텍스쳐의 색상에 따라 변경되는 이미지의 이동 범위
d 텍스쳐의 이미지에 따라 다르게 출력되는 c 텍스쳐의 이미지
위로 흐르도록 코드 변경
void surf (Input IN, inout SurfaceOutputStandard o)
{
fixed4 d = tex2D(_MainTex2, float2(IN.uv_MainTex2.x, IN.uv_MainTex2.y - _Time.y));
fixed4 c = tex2D(_MainTex, IN.uv_MainTex + d.r);
o.Emission = c.rgb;
o.Alpha = c.a;
}
Shader "Custom/Practice_Part5"
{
Properties
{
_Color ("Color", Color) = (1,1,1,1)
_MainTex ("Albedo (RGB)", 2D) = "white" {}
_Glossiness ("Smoothness", Range(0,1)) = 0.5
_Metallic ("Metallic", Range(0,1)) = 0.0
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 200
CGPROGRAM
// Physically based Standard lighting model, and enable shadows on all light types
#pragma surface surf Standard fullforwardshadows
// Use shader model 3.0 target, to get nicer looking lighting
#pragma target 3.0
sampler2D _MainTex;
struct Input
{
float2 uv_MainTex;
};
half _Glossiness;
half _Metallic;
fixed4 _Color;
// Add instancing support for this shader. You need to check 'Enable Instancing' on materials that use the shader.
// See https://docs.unity3d.com/Manual/GPUInstancing.html for more information about instancing.
// #pragma instancing_options assumeuniformscaling
UNITY_INSTANCING_BUFFER_START(Props)
// put more per-instance properties here
UNITY_INSTANCING_BUFFER_END(Props)
void surf (Input IN, inout SurfaceOutputStandard o)
{
// Albedo comes from a texture tinted by color
fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color;
o.Albedo = c.rgb;
// Metallic and smoothness come from slider variables
o.Metallic = _Metallic;
o.Smoothness = _Glossiness;
o.Alpha = c.a;
}
ENDCG
}
FallBack "Diffuse"
}
위 코드에서 Texture 한 장을 출력하기 위한 코드를 제외하고 필요 없는 코드를 모두 정리해보면 아래와 같다.
"Albedo (RGB)" : 해당 부분은 Albedo 텍스쳐를 넣는 곳이고, 알파는 사용하지 않고 RGB 채널만 사용하겠다는 의미 2D : 해당 인터페이스가 2D 텍스쳐를 받는 부분이라는 의미 "white" {} : 해당 텍스쳐 인터페이스의 초기 default 값은 흰색 텍스쳐라는 의미
인터페이스를 통해 입력받은 텍스쳐를 변수로 받는 코드
sampler2D _MainTex;
구조체 내부에서 uv를 받아오는 코드
float2 uv_MainTex;
uv : float2이며 텍스쳐는 이 uv 좌표와 text2D를 통해 계산되어야 float4로 출력할 수 있음. uv는 vertex가 가지고 있기 때문에 이렇듯이 우리가 만든 인터페이스가 아닌 vertex 내부의 것을 엔진에게 명령할 때에는 Input 구조체를 사용해야 함.
텍스쳐를 연산하여 컬러를 화면에 출력하는 코드
fixed4 c = tex2D (_MainTex, IN.uv_MainTex);
이미지 흑백 전환
흑백 이미지의 두 가지 속성
- R,G,B 모두 동일한 숫자로 이루어짐 - 해당 숫자는 R,G,B 각 요소에 따른 강도의 평균이어야 함
o.Albedo = (c.r+c.g+c.b)/3;
o.Albedo = c.rgb;에서o.Albedo = (c.r+c.g+c.b)/ 3;로 코드를 변화하니 위와 같이 이미지가 흑백으로 변경된 것을 볼 수 있다.
5. text2D float 계열로 분류되지 않는 sampler들 (2D텍스쳐를 받는 인터페이스)
_Name ("display name", 2D) = "name" {options}
색상 출력 및 연산
유니티 자체 스크립트가 아닌 CG 언어를 이용하여 쉐이더를 짜는CGPRGRAM ~ ENDCG영역
CGPROGRAM
// Physically based Standard lighting model, and enable shadows on all light types
#pragma surface surf Standard fullforwardshadows
// Use shader model 3.0 target, to get nicer looking lighting
#pragma target 3.0
#pragma enable_d3d11_debug_symbols
sampler2D _MainTex;
struct Input
{
float2 uv_MainTex;
};
half _Glossiness;
half _Metallic;
fixed4 _Color;
void surf (Input IN, inout SurfaceOutputStandard o)
{
// Albedo comes from a texture tinted by color
fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color;
o.Albedo = c.rgb;
// Metallic and smoothness come from slider variables
o.Metallic = _Metallic;
o.Smoothness = _Glossiness;
o.Alpha = c.a;
}
ENDCG
1. 설정 부분 전처리 또는 스니핏(snippet)이라 부름. 쉐이더의 조명 계산 설정이나 세부적인 분기를 정해주는 부분
// Physically based Standard lighting model, and enable shadows on all light types
#pragma surface surf Standard fullforwardshadows
// Use shader model 3.0 target, to get nicer looking lighting
#pragma target 3.0
2. Input 구조체 엔진으로부터 받아와야 할 데이터
struct Input{
float2 uv_MainTex;
};
3. 함수 영역 색상이나 이미지가 출력되는 부분
void surf (Input IN, inout SurfaceOutputStandard o)
{
// Albedo comes from a texture tinted by color
fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color;
o.Albedo = c.rgb;
// Metallic and smoothness come from slider variables
o.Metallic = _Metallic;
o.Smoothness = _Glossiness;
o.Alpha = c.a;
}
o.Albed: 빛의 영향을 받는 색이 출력
o.Emmision: 빛의 영향을 받지 않는 색이 출력위와 같이 변수를 사용하여 출력 색상 변경 또한 가능하다.
색상 입력 받고 출력 ex)properties에_TestColor("TestColor", Color) = (1,1,1,1)을 추가하여 인스펙터에 나타내고,float4 _TestColor;로 변수를 추가한 뒤, surf 함수 내에서o.Albedo = _TestColor.rgb;을 추가
즉, 인터페이스에서 원하는 타입의 변수를 생성한 후, SubShader 코드에 선언하고, surf함수에서 변수를 사용하면 출력 가능