[DX11][PMX] 07_pmxTexture: PMX 텍스쳐 매핑

ChangJin·2025년 9월 9일
0

DirectX11

목록 보기
1/6
post-thumbnail

글에 사용된 모든 그림과 내용은 직접 작성한 것입니다.

[유튜브 영상]


[풀리퀘 보러가기]
https://github.com/Chang-Jin-Lee/D3D11-AliceTutorial/pull/3


글의 목적

PMX 모델의 머티리얼 텍스쳐를 로딩/매핑/샘플링하여 렌더링하는 파이프라인을 완성하고, 카메라 조작과 ImGui 기반 모델 통계 표시(버텍스 수, 인덱스 수, 텍스쳐 폴더등)를 정리한다.


알게 된 점

  1. 머티리얼 DIFFUSE 텍스쳐는 외부 경로/임베디드(*0 형식) 두 가지를 모두 처리해야 하며, 외부 경로 실패 시 Alice.fbm/ 폴더로 폴백해야 한다.
  2. Material-Subset 드로우: 서브셋마다 SRV를 바인딩하고 해당 인덱스 범위만 DrawIndexed로 그려야 텍스쳐가 의도대로 나온다.
  3. 픽셀 셰이더는 단순 텍스쳐 샘플링으로 구성해(Linear+Wrap 샘플러), 텍스쳐 미존재 시 1x1 흰색 폴백 SRV를 사용하면 블랙/핑크 문제를 피할 수 있다.
  4. 마우스 휠은 ImGui가 캡처할 수 있으므로 DirectXTK scrollWheelValue로 카메라 이동을 적용해야 한다.
  5. AABB 정규화(중심이동+스케일링)와 Rasterizer Cull None, Depth 테스트(LESS)는 PMX 가시화의 기본 안정 장치다.


PMX 텍스쳐 로딩 파이프라인 요약

  • hlsl 쉐이더를 잘 만들어야합니다

  • 머티리얼 처리

    • DIFFUSE 경로 조회 → 분기
      • 임베디드(*0, *1 ...): aiTexture 사용
        • 압축(메모리 PNG/JPG): WIC 메모리 로더
        • 비압축(BGRA): CreateTexture2D → SRV
      • 외부 파일: 모델 폴더 기준 상대경로 시도 → 실패 시 Alice.fbm/ 폴더 재시도
    • 실패한 머티리얼은 1x1 흰색 폴백 SRV 연결
  • 서브셋/드로우

    • 각 서브메시에 대해 {startIndex, indexCount, materialIndex} 기록
    • 렌더 시 서브셋 순회 → PSSetShaderResources(t0, SRV_of_material) → 해당 범위 DrawIndexed
  • 셰이더/샘플러

    • TexVertexShader.hlsl: POSITION/TEXCOORD 패스스루
    • TexPixelShader.hlsl: 텍스쳐 샘플링
    • 샘플러: Linear Filter + Address Wrap
// 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);
}


카메라 조작

  • 우클릭 드래그: Yaw/Pitch 회전(피치 ±89° 클램프), LookAt(pos, pos+forward, up)
  • WASD/QE: 전/후, 좌/우, 상/하 이동
  • 마우스 휠: 뷰 방향 돌리(Dolly)
    • ImGui 캡처 회피: DirectXTK 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
}


ImGui: 모델 정보(우하단)

  • 표시 항목
    • Vertices / Indices / Triangles (3자리 콤마)
    • Subsets / Materials / Textures(unique/fallback)
    • Model Path
    • Texture Folder(폴더명만, 예: Alice.fbm)
  • 숫자 포맷: 간단 3자리 콤마 삽입
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()
      • 각 면(face)이 삼각형일 때 인덱스 3개를 m_ModelIndices에 추가.
    • 삼각형 수: m_ModelIndices.size() / 3
      • Assimp에 aiProcess_Triangulate를 사용하므로 인덱스는 3의 배수.
    • (참고) 서브셋 수: m_Subsets.size()
      • 각 메시 처리 시 {startIndex, indexCount, materialIndex}를 기록.
    • (참고) 머티리얼 수: m_MaterialSRVs.size()
      • 머티리얼별 텍스처 SRV를 로드한 개수와 동일.
  • 주의

    • AABB 정규화(중심 이동/스케일)는 “좌표”만 바꾸며, 버텍스/인덱스/삼각형 수에는 영향 없음.
    • 텍스처 로딩 성공/실패와도 수치 집계는 무관.


안정화 장치

  • AABB 정규화: 중심 이동 + 목표 반경 스케일링(시야 안정)
  • Rasterizer: Cull None(수출/좌수계 변환 시 와인딩 이슈 회피)
  • Depth 테스트: LESS
  • 카메라 LookAt: 현재 위치 기준 전방을 바라보도록 수정


주의사항

  • 휠 줌이 안 먹는 경우

    • 원인: ImGui가 휠 입력 캡처 → io.MouseWheel 0
    • 해결: DirectXTK scrollWheelValue delta를 사용하고, io.WantCaptureMouse일 때 카메라 입력 무시
  • 텍스쳐가 흰색만 보이는 경우

    • 원인: 외부 경로 실패, 임베디드 미처리, 픽셀 셰이더 고정 색 출력
    • 해결: Alice.fbm/ 폴백 경로 추가, 임베디드(압축/비압축) 모두 처리, PS에서 tex0.Sample 사용


마무리

  • 07_pmxTexture의 핵심은 “머티리얼 → 텍스쳐 → 서브셋 드로우”를 끝까지 연결한 것.
  • 경로 폴백(Alice.fbm/), 임베디드 텍스쳐, 폴백 SRV, 샘플러, PS 샘플링, 서브셋 드로우, 카메라/휠 입력, ImGui 통계까지 “PMX 텍스쳐 매핑”을 완성했다.
profile
게임 프로그래머

0개의 댓글