일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 | 31 |
- C++
- c#
- 포인터
- c언어
- jupyter
- c# 추상 클래스
- vim
- Houdini
- Data Structure
- 플러터
- c# winform
- 다트 언어
- Unity
- 도커
- gitlab
- jupyter lab
- 깃
- Flutter
- C언어 포인터
- github
- git
- Python
- c# 윈폼
- Algorithm
- 구조체
- C# delegate
- dart 언어
- docker
- 유니티
- HTML
- Today
- Total
nomad-programmer
[CG/Unity] Cube Map 본문
Shader "Custom/Reflection"
{
Properties
{
_MainTex ("Albedo (RGB)", 2D) = "white" {}
_Cube ("Cubemap", Cube) = ""{}
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 200
CGPROGRAM
#pragma surface surf Lambert noambient
#pragma target 3.0
sampler2D _MainTex;
samplerCUBE _Cube;
struct Input
{
float2 uv_MainTex;
float3 worldRefl;
};
void surf (Input IN, inout SurfaceOutput o)
{
fixed4 c = tex2D (_MainTex, IN.uv_MainTex);
float4 re = texCUBE(_Cube, IN.worldRefl);
o.Albedo = 0;
o.Emission = re.rgb;
o.Alpha = c.a;
}
ENDCG
}
FallBack "Diffuse"
}
Input 구조체 안에 float2의 UV가 아닌 반사 벡터를 받아온다. 이것을 UV로 쓰게 된다. Cubemap은 주변을 둘러싸고 있는 3차원 공간의 텍스처이기 때문이다.
float3 worldRefl;
그 후 surf 함수 안에 Cubemap 텍스처를 연산하는 함수인 texCUBE를 이용하여 텍스처의 컬러를 만든다.
float4 re = texCUBE(_Cube, IN.worldRefl);
그리고 이것을 Emission에 넣어준다. Emission에 넣는 이유는, 반사 재질이란 '주변을 반사하는' 것이다. 반사만 생각해 본다면 빛을 비춘다고 거기에 영향을 받아 밝아지거나 어두워지는 것이 아니라 '밝거나 어두운 주변 이미지를 반사'하는 것 뿐이기 때문이다.
o.Albedo = 0;
albedo에 이미 값이 있다면 emission의 반사 이미지가 더해져서 하얗게 떠 보이는 현상이 나타난다. 즉, 최종 결과가 1이 한참 넘어가는 결과가 나오면서 물리적 법칙을 넘어서는 빛의 양이 된 것이다.
그러므로 이렇게 반사가 100%인 이미지를 만들려면 Albedo의 이미지를 0으로 만들어야 한다. 그래야 디퓨즈(Diffuse) 0% / 스펙큘러(Specular) 100%인 이미지가 나온다.
참고로 만약 적당히 절반 정보 반사가 일어나는 정도라면 Albedo와 Emission에 각각 0.5를 곱해줘서 서로의 영향을 절반씩 줄이면 된다.
Cubemap은 기본적으로 반사를 표현할 때 쓰이겠지만, 빈약한 환경광(Ambient Color) 기능을 개선하기 위해서도 사용하곤 한다. 큐브맵을 매우 뿌옇게 만든 다음 (주로 사이즈를 극도로 줄이는 방법을 사용) 그것을 입히면 주변의 산란광들이 캐릭터에 닿는 것처럼 보이기 때문에 Ambient Cube라는 이름으로 이런 방식을 사용하기도 했었다. 그리고 이렇게 이미지를 이용해서 조명을 표현하는 방식을 IBL(Image Based Lighting)이라고 한다.
Normal Map 적용
평범하게 NormalMap을 적용하면 애석하게도 에러가 발생한다.
float4 re = texCUBE(_Cube, IN.worldRefl);
o.Albedo = 0;
o.Normal = UnpackNormal(tex2D(_BumpMap, IN.uv_BumpMap));
o.Emission = re.rgb;
o.Alpha = c.a;
이러한 에러가 발생하는 경우는 Input에서 float3 worldRefl이나 float3 worldNormal과 같이 '벅텍스 월드 노멀'에 관련된 데이터를 받아와 surf 함수 내부에서 사용하면서, 동시에 '탄젠트 노멀'인 UnpackNormal 함수를 거친 노멀 데이터를 함수 내부에서 같이 사용하면 에러가 발생한다.
NormalMap은 사실 전부 파랑색이다. 즉, 모든 픽셀이 '픽셀 자신이 중심축' 을 가진 듯한 좌표계이다. 이 좌표계를 '탄젠트 좌표계' 라고 한다.
현재 NormalMap을 사용해야 하고, NormalMap에 대응되는 반사 벡터를 사용해야 하므로 '픽셀 월드 노멀' 로 변환해야 한다. 이것을 만드는 방법은 다음과 같다.
struct Input
{
float2 uv_MainTex;
float2 uv_BumpMap;
float3 worldRefl;
INTERNAL_DATA
};
void surf (Input IN, inout SurfaceOutput o)
{
o.Normal = UnpackNormal(tex2D(_BumpMap, IN.uv_BumpMap));
fixed4 c = tex2D (_MainTex, IN.uv_MainTex);
float4 re = texCUBE(_Cube, WorldReflectionVector(IN, o.Normal));
o.Albedo = 0;
o.Emission = re.rgb;
o.Alpha = c.a;
}
우선 버텍스로부터 받는 반사 벡터는 worldRefl이 존재해야 한다. 그리고 추가로 INTERNAL_DATA라는 키워드를 추가한다.
INTERNAL_DATA 키워드란?
이 키워드를 발동시키면 버텍스 노멀 데이터를 픽셀 노멀 데이터로 변환시키기 위한 행렬 연산이 가동된다. 참고로 키워드이므로 ;을 붙이지 않는다.
NormalMap이 들어간 '픽셀 월드 노멀' 을 구할 때도 동일한 방법이 응용된다. 아래는 노멀 상태를 비교한 결과이다.
- UnpackNormal의 결과물을 그냥 출력한 결과물. 전부 파랑색인 탄젠트 좌표계의 노멀이다.
Shader "Custom/Reflection"
{
Properties
{
_BumpMap ("NormalMap", 2D) = "bump"{}
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 200
CGPROGRAM
#pragma surface surf Lambert noambient
#pragma target 3.0
sampler2D _BumpMap;
struct Input
{
float2 uv_BumpMap;
};
void surf (Input IN, inout SurfaceOutput o)
{
o.Normal = UnpackNormal(tex2D(_BumpMap, IN.uv_BumpMap));
o.Emission = o.Normal;
}
ENDCG
}
FallBack "Diffuse"
}
- 버텍스 월드 노멀을 받아와서 출력한 상태. 버텍스 사이의 보간(interpolation)이 보인다.
Shader "Custom/Reflection"
{
Properties
{
_BumpMap ("NormalMap", 2D) = "bump"{}
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 200
CGPROGRAM
#pragma surface surf Lambert noambient
#pragma target 3.0
sampler2D _BumpMap;
struct Input
{
float2 uv_BumpMap;
float3 worldNormal;
};
void surf (Input IN, inout SurfaceOutput o)
{
// o.Normal = UnpackNormal(tex2D(_BumpMap, IN.uv_BumpMap));
o.Emission = IN.worldNormal;
}
ENDCG
}
FallBack "Diffuse"
}
- NormalMap이 적용된 월드 좌표계의 픽셀 노멀을 출력시킨 상태
Shader "Custom/Reflection"
{
Properties
{
_BumpMap ("NormalMap", 2D) = "bump"{}
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 200
CGPROGRAM
#pragma surface surf Lambert noambient
#pragma target 3.0
sampler2D _BumpMap;
struct Input
{
float2 uv_BumpMap;
float3 worldNormal;
INTERNAL_DATA
};
void surf (Input IN, inout SurfaceOutput o)
{
o.Normal = UnpackNormal(tex2D(_BumpMap, IN.uv_BumpMap));
float3 worldNormal = WorldNormalVector(IN, o.Normal);
o.Emission = worldNormal;
}
ENDCG
}
FallBack "Diffuse"
}
최종 결과물
Shader "Custom/Reflection"
{
Properties
{
_MainTex ("Albedo (RGB)", 2D) = "white" {}
_BumpMap ("NormalMap", 2D) = "bump"{}
_Cube ("Cubemap", Cube) = ""{}
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 200
CGPROGRAM
#pragma surface surf Lambert noambient
#pragma target 3.0
sampler2D _MainTex;
sampler2D _BumpMap;
samplerCUBE _Cube;
struct Input
{
float2 uv_MainTex;
float2 uv_BumpMap;
float3 worldRefl;
INTERNAL_DATA
};
void surf (Input IN, inout SurfaceOutput o)
{
o.Normal = UnpackNormal(tex2D(_BumpMap, IN.uv_BumpMap));
fixed4 c = tex2D (_MainTex, IN.uv_MainTex);
float4 re = texCUBE(_Cube, WorldReflectionVector(IN, o.Normal));
o.Albedo = c.rgb * 0.5;
o.Emission = re.rgb * 0.5;
o.Alpha = c.a;
}
ENDCG
}
FallBack "Diffuse"
}
'CG > Unity' 카테고리의 다른 글
[CG/Unity] 커스텀 알파 블랜딩 (0) | 2022.02.02 |
---|---|
[CG/Unity] Z Buffer (0) | 2022.02.02 |
[CG/Unity] Diffuse Warping (0) | 2022.02.01 |
[CG/Unity] Toon Shader (0) | 2022.02.01 |
[CG/Unity] Vertex Shader (0) | 2022.01.30 |