Unity 큐브맵 배열 사용 가이드
큐브맵 배열이란?
큐브맵 배열은 크기와 포맷이 동일한 큐브맵의 집합으로, GPU가 이를 단일 텍스처 리소스로 접근할 수 있도록 합니다. 주로 효율적인 반사 프로브, 조명 및 그림자 시스템 구현에 사용됩니다.
큐브맵 배열 생성
Unity 프로젝트 내에서 큐브맵 배열을 생성하기 위해서는 스크립트를 사용할 필요가 있습니다. 다음은 CubemapArray 클래스의 인스턴스를 생성하고, 컬러 데이터로 채운 후 텍스처 에셋으로 저장하는 에디터 스크립트의 예시입니다.
C# 스크립트 예제
using UnityEngine;
public class CreateCubeArrayTexture : MonoBehaviour
{
[UnityEditor.MenuItem("CreateExamples/CubemapArray")]
static void CreateCubemapArray()
{
// 큐브맵 배열 및 색상 데이터를 구성합니다.
int faceSize = 16;
int arraySize = 4;
int[] kCubeXRemap = new int[] { 2, 2, 0, 0, 0, 0 };
int[] kCubeYRemap = new int[] { 1, 1, 2, 2, 1, 1 };
int[] kCubeZRemap = new int[] { 0, 0, 1, 1, 2, 2 };
float[] kCubeXSign = new float[] { -1.0F, 1.0F, 1.0F, 1.0F, 1.0F, -1.0F };
float[] kCubeYSign = new float[] { -1.0F, -1.0F, 1.0F, -1.0F, -1.0F, -1.0F };
float[] kCubeZSign = new float[] { 1.0F, -1.0F, 1.0F, -1.0F, 1.0F, -1.0F };
var baseCols = new Color[] { Color.white, new Color(1, .5f, .5f, 1), new Color(.5f, 1, .5f, 1), new Color(.5f, .5f, 1, 1), Color.gray };
// CubemapArray 인스턴스를 생성합니다.
var tex = new CubemapArray(faceSize, arraySize, TextureFormat.ARGB32, true);
tex.filterMode = FilterMode.Trilinear;
// 각 큐브맵을 순회합니다.
var col = new Color[tex.width * tex.width];
float invSize = 1.0f / tex.width;
for (var i = 0; i < tex.cubemapCount; ++i)
{
var baseCol = baseCols[i % baseCols.Length];
// 현재 큐브맵의 각 면을 반복합니다.
for (var face = 0; face < 6; ++face)
{
var idx = 0;
Vector3 signScale = new Vector3(kCubeXSign[face], kCubeYSign[face], kCubeZSign[face]);
// 현재 면의 각 픽셀을 반복합니다.
for (int y = 0; y < tex.width; ++y)
{
for (int x = 0; x < tex.width; ++x)
{
// 현재 픽셀에 대한 "노멀 방향" 색상을 계산합니다.
Vector3 uvDir = new Vector3(x * invSize * 2.0f - 1.0f, y * invSize * 2.0f - 1.0f, 1.0f);
uvDir = uvDir.normalized;
uvDir.Scale(signScale);
Vector3 dir = Vector3.zero;
dir[kCubeXRemap[face]] = uvDir[0];
dir[kCubeYRemap[face]] = uvDir[1];
dir[kCubeZRemap[face]] = uvDir[2];
// 색상을 0.4..1.0 범위로 이동시킵니다.
Color c = new Color(dir.x * 0.3f + 0.7f, dir.y * 0.3f + 0.7f, dir.z * 0.3f + 0.7f, 1.0f);
// 일부 픽셀에 패턴을 추가하여 mipmap을 더욱 뚜렷하게 표시합니다.
if (((x ^ y) & 3) == 1)
c *= 0.5f;
// 기본 색상으로 색상을 변경합니다.
col[idx] = baseCol * c;
++idx;
}
}
// 이 면의 색상 값을 텍스처에 복사합니다.
tex.SetPixels(col, (CubemapFace)face, i);
}
}
// 텍스처에 변경 사항을 적용하고 GPU에 업데이트된 텍스처를 업로드합니다.
tex.Apply();
// 텍스처를 Unity 프로젝트에 저장합니다.
AssetDatabase.CreateAsset(tex, "Assets/ExampleCubemapArray.asset");
UnityEditor.AssetDatabase.SaveAssets();
}
}
큐브맵 배열 미리보기
큐브맵 배열을 미리 보려면 프로젝트 창에서 텍스처 에셋을 선택하십시오. 그러면 인스펙터의 하단에 큐브맵 배열의 미리보기가 렌더링됩니다.
인스펙터의 컨트롤
| 컨트롤 | 기능 |
|---|---|
| Filter Mode | 미리보기에 사용할 필터링입니다. |
셰이더에서 큐브맵 배열 사용
다음은 큐브맵 배열을 사용하는 셰이더의 예시입니다.
셰이더 코드 예제
Shader "CubemapArrayShaderExample" {
Properties {
_MainTex ("CubemapArray", CubeArray) = "" {}
_Mip ("Mip", Float) = 0.0
_Intensity ("Intensity", Float) = 1.0
_SliceIndex ("Slice", Int) = 0
_Exposure ("Exposure", Float) = 0.0
}
SubShader {
Tags {"Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent" "ForceSupported" = "True"}
Pass {
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma require sampleLOD
#pragma require cubearray
#include "UnityCG.cginc"
struct appdata {
float4 pos : POSITION;
float3 nor : NORMAL;
};
struct v2f {
float3 uv : TEXCOORD0;
float4 pos : SV_POSITION;
};
uniform int _SliceIndex;
float _Mip;
half _Alpha;
half _Intensity;
float _Exposure;
v2f vert (appdata v) {
v2f o;
o.pos = UnityObjectToClipPos(v.pos);
float3 viewDir = -normalize(ObjSpaceViewDir(v.pos));
o.uv = reflect(viewDir, v.nor);
return o;
}
half4 _MainTex_HDR;
UNITY_DECLARE_TEXCUBEARRAY(_MainTex);
fixed4 frag (v2f i) : COLOR0
{
fixed4 c = UNITY_SAMPLE_TEXCUBEARRAY(_MainTex, float4(i.uv, _SliceIndex));
fixed4 cmip = UNITY_SAMPLE_TEXCUBEARRAY_LOD(_MainTex, float4(i.uv, _SliceIndex), _Mip);
if (_Mip >= 0.0)
c = cmip;
c.rgb = DecodeHDR (c, _MainTex_HDR) * _Intensity;
c.rgb *= exp2(_Exposure);
c = lerp (c, c.aaaa, _Alpha);
return c;
}
ENDCG
}
}
Fallback Off
}
이 셰이더를 위의 예제에서 생성된 큐브맵 배열과 함께 사용하면 고급 그래픽 효과를 얻을 수 있습니다.