[목차]
- TEXCOORD(UV)
- 간단한 UV 테스트
- Sampler
- Sampler와 AddressUV 테스트
[TEXCOORD(UV)]
TEXCOORD는 DirectX 및 HLSL에서 텍스쳐 좌표(Texture Coordinate)를 전달하기 위한 시맨틱 중 하나의 명칭입니다. 보통 UV값이라고 하며, Vertex Shader에서 Pixel Shader로 텍스쳐 좌표를 넘길 때 주로 활용합니다. 범위는 0.0 ~ 1.0를 사용하며 (0, 0)이 텍스쳐의 왼쪽 위, (1, 1)이 텍스쳐의 오른쪽 아래를 의미합니다.

이를 활용하여 다음의 기능을 활용할 수 있습니다.
1. 텍스쳐 매핑
: 픽셀 위치에 따라 텍스쳐의 어떤 위치를 샘플링할지 결정
2. 애니메이션
: 좌표를 시간에 따라 변경하여 스크롤, 웨이브 등 효과 생성
3. 범위조정
: UV를 0~1로 넘어서면, 샘플러 주소 모드(Wrap, Clamp, Mirror)에 따라 처리방식 결정
[간단한 UV 테스트]
현재의 상태에서 테스트해보겠습니다. 목표는 한 면에 대해 Textcoord값을 활용하는 것입니다. 현재는 모든 면에 Textcoord값이 전달되어 있는데, 아래처럼 한 면만 적용해보겠습니다.
// 정육면체
{
std::vector<Ext_DirectXVertexData> Vertex =
{
// Front (+Z)
{ {-0.5f, 0.5f, 0.5f, 1.0f}, {1, 0, 0, 1}, {0, 0}, {0, 0, 1} },
{ { 0.5f, 0.5f, 0.5f, 1.0f}, {0, 1, 0, 1}, {1, 0}, {0, 0, 1} },
{ { 0.5f, -0.5f, 0.5f, 1.0f}, {0, 0, 1, 1}, {1, 1}, {0, 0, 1} },
{ {-0.5f, -0.5f, 0.5f, 1.0f}, {1, 1, 0, 1}, {0, 1}, {0, 0, 1} },
// Back (-Z)
{ { 0.5f, 0.5f, -0.5f, 1.0f}, {1, 0, 0, 1}, {0, 0}, {0, 0, -1} },
{ {-0.5f, 0.5f, -0.5f, 1.0f}, {0, 1, 0, 1}, {1, 0}, {0, 0, -1} },
{ {-0.5f, -0.5f, -0.5f, 1.0f}, {0, 0, 1, 1}, {1, 1}, {0, 0, -1} },
{ { 0.5f, -0.5f, -0.5f, 1.0f}, {1, 1, 0, 1}, {0, 1}, {0, 0, -1} },
// Left (-X)
{ {-0.5f, 0.5f, -0.5f, 1.0f}, {1, 0, 0, 1}, {0, 0}, {-1, 0, 0} },
{ {-0.5f, 0.5f, 0.5f, 1.0f}, {0, 1, 0, 1}, {1, 0}, {-1, 0, 0} },
{ {-0.5f, -0.5f, 0.5f, 1.0f}, {0, 0, 1, 1}, {1, 1}, {-1, 0, 0} },
{ {-0.5f, -0.5f, -0.5f, 1.0f}, {1, 1, 0, 1}, {0, 1}, {-1, 0, 0} },
// Right (+X)
{ { 0.5f, 0.5f, 0.5f, 1.0f}, {1, 0, 0, 1}, {0, 0}, {1, 0, 0} },
{ { 0.5f, 0.5f, -0.5f, 1.0f}, {0, 1, 0, 1}, {1, 0}, {1, 0, 0} },
{ { 0.5f, -0.5f, -0.5f, 1.0f}, {0, 0, 1, 1}, {1, 1}, {1, 0, 0} },
{ { 0.5f, -0.5f, 0.5f, 1.0f}, {1, 1, 0, 1}, {0, 1}, {1, 0, 0} },
// Top (+Y)
{ {-0.5f, 0.5f, -0.5f, 1.0f}, {1, 0, 0, 1}, {0, 0}, {0, 1, 0} },
{ { 0.5f, 0.5f, -0.5f, 1.0f}, {0, 1, 0, 1}, {1, 0}, {0, 1, 0} },
{ { 0.5f, 0.5f, 0.5f, 1.0f}, {0, 0, 1, 1}, {1, 1}, {0, 1, 0} },
{ {-0.5f, 0.5f, 0.5f, 1.0f}, {1, 1, 0, 1}, {0, 1}, {0, 1, 0} },
// Bottom (-Y)
{ {-0.5f, -0.5f, 0.5f, 1.0f}, {1, 0, 0, 1}, {0, 0}, {0, -1, 0} },
{ { 0.5f, -0.5f, 0.5f, 1.0f}, {0, 1, 0, 1}, {1, 0}, {0, -1, 0} },
{ { 0.5f, -0.5f, -0.5f, 1.0f}, {0, 0, 1, 1}, {1, 1}, {0, -1, 0} },
{ {-0.5f, -0.5f, -0.5f, 1.0f}, {1, 1, 0, 1}, {0, 1}, {0, -1, 0} },
};
// 한면만 테스트///////////////////////////////////////////////////////////////////////////////////////
// Front (+Z)
Vertex[0].TEXCOORD = { 0.0f, 0.0f }; // LT
Vertex[1].TEXCOORD = { 0.0f, 0.0f }; // RT
Vertex[2].TEXCOORD = { 0.0f, 0.0f }; // RB
Vertex[3].TEXCOORD = { 0.0f, 0.0f }; // LB
// Back (-Z)
Vertex[4].TEXCOORD = { 0.0f, 0.0f }; // LT
Vertex[5].TEXCOORD = { 1.0f, 0.0f }; // RT
Vertex[6].TEXCOORD = { 1.0f, 1.0f }; // RB
Vertex[7].TEXCOORD = { 0.0f, 1.0f }; // LB
// 나머지 면은 0으로 고정하거나 무시됨
for (int i = 8; i < Vertex.size(); ++i)
{
Vertex[i].TEXCOORD = { 0.0f, 0.0f };
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////
std::vector<UINT> Index =
{
// Front (+Z)
0, 1, 2, 0, 2, 3,
// Back (-Z)
4, 5, 6, 4, 6, 7,
// Left (-X)
8, 9, 10, 8, 10, 11,
// Right (+X)
12, 13, 14, 12, 14, 15,
// Top (+Y)
16, 17, 18, 16, 18, 19,
// Bottom (-Y)
20, 21, 22, 20, 22, 23
};
Ext_DirectXVertexBuffer::CreateVertexBuffer("Box", Vertex);
Ext_DirectXIndexBuffer::CreateIndexBuffer("Box", Index);
std::shared_ptr<Ext_DirectXMesh> Mesh = Ext_DirectXMesh::CreateMesh("Box");
}
이러면 카메라가 바라보는 방향의 면인 Box의 -Z방향(뒷쪽)에만 UV 좌표가 적용되고, 나머지는 (0, 0)이 되기 때문에 적용되지 않는 것이나 마찬가지일 것입니다.
이제 Pixel Shader의 엔트리포인트에 다음과 같이 작성해줍니다.
struct PSInput
{
float4 Position : SV_POSITION;
float4 Color : COLOR;
float2 TexCoord : TEXCOORD;
};
float4 Basic_PS(PSInput _Input) : SV_TARGET
{
if (_Input.TexCoord.x > 0.5f)
{
return float4(1.0f, 1.0f, 0.0f, 1.0f);
}
else
{
return _Input.Color;
}
}
Texcoord값이 있다면 0.5 이상일 경우 노란색으로 변경합니다. 없을 경우(값이 있지만, 0.0 ~ 0.0으로 매핑되서 안보임)는 그대로 출력합니다. 결과를 확인해보겠습니다.
[Sampler]
Sampler는 Direct3D에서 텍스쳐 좌표를 어떻게 해석하고 샘플링할지 정의하는 객체입니다. 단독으로는 사용되지 않고 텍스쳐와 함께 사용되며, 텍스쳐를 어떻게 읽을지 정하는 설명서라고 볼 수 있습니다.
- 텍셀 사이를 어떻게 계산할 것인가(Point, Linear, Anisotropic)
- 텍스쳐 바깥은 어떻게 처리할 것인가(0 ~ 1 범위 바깥), (Wrap, Clamp, Mirror, Border)
- MipMap 수준 정의
- 텍스쳐 왜곡 보정 on/off(Anisotropy Level)
샘플러 규칙은 다른 State와 마찬가지로 SamplerState를 통해 정해줄 수 있습니다. 디폴트 SamplerState는 따로 없고, 필요에 따라 만들어서 사용해야합니다. 그나마 대표적으로 많이 활용되는 Sampler가 LinearClamp Sampler입니다.
// 샘플러
{
D3D11_SAMPLER_DESC SamperInfo = {};
SamperInfo.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR;
SamperInfo.AddressU = D3D11_TEXTURE_ADDRESS_CLAMP;
SamperInfo.AddressV = D3D11_TEXTURE_ADDRESS_CLAMP;
SamperInfo.AddressW = D3D11_TEXTURE_ADDRESS_CLAMP;
SamperInfo.MipLODBias = 0.0f;
SamperInfo.MaxAnisotropy = 1;
SamperInfo.ComparisonFunc = D3D11_COMPARISON_ALWAYS;
SamperInfo.MinLOD = -FLT_MAX;
SamperInfo.MaxLOD = FLT_MAX;
// <<설명>>
/*1. Filter : MIN_MAG_MIP_LINEAR(축소, 확대, 밉맵) 모두 선형 보간으로 부드럽게 처리한다는 뜻의 D3D11_FILTER_MIN_MAG_MIP_LINEAR 전달*/
/*2~4. AddressUVW : D3D11_TEXTURE_ADDRESS_CLAMP는 UV가 0~1 범위를 넘을 경우, 가장자리 색상을 계속 사용한다는 뜻*/
/*5. MipLODBias : 밉맵 LOD를 얼마나 조정할지 정하기 위함, 0.0f로 전달하면 기본 수준 사용 */
/*6. MaxAnisotropy : 애니소트로픽 필터링 여부, 1은 비활성화이며 4 ~ 16을 보통 사용한다.*/
/*7. ComparisonFunc : 그림자 맵 등에서 비교 연산 시 항상 통과한다는 설정, 일반 텍스쳐는 보통 ALWAYS를 설정한다.*/
/*8~9. MinLOD, MaxLOD : 밉맵 레벨 범위 제한, -FLT_MAX ~ FLT_MAX면 전체를 허용하는 것*/
Ext_DirectXSampler::CreateSampler("BaseSampler", SamperInfo);
// ....
}
이렇게 CreateSampler()를 호출하면 아래의 함수들이 호출됩니다.
// 위의 CreateSampler로 호출, 이후 픽셀 셰이더 리플렉션 시 사용되기 위해 BaseSampler 이름으로 저장
void Ext_DirectXSampler::CreateSampler(const D3D11_SAMPLER_DESC& _Desc)
{
if (nullptr != SmplerState)
{
SmplerState.Reset(); // 기존 샘플러 있을 경우, 해제한 뒤 다시 사용
}
SamplerInfo = _Desc;
// GPU에 실제 샘플러 상태 객체 생성, SmplerState에 저장
if (S_OK != Ext_DirectXDevice::GetDevice()->CreateSamplerState(&SamplerInfo, SmplerState.GetAddressOf()))
{
MsgAssert("샘플러 생성에 실패했습니다.");
return;
}
}
/////// 렌더링 파이프라인에서 사용하는 두 종류
// 정점 셰이더에 슬롯별로 샘플러 바인딩, 정점 셰이더 샘플러 바인딩은 거의 사용하지 않고 픽셀 셰이더에서만 일어난다.
void Ext_DirectXSampler::VSSetting(UINT _Slot)
{
Ext_DirectXDevice::GetContext()->VSSetSamplers(_Slot, 1, SmplerState.GetAddressOf());
}
// 픽셀 셰이더에 슬롯별로 샘플러 바인딩(register(b0)에 바인딩됨) -> Tex.Sample(Sampler, uv)에 사용
void Ext_DirectXSampler::PSSetting(UINT _Slot)
{
Ext_DirectXDevice::GetContext()->PSSetSamplers(_Slot, 1, SmplerState.GetAddressOf());
}
Sampler는 Pixel Shader에서 선언하고 사용하는 방식이기 때문에, 다른 State와 달리 Maetrial 클래스에 바인딩해주지 않고 Constant Buffer 처럼 리플렉션 시 버퍼를 세팅하도록 했습니다.
case D3D_SIT_SAMPLER:
{
// 바인딩 슬롯 이름과 동일한 샘플러를 찾지 못하면, 기본 Base 샘플러로 변경
std::shared_ptr<Ext_DirectXSampler> SamplerResource = Ext_DirectXSampler::Find(UpperName);
if (nullptr == SamplerResource) SamplerResource = Ext_DirectXSampler::Find("LinearClampSampler");
// 샘플러 세터 데이터 입력 후 저장
SamplerSetter Set;
Set.OwnerShader = GetSharedFromThis<Ext_DirectXShader>();
Set.Name = UpperName;
Set.BindPoint = ResDesc.BindPoint; // GPU에 바인딩할 슬롯 정보 register(s0)
Set.Sampler = SamplerResource; // 샘플러 저장
BufferSetter.InsertSamplerSetter(Set);
break;
}
이렇게 해주면 MeshComponentUnit이 Initialize를 수행할 때, Copy를 통해 자신의 BufferSetter에 값을 복사해서 가져갈 것입니다.
세팅이 완료됐으면 바로 사용할 수 있습니다. Pixel Shader에서 다음과 같이 선언해주면 됩니다.
Texture2D BaseColorTex : register(t0); // 텍스처 자원
SamplerState Sampler : register(s0); // 샘플러
struct PSInput
{
float4 Position : SV_POSITION;
float4 Color : COLOR;
float2 TexCoord : TEXCOORD;
float4 Normal : NORMAL;
};
float4 Basic_PS(PSInput _Input) : SV_TARGET
{
float4 Color = BaseColorTex.Sample(Sampler, _Input.TexCoord);
return Color;
}
[BaseColorTex.Sample(Sampler, _Input.TexCoord)]는 "BaseColorTex 텍스쳐에서 _Input.TexCoord 위치의 텍셀을 Sampler 설정 방식에 따라 읽어서 Color에 저장하라"는 뜻입니다. 말이 조금 어려울 수 있는데, 단순하게 말하자면 Mesh의 UV 영역에 Texture를 복붙하라는 뜻입니다. 이러면 둘의 크기가 달라도 샘플링을 통해 Mesh UV값에 따라 Texture가 입혀집니다.
[Sampler와 AddressUV 테스트]
현재 UV값을 (0. 0), (1. 1)로 사용하면서, CLAMP를 활용하고 있습니다. 좌표값을 임의로 변경하여 테스트해보겠습니다. 먼저 TEXCOORD로 전달될 값을 최소 -0.5, 최대 0.5로 변경해보겠습니다.
// Back (-Z) 변경전
{ { 0.5f, 0.5f, -0.5f, 1.0f}, {1, 0, 0, 1}, {1, 0}, {0, 0, -1} },
{ {-0.5f, 0.5f, -0.5f, 1.0f}, {0, 1, 0, 1}, {0, 0}, {0, 0, -1} },
{ {-0.5f, -0.5f, -0.5f, 1.0f}, {0, 0, 1, 1}, {0, 1}, {0, 0, -1} },
{ { 0.5f, -0.5f, -0.5f, 1.0f}, {1, 1, 0, 1}, {1, 1}, {0, 0, -1} },
// Back (-Z) 변경후
{ { 0.5f, 0.5f, -0.5f, 1.0f}, {1, 0, 0, 1}, {0.5, -0.5}, {0, 0, -1} },
{ {-0.5f, 0.5f, -0.5f, 1.0f}, {0, 1, 0, 1}, {-0.5, -0.5}, {0, 0, -1} },
{ {-0.5f, -0.5f, -0.5f, 1.0f}, {0, 0, 1, 1}, {-0.5, 0.5}, {0, 0, -1} },
{ { 0.5f, -0.5f, -0.5f, 1.0f}, {1, 1, 0, 1}, {0.5, 0.5}, {0, 0, -1} },
1. Clamp : 0~1 범위를 벗어나도 가장자리값 유지(끝 색상이 계속 늘어나면서 유지)
2. Wrap : 1을 넘어가면 반복됨(텍스쳐 타일링)
3. Mirror : 반복되지만 짝수는 그대로, 홀수는 반전(반전 반복, 마치 거울처럼)
'DirectX11 > 프레임워크 제작' 카테고리의 다른 글
| [DirectX11] assimp 라이브러리를 통한 3DMesh 로드와 애니메이션 (0) | 2025.05.29 |
|---|---|
| [DirectX11] BlendState 생성 (0) | 2025.05.28 |
| [DirectX11] DirectXTex를 통한 Texture 활용 (0) | 2025.05.28 |
| [DirectX11] RasterizerState 생성 (0) | 2025.05.28 |
| [DirectX11] DepthStencilState 생성 (0) | 2025.05.28 |