1. 입방체 환경 매핑
환경 매핑이란 물체에 비친 주변 환경을 물체의 표면에 나타내는 것이다.
유니티에서는 입방체 6면체 (6개의 상하좌우 전후)가
필요한 큐브 맵(Cubemap)이나
한 장의 텍스쳐(Panoramic) HDR 파일을 사용하는
쉐이더가 있음을 확인할 수 있다.
2. 큐브 텍스쳐
다이렉트 X11에 큐브 맵을 적용하는 방법은 3가지가 있다.
1. 텍스쳐를 배열로 넘겨서 한번 렌더링 하는 법
2. 6개의 면을 하나씩 렌더링 하는 법
3. CubeMap으로 렌더링 하는 법
이 포스팅에서는 3번만 다룬다.
Cubemap은 6개의 각 면이 텍스처 배열과 같이 저장되어 있는 타입이다.
텍스쳐를 만들 때, 다이렉트 툴킷 (DirectXTK)의 CreateDDSTextureFromFile을
사용했었다. 참고로, 다이렉트 툴킷은 큐브 맵을 완벽히 지원하지 않는다.
(예를 들어 밉맵 버전을 지원하지 않는다, 압축 포맷 지원도 다른 걸로 알고 있음)
완벽히 지원하는 건 다이렉트 텍스쳐 라이브러리를 사용하면 된다.
본인은 다이렉트 툴킷을 사용한다.
HRESULT hr = DirectX::CreateWICTextureFromFile(
g_pd3dDevice,
filename.c_str(),
m_pResourceTexture.GetAddressOf(),
m_pSRVTexture.GetAddressOf());
if (FAILED(hr))
{
hr = DirectX::CreateDDSTextureFromFile(
g_pd3dDevice,
filename.c_str(),
m_pResourceTexture.GetAddressOf(),
m_pSRVTexture.GetAddressOf());
}
if (FAILED(hr))
{
return false;
}
이렇게 함수를 통해 파일로 텍스쳐를 만들었었다.
CubeMap 파일을 불러와서 쉐이더에 넘겨주면 된다.
2. 입방체 큐브 맵 파일
큐브 맵 파일이 있어야 한다.
DDS파일의 큐브 맵을 인터넷을 다운로드하거나,
아니면 직접 제작할 수 있다.
우선 상하좌우 전후 각 6개의 텍스쳐가 있어야 한다.
https://opengameart.org/content/miramar-skybox
위에 링크에는. tga파일의 6개 파일의 무료 소스 텍스쳐가 있다.
위에서 받은 텍스쳐 6개로 디렉트 툴킷 유틸리티인 DxTex로 제작 가능하다.
DirectXTK 다운로드하면 기본적으로 Utilities 폴더에 있다.
프로그램을 켰으면 새 텍스쳐를 만든다.
Type은 Cubemap Texture로 포맷은 1-bit
알파 값을 가진 Four CC 4-bit : DXT1으로 진행한다.
View 탭에서 여러 방향의 큐브 맵 면을 선택할 수 있다.
선택해서 해당 위치에 맞는 텍스쳐를 넣으면 된다.
아래는 편하게 텍스쳐 넣으려고 만든 그림이다..
아티스트마다, 좌표의 차이가 있는 것 같다.
TextureCube g_txSky : register(t0);
SamplerState g_Sample : register(s0);
struct VS_INPUT
{
float3 p : POSITION;
float3 n : NORMAL;
float4 c : COLOR;
float3 t : TEXCOORD;
};
struct VS_OUTPUT
{
float4 p : SV_POSITION;
float3 n : NORMAL;
float4 c : COLOR0;
float3 t : TEXCOORD; // float3임
};
VS_OUTPUT VS(VS_INPUT vIn)
{
VS_OUTPUT vOut = (VS_OUTPUT)0;
vOut.p = mul( float4(vIn.p,1.0f), g_matWorld );
vOut.p = mul(vOut.p, g_matView );
vOut.p = mul(vOut.p, g_matProj );
vOut.p = vOut.p.xyww;
vOut.c = vIn.c;
vOut.t = normalize(vIn.p.xyz);
vOut.n = normalize(mul(vIn.n, (float3x3)g_matWorld));
return vOut;
}
float4 PS(VS_OUTPUT input) : SV_TARGET
{
//텍스처에서 t좌표에 해당하는 컬러값(픽셀) 반환
return g_txSky.Sample(g_Sample, input.t);
}
큐브 맵은 Texture2D가 아닌 TextureCube 타입의 텍스쳐 리소스이다.
일반적으로 UV 값이 float2 값이었지만, 큐브 맵은 3차원 공간의 텍스쳐이기 때문에,
float3의 Texcoord를 갖는다.
박스 하나를 상속받아 텍스쳐를 붙이고,
뷰 행렬의 위치를 원점에 두어, 움직임이 없게 하고
크기를 시야보다 크게 해서 매핑을 하면 된다. 배경에 큐브 맵이 붙는다.
결과
3. 반사 벡터, 큐브 맵
동적 큐브 맵(실시간 계산)과 정적 큐브 맵(저장된 이미지)이 있는데,
동적 큐브 맵은 계속해서 텍스쳐를 찍어내는 방식이다. 연산량이 많다.
정적 큐브 맵은 위에서 적용한 큐브 맵 텍스쳐로
물체의 반사되는 이미지로 적용하면 된다.
이전에 포스팅에 렌더 몽키로 구현했었다.
https://dlemrcnd.tistory.com/39?category=525775
렌더 몽키가 9 버전이라 버전이 달라서 함수의 차이가 있다.
정점 노말과 시선 벡터를 사용하여 반사 벡터를 계산한다.
반사 벡터 자체가 큐브 맵의 3차원 텍스처 좌표가 된다.
6번 슬롯에 큐브 맵 텍스쳐 SRV를 넘긴다.
4. 환경 매핑 쉐이더
Texture2D g_txDiffuse : register(t0);
Texture2D g_txSpecular : register(t1);
Texture2D g_txNormal : register(t2);
Texture2D g_txShadow : register(t3);
TextureCube g_txCubeMap : register(t6); //환경매핑
SamplerState g_Sample : register(s0);
SamplerState g_SamplerClamp : register(s1);
float4 PS(VS_OUTPUT Input) : SV_TARGET
{
//텍스쳐에서 노말, 법선 좌표 구해옴
..
//----------------------------------------------------------------------
float4 albedo = g_txDiffuse.Sample(g_Sample, Input.t); //알베도 기본 색상 텍스쳐
//쉐도우
..
//방향 벡터 정규화
..
//디퓨즈 반사, 난반사
..
//스페큘러 반사, 정반사
..
//환경 매핑
float3 env_coord = reflect(viewDir, worldNormal);//환경 매핑을 위한 텍스처 주소 반사벡터
float4 env = g_txCubeMap.Sample(g_Sample, env_coord);
float4 final = float4(ambient + diffuse + specular + (env*0.35f), 1);
return final;
}
정점에서 반사 벡터를 구해서, 넘겨줘도 되지만,
픽셀 단위로 반사 벡터를 만들면 좀 더 깔끔한 결과를 얻을 수 있다.
결과
극단적으로 오브젝트에 환경 매핑이 붙은 모습이다.
현재까지 ambient, diffuse, specular, environment
4개의 텍스쳐를 불러와 오브젝트에 입힐 수 있도록 적용했다.