일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- 유니티
- Data Structure
- c# 추상 클래스
- Python
- jupyter
- C# delegate
- gitlab
- Flutter
- 구조체
- docker
- Unity
- c언어
- vim
- c#
- 플러터
- git
- jupyter lab
- C언어 포인터
- c# winform
- c# 윈폼
- Algorithm
- dart 언어
- 깃
- 다트 언어
- 도커
- Houdini
- HTML
- 포인터
- C++
- github
- Today
- Total
nomad-programmer
[CG/Unity] Matcap (메터리얼 캡쳐) 본문
Matcap이란 Material Capture의 준말로, Ramp Texture와 비슷하게 이미지로 조명을 대신하는 방식 중 하나이다.
Matcap은 라이트의 영향을 최소화해야 하는 모바일 게임에서 최적화를 위해 자주 사용되는 '가짜' 라이팅 방식이며, ViewDir을 기반으로 하므로 카메라를 회전하지 않는 상황에서 좋은 효과를 볼 수 있는 기능이다. 이 방식을 사용하면 라이트를 '전혀 사용하지 않고'도 조명을 받은 것 같은 효과를 낼 수 있다.
Shader "Custom/matcap"
{
Properties
{
_MainTex ("Albedo (RGB)", 2D) = "white" {}
_Matcap ("Matcap", 2D) = "white" {}
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 200
CGPROGRAM
#pragma surface surf Nolight noambient
#pragma target 3.0
sampler2D _MainTex;
sampler2D _Matcap;
struct Input
{
float2 uv_MainTex;
float3 worldNormal;
};
void surf (Input IN, inout SurfaceOutput o)
{
fixed4 c = tex2D (_MainTex, IN.uv_MainTex);
o.Alpha = c.a;
}
float4 LightingNolight(SurfaceOutput s, float3 lightDir, float atten){
return float4(0, 0, 0, s.Alpha);
}
ENDCG
}
FallBack "Diffuse"
}
아주 기본적인 코드 구성이다. 이제 여기에서부터 맷캡을 적용하자.
저 셀 셰이딩같은 느낌의 MatCap 텍스처가 조명이 될 것이다. 저 텍스처를 적용하기 위한 UV를 연산하는 것이 핵심이다.
IN.worldNormal은 world 좌표계이기 때문에, 이 normal은 카메라를 돌려도 카메라와 상관없이 고정되어 있다. 그래서 이것은 view 좌표계로 변환해야 한다.
worldNormal에다가 view 좌표계 즉, view Matrix를 구해서 view 좌표계의 Normal로 만들어 보자.
float3 viewNormal = mul((float3x3)UNITY_MATRIX_V, IN.worldNormal.rgb);
UNITY_MATRIX_V는 유니티 안에 내장된 'view 좌표계' 매트릭스이다. mul 함수는 매트릭스 곱셈을 위한 함수이다.
이렇게 하면, normal이 바뀐다. 카메라를 돌리는 것에 따라 그 방향이 따라온다. 즉, view 좌표계의 normal이 된 것이다. 아무리 카메라를 돌려도 왼쪽이 파랗다는 것을 볼 수 있다.
view normal을 구했으니 UV를 구하면되는데, view normal의 x, y를 u, v로 사용하면 된다. 그런데 normal 값 그대로를 사용하면 안된다. 그 이유는 Normal은 180도를 표현해야 하는데 0~1 로는 90도밖에 표현하지 못하기 때문이다.
-1~1 이어야 180도가 표현 가능하다. 즉, NormalMap은 일반적인 텍스쳐이기 때문에 0~1로 되어 있으니, NormalMap에 *2-1을 연산해서 0~1을 -1~1로 만들어 주는 것이다. 이것이 NormalMap이 어정쩡하게 파란색인 이유이다. NormalMap에 *2-1 연산을 해주면 확실히 새파랗게 변하게 된다.
float3 viewNormal = mul((float3x3)UNITY_MATRIX_V, IN.worldNormal.rgb);
float2 matCapUV = viewNormal.xy * 0.5 + 0.5;
이제 이 UV를 이용하여 Matcap 텍스쳐를 연산해주자.
Shader "Custom/matcap"
{
Properties
{
_MainTex ("Albedo (RGB)", 2D) = "white" {}
_Matcap ("Matcap", 2D) = "white" {}
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 200
CGPROGRAM
#pragma surface surf Nolight noambient
#pragma target 3.0
sampler2D _MainTex;
sampler2D _Matcap;
struct Input
{
float2 uv_MainTex;
float3 worldNormal;
};
void surf (Input IN, inout SurfaceOutput o)
{
fixed4 c = tex2D (_MainTex, IN.uv_MainTex);
float3 viewNormal = mul((float3x3)UNITY_MATRIX_V, IN.worldNormal.rgb);
float2 MatcapUV = viewNormal.xy * 0.5 + 0.5;
o.Emission = tex2D(_Matcap, MatcapUV) * c.rgb;
o.Alpha = c.a;
}
float4 LightingNolight(SurfaceOutput s, float3 lightDir, float atten){
return float4(0, 0, 0, s.Alpha);
}
ENDCG
}
FallBack "Diffuse"
}
Normal을 사용하는 Matcap을 만들기 위해서는 NormalMap이 사용된 worldNormal을 만들어야 한다. worldNormal과 UnpackNormal을 함께 사용하는 상황이 만들어지는데, 이런 경우 서피스 셰이더에서 사용할 수 있는 함수는 worldNormalVector 이다.
Shader "Custom/matcap"
{
Properties
{
_MainTex ("Albedo (RGB)", 2D) = "white" {}
_BumpMap ("Normalmap", 2D) = "bump" {}
_Matcap ("Matcap", 2D) = "white" {}
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 200
CGPROGRAM
#pragma surface surf Nolight noambient
#pragma target 3.0
sampler2D _MainTex;
sampler2D _BumpMap;
sampler2D _Matcap;
struct Input
{
float2 uv_MainTex;
float2 uv_BumpMap;
float3 worldNormal;
INTERNAL_DATA
};
void surf (Input IN, inout SurfaceOutput o)
{
fixed4 c = tex2D (_MainTex, IN.uv_MainTex);
o.Normal = UnpackNormal(tex2D(_BumpMap, IN.uv_BumpMap));
float3 worldNorm = WorldNormalVector(IN, o.Normal);
float3 viewNormal = mul((float3x3)UNITY_MATRIX_V, worldNorm);
float2 MatcapUV = viewNormal.xy * 0.5 + 0.5;
o.Emission = tex2D(_Matcap, MatcapUV) * c.rgb;
o.Alpha = c.a;
}
float4 LightingNolight(SurfaceOutput s, float3 lightDir, float atten){
return float4(0, 0, 0, s.Alpha);
}
ENDCG
}
FallBack "Diffuse"
}
'CG > Unity' 카테고리의 다른 글
[CG/Unity] Triplanar (2) | 2022.03.13 |
---|---|
[CG/Unity] 굴절 만들기 (0) | 2022.03.13 |
[CG/Unity] 타 들어가며 없어지는 셰이더 (0) | 2022.02.02 |
[CG/Unity] 2Pass를 이용한 깨끗한 알파 블랜딩 (1) | 2022.02.02 |
[CG/Unity] 커스텀 알파 블랜딩 (0) | 2022.02.02 |