큐브 맵은 Sky box에서 사용하기도 하지만,
큐브 맵 텍스처를 일반 오브젝트에 반사재질로 사용하면, 오브젝트에서 하늘에 반사되는 효과를 볼 수 있습니다.
Cube map 텍스처를 사용하여 건물을 렌더링 해봅시다.
먼저 렌더링 패스를 이해해 봅니다.
=======================================================
SurfaceShader 에서 Light를 표현할때, #pragma 에 Lambert를 추가해도 되지만,
Custom Lighting을 하기 위해서는 Lighting + Custom API name 으로 #pragma 에 추가 한뒤,
함수 이름으로 정의를 하면 됩니다.
아래 예제에서는 함수 명이 LightingTest 이기 때문에 , #pragma Test를 추가 해줬습니다.
추가를 하게 되면,
surf 함수가 먼저 실행이 되고, SurfaceOutput o 함수 인자가 다시 LighitngTest의 첫번째 인자 SurfaceOutput s 로
들어가게 됩니다.
즉 파이프라인은 surf --> LighitngTest 순서로 진행이 됩니다.
=======================================================
Reflection을 적용해봅니다.
준비 과정은 다음과 같습니다.
1) 에셋스토어에서 Skybox를 몇개 다운을 받구요
2) 하늘에 Skybox를 넣어봅니다.
===================================================
큐브맵은
큐브맵의 구조는 정육면체에 사진 6개 붙인 형태의 타입니다.
정육면체의 전개도를 보시면
Z : 앞뒤, X : 왼쪽, 오른쪽, Y : 위 아래 입니다.
텍스처 샘플링과 큐브맵 샘플링은 텍스처에서 샘플링하는 방식이 동일하기 떄문에
API도 비슷하게 사용이 됩니다.
- 큐브맵은 Property Type은 "Cube" 이며, CG Type은 "samplerCUBE" , 샘플링 함수는 texCUBE
- 텍스처 Property Type은 "2D" 이며, CG Type은 "sampler2D", 샘플링 함수는 tex2D
아래 1. 그림은 커다란 정육면체를 감싸고 있는 6개의 텍스처가 있으며,
정육면체 안에 있는 오브젝트의 하나의 픽셀 P를 나타낸 것 입니다.
큐브맵의 텍스처를 샘플링하는 방식은 specular 에서 refelection vector를 구하는 방식과 비슷한데요
카메라 방향벡터가 I라고 한다면, I와 지면의 P점에 반사된 방향벡터가 R 입니다.
방향벡터 R을 연장시켰을때 정육면체 큐브맵 텍스처와 만나는 부분을 Sampling Point라고 나타내었습니다.
해당 지점에 대응하는 큐브맵 텍스처 칼러값을 가져옵니다.
그렇기 때문에 맥락상 방향 벡터 p를 3차원 uv로 볼 수 있습니다.
I : View Vector
N : Normal Vector
R : 반사 Vector
R 벡터는 다음과 같이 표현 될 수 있다.
유도 과정은 다음과 같습니다.
공식에서 필요한 값은 노멀벡터와 뷰 벡터 입니다.
서피스 함수에서 뷰벡터를 가져오기 위해서는 Input Struct에 viewDir을 선언해서 사용하면 됩니다.
위 공식으로 사용해서 구현을 해도 되고, 이미 유니티에서 구현된 변수를 사용해도 됩니다.
구현된 변수이름은 worldRefl 입니다.
그리고 샘플링 된 컬러값은 빛에 영향받지 말아야 합니다.
그래서 Albedo 값에 넣지 말고 emissive에 컬러값을 넣어야 합니다.
Shader "Custom/Reflection"
{
Properties
{
_Reflect("Reflect", Range(0, 1)) = 0
_Color("_Color", Color) = (1, 1, 1, 1)
_MainTex("Main Tex", 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;
float4 _Color;
float _Reflect;
struct Input
{
float2 uv_MainTex;
float2 uv_BumpMap;
float3 worldRefl;
float3 viewDir;
INTERNAL_DATA
};
void surf(Input IN, inout SurfaceOutput o)
{
float3 refl = 2.0 * o.Normal * dot(o.Normal, IN.viewDir) - IN.viewDir;
//float3 refl = IN.worldRefl;
float4 re = texCUBE(_Cube, refl);
o.Albedo = 0;
o.Emission = re.rgb;
}
ENDCG
}
FallBack "Diffuse"
}
잘 나옵니다.
여기에 원래 빌딩 건물의 색상 Albedo + Normal을 적용해 봅니다.
Shader "Custom/Reflection"
{
Properties
{
_Reflect("Reflect", Range(0, 1)) = 0
_Color("_Color", Color) = (1, 1, 1, 1)
_MainTex("Main Tex", 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;
float4 _Color;
float _Reflect;
struct Input
{
float2 uv_MainTex;
float2 uv_BumpMap;
float3 worldRefl;
};
void surf(Input IN, inout SurfaceOutput o)
{
float4 c = tex2D(_MainTex, IN.uv_MainTex);
float4 re = texCUBE(_Cube, IN.worldRefl);
float3 n = UnpackNormal(tex2D(_BumpMap, IN.uv_BumpMap));
o.Albedo = c;
o.Normal = n;
o.Emission = re.rgb;
o.Alpha = c.a;
}
ENDCG
}
FallBack "Diffuse"
}
Shader error in 'Custom/Reflection': Surface shader Input structure needs INTERNAL_DATA for this WorldNormalVector or WorldReflectionVector usage at line 123 (on d3d11)
worldRefl 벡터와 UnPackNormal을 같이 surf 함수 내에서 호출하면, 내부 API 에러가 발생한다고 하네요.
struct Input
{
float2 uv_MainTex;
float2 uv_BumpMap;
float3 worldRefl;
INTERNAL_DATA
};
o.Normal = UnpackNormal(tex2D (_BumpMap, IN.uv_BumpMap));
float4 re = texCUBE(_Cube, WorldReflectionVector(IN, o.Normal));
WorldReflectionVector 함수를 사용하여 Normal 값을 사용하여 월드공간 반사 벡터를 구해옵니다.
Input 구조체에 있는 worldRefl 사용을 안하는 듯 보이지만 WorldReflectionVector 함수 내부에서 사용이 되기 때문에,
float3 worldRefl 을 반드시 선언해야 한다.
선언할때 INTERNAL_DATA 키워드를 같이 추가해야 한다.
worldRefl을 사용하지 않고 직접 구한 공식을 사용하면, 하늘이 제대로 표현되지 못하는 부분이 나온다.
그 이유는 WorldReflectionVector 함수내부를 까봐야 알 것 같은데.....
원인을 좀 파악은 나중에 하고..... WorldReflectionVector 함수를 일단 사용하기로 하자...
전체 코드입니다.
Shader "Custom/Reflection"
{
Properties
{
_Reflect("Reflect", Range(0, 1)) = 0
_Color("_Color", Color) = (1, 1, 1, 1)
_MainTex("Main Tex", 2D) = "white" {}
_BumpMap("NormalMap", 2D) = "bump" {}
_Cube("Cubemap", Cube) = "" {}
}
SubShader
{
Tags { "RenderType" = "Opaque" }
LOD 200
CGPROGRAM
#pragma surface surf Test noambient
#pragma target 3.0
sampler2D _MainTex;
sampler2D _BumpMap;
samplerCUBE _Cube;
float4 _Color;
float _Reflect;
struct Input
{
float2 uv_MainTex;
float2 uv_BumpMap;
float3 worldRefl;
float3 viewDir;
INTERNAL_DATA
};
void surf(Input IN, inout SurfaceOutput o)
{
o.Normal = UnpackNormal(tex2D(_BumpMap, IN.uv_BumpMap));
fixed4 c = tex2D(_MainTex, IN.uv_MainTex);
float3 refl = WorldReflectionVector(IN, o.Normal);
//float3 refl = 2.0 * o.Normal * dot(o.Normal, IN.viewDir) - IN.viewDir;
float4 re = texCUBE(_Cube, refl);
o.Albedo = c.rgb * _Color * (1 - _Reflect);
o.Emission = re.rgb * _Reflect;
o.Alpha = c.a;
}
float4 LightingTest(SurfaceOutput s, float3 lightDir, float atten)
{
float ndotl = dot(s.Normal, lightDir) * 0.5 + 0.5;
float4 final;
final.rgb = s.Albedo * ndotl * _LightColor0.rgb;
final.a = s.Alpha;
return final;
}
ENDCG
}
FallBack "Diffuse"
}
'Unity - Surface Shader' 카테고리의 다른 글
5. 알파블렌딩과 컷아웃 (3) | 2023.11.20 |
---|---|
3. Vertex Shader를 사용하여 Toon Shading 해보기 (0) | 2023.11.14 |
2. Blin - phong Specular + Rim Lighiting (0) | 2023.11.11 |
1. Hologram (0) | 2023.11.10 |
시작하면서... (0) | 2023.11.10 |