글에 사용된 모든 그림과 내용은 직접 작성한 것입니다.
[유튜브 영상]
[풀리퀘 보러가기]
https://github.com/Chang-Jin-Lee/D3D11-AliceTutorial/pull/3
PMX 모델의 머티리얼 텍스쳐를 로딩/매핑/샘플링하여 렌더링하는 파이프라인을 완성하고, 카메라 조작과 ImGui 기반 모델 통계 표시(버텍스 수, 인덱스 수, 텍스쳐 폴더등)를 정리한다.
- 머티리얼 DIFFUSE 텍스쳐는 외부 경로/임베디드(
*0
형식) 두 가지를 모두 처리해야 하며, 외부 경로 실패 시Alice.fbm/
폴더로 폴백해야 한다.- Material-Subset 드로우: 서브셋마다 SRV를 바인딩하고 해당 인덱스 범위만
DrawIndexed
로 그려야 텍스쳐가 의도대로 나온다.- 픽셀 셰이더는 단순 텍스쳐 샘플링으로 구성해(Linear+Wrap 샘플러), 텍스쳐 미존재 시 1x1 흰색 폴백 SRV를 사용하면 블랙/핑크 문제를 피할 수 있다.
- 마우스 휠은 ImGui가 캡처할 수 있으므로 DirectXTK
scrollWheelValue
로 카메라 이동을 적용해야 한다.- AABB 정규화(중심이동+스케일링)와 Rasterizer Cull None, Depth 테스트(LESS)는 PMX 가시화의 기본 안정 장치다.
hlsl 쉐이더를 잘 만들어야합니다
머티리얼 처리
*0
, *1
...): aiTexture
사용CreateTexture2D
→ SRVAlice.fbm/
폴더 재시도서브셋/드로우
{startIndex, indexCount, materialIndex}
기록PSSetShaderResources(t0, SRV_of_material)
→ 해당 범위 DrawIndexed
셰이더/샘플러
TexVertexShader.hlsl
: POSITION/TEXCOORD 패스스루TexPixelShader.hlsl
: 텍스쳐 샘플링// TexPixelShader.hlsl
Texture2D tex0 : register(t0);
SamplerState samp0 : register(s0);
struct PSInput { float4 position:SV_POSITION; float2 texcoord:TEXCOORD0; };
float4 main(PSInput input) : SV_TARGET
{
return tex0.Sample(samp0, input.texcoord);
}
baseDir + path
→ 실패 시 baseDir + "Alice.fbm/" + filename
scene->mTextures[idx]
분기 처리// 외부 경로 + Alice.fbm 폴백
std::wstring full = baseDir + wtex; // 상대경로
HRESULT hr = CreateWICTextureFromFile(device, full.c_str(), ..., &srv);
if (FAILED(hr)) {
std::wstring full2 = baseDir + L"Alice.fbm/" + wtex; // 폴더 폴백
hr = CreateWICTextureFromFile(device, full2.c_str(), ..., &srv);
}
// 임베디드 텍스쳐(*0 형식)
const aiTexture* at = scene->mTextures[idx];
if (at->mHeight == 0) {
// 압축 포맷(예: PNG/JPG)
CreateWICTextureFromMemory(device, (uint8_t*)at->pcData, at->mWidth, ..., &srv);
} else {
// 비압축 BGRA
D3D11_TEXTURE2D_DESC td = { /* BGRA, IMMUTABLE, SRV */ };
D3D11_SUBRESOURCE_DATA sd = { at->pcData, at->mWidth * sizeof(aiTexel) };
device->CreateTexture2D(&td, &sd, &tex);
device->CreateShaderResourceView(tex, &srvd, &srv);
}
LookAt(pos, pos+forward, up)
scrollWheelValue
의 delta를 사용// 휠 delta → 전방(dolly)
int wheel = MouseState.scrollWheelValue;
static int last = wheel;
int delta = wheel - last; last = wheel;
if (delta != 0) {
float steps = float(delta) / WHEEL_DELTA; // 보통 120
posV = posV + forward * (steps * 0.5f); // 감도 0.5
}
Alice.fbm
)auto fmt = [](uint64_t v){
std::wstring s = std::to_wstring(v);
for (int i = int(s.size()) - 3; i > 0; i -= 3) s.insert(i, L",");
return s;
};
// 예시
ImGui::Text("Vertices : %ls", fmt(m_ModelVertices.size()).c_str());
ImGui::Text("Texture Folder: %ls", texdir.filename().c_str());
InitScene()의 노드 트래버스에서 서브메시를 병합할 때 집계됨. 각 aiMesh를 방문하면서 정점/인덱스를 하나의 배열로 모은 뒤, 그 결과로 수치를 계산.
계산 방식
m_ModelVertices.size()
m_ModelVertices.push_back(...)
.m_ModelIndices.size()
m_ModelIndices
에 추가.m_ModelIndices.size() / 3
aiProcess_Triangulate
를 사용하므로 인덱스는 3의 배수.m_Subsets.size()
{startIndex, indexCount, materialIndex}
를 기록.m_MaterialSRVs.size()
주의
Cull None
(수출/좌수계 변환 시 와인딩 이슈 회피)휠 줌이 안 먹는 경우
io.MouseWheel
0scrollWheelValue
delta를 사용하고, io.WantCaptureMouse
일 때 카메라 입력 무시텍스쳐가 흰색만 보이는 경우
Alice.fbm/
폴백 경로 추가, 임베디드(압축/비압축) 모두 처리, PS에서 tex0.Sample
사용Alice.fbm/
), 임베디드 텍스쳐, 폴백 SRV, 샘플러, PS 샘플링, 서브셋 드로우, 카메라/휠 입력, ImGui 통계까지 “PMX 텍스쳐 매핑”을 완성했다.