출처/범위: 제공된 블로그 글의 흐름과 코드에 충실하게 재구성. 과도한 확장 없이, 학습·강의·리팩토링 실전에 바로 쓰도록 Step-by-Step로 정리.
VertexData)와 InputLayout 자동 매핑을 연결한다.Graphics / VertexBuffer / IndexBuffer / InputLayout / Geometry의 수명 관리 체계를 정착한다.Game
├─ Graphics // Device / DeviceContext 보유
├─ VertexBuffer / IndexBuffer / InputLayout (shared_ptr)
├─ Geometry<T> // vertices[] + indices[] 캡슐화 (템플릿)
└─ GeometryHelper // 기본 도형 정적 생성 유틸(사각형 등)
[절차]
1) GeometryHelper가 Geometry<T> 내부 데이터를 채움(정점/인덱스)
2) Game::CreateGeometry()에서 Geometry 데이터로 VB/IB 생성
3) VertexData::<descs> 로 InputLayout 생성
4) Render: IA 바인딩 → DrawIndexed
#include <memory>
#include <iostream>
// Before
Graphics* _graphics;
VertexBuffer* _vertexBuffer;
IndexBuffer* _indexBuffer;
InputLayout* _inputLayout;
// After
std::shared_ptr<Graphics> _graphics;
std::shared_ptr<VertexBuffer> _vertexBuffer;
std::shared_ptr<IndexBuffer> _indexBuffer;
std::shared_ptr<InputLayout> _inputLayout;
// Before
_graphics = new Graphics(hwnd);
_vertexBuffer= new VertexBuffer(_graphics->GetDevice());
_indexBuffer = new IndexBuffer(_graphics->GetDevice());
_inputLayout = new InputLayout(_graphics->GetDevice());
// After
_graphics = std::make_shared<Graphics>(hwnd);
_vertexBuffer = std::make_shared<VertexBuffer>(_graphics->GetDevice());
_indexBuffer = std::make_shared<IndexBuffer>(_graphics->GetDevice());
_inputLayout = std::make_shared<InputLayout>(_graphics->GetDevice());
⚠️ 주의: shared_ptr는 순환 참조에 유의. (필요 시
weak_ptr사용 검토)
#pragma once
template<typename T>
class Geometry {
public:
Geometry() {}
~Geometry() {}
// Query
uint32 GetVertexCount() { return static_cast<uint32>(_vertices.size()); }
void* GetVertexData() { return _vertices.data(); }
const std::vector<T>& GetVertices() { return _vertices; }
uint32 GetIndexCount() { return static_cast<uint32>(_indices.size()); }
void* GetIndexData() { return _indices.data(); }
const std::vector<uint32>& GetIndices() { return _indices; }
// Edit: Vertices
void AddVertex(const T& v) { _vertices.push_back(v); }
void AddVertices(const std::vector<T>& vs) {
_vertices.insert(_vertices.end(), vs.begin(), vs.end());
}
void SetVertices(const std::vector<T>& vs) { _vertices = vs; }
// Edit: Indices
void AddIndex(uint32 i) { _indices.push_back(i); }
void AddIndices(const std::vector<uint32>& is) {
_indices.insert(_indices.end(), is.begin(), is.end());
}
void SetIndices(const std::vector<uint32>& is) { _indices = is; }
private:
std::vector<T> _vertices;
std::vector<uint32> _indices;
};
💡 템플릿 클래스이므로 .h에서 구현 완료.
#pragma once
struct VertexTextureData {
Vec3 position = {0,0,0};
Vec2 uv = {0,0};
static std::vector<D3D11_INPUT_ELEMENT_DESC> descs; // IL 매핑 정보
};
struct VertexColorData {
Vec3 position = {0,0,0};
Color color = {0,0,0,0};
static std::vector<D3D11_INPUT_ELEMENT_DESC> descs; // IL 매핑 정보
};
std::vector<D3D11_INPUT_ELEMENT_DESC> VertexTextureData::descs = {
{"POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0},
{"TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0},
};
std::vector<D3D11_INPUT_ELEMENT_DESC> VertexColorData::descs = {
{"POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0},
{"COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0},
};
✅
D3D11_APPEND_ALIGNED_ELEMENT로 오프셋 자동 정렬. 수동 12/16 계산을 피할 수 있다.
void Game::CreateInputLayout() {
// 기존: 수동 layout 벡터 작성 → _inputLayout->Create(layout, _vsBlob)
// 변경: 정점 타입의 정적 descs 사용
_inputLayout->Create(VertexTextureData::descs, _vsBlob);
}
정점 타입을
VertexColorData로 바꾸면VertexColorData::descs로 교체하면 된다.
#pragma once
class GeometryHelper {
public:
static void CreateRectangle(std::shared_ptr<Geometry<VertexTextureData>> geometry);
static void CreateRectangle(std::shared_ptr<Geometry<VertexColorData>> geometry, Color color);
};
void GeometryHelper::CreateRectangle(std::shared_ptr<Geometry<VertexTextureData>> geometry) {
std::vector<VertexTextureData> v(4);
v[0].position = Vec3(-0.5f,-0.5f,0.f); v[0].uv = Vec2(0.f,1.f);
v[1].position = Vec3(-0.5f, 0.5f,0.f); v[1].uv = Vec2(0.f,0.f);
v[2].position = Vec3( 0.5f,-0.5f,0.f); v[2].uv = Vec2(1.f,1.f);
v[3].position = Vec3( 0.5f, 0.5f,0.f); v[3].uv = Vec2(1.f,0.f);
geometry->SetVertices(v);
std::vector<uint32> i = {0,1,2, 2,1,3};
geometry->SetIndices(i);
}
void GeometryHelper::CreateRectangle(std::shared_ptr<Geometry<VertexColorData>> geometry, Color color) {
std::vector<VertexColorData> v(4);
v[0].position = Vec3(-0.5f,-0.5f,0.f); v[0].color = color;
v[1].position = Vec3(-0.5f, 0.5f,0.f); v[1].color = color;
v[2].position = Vec3( 0.5f,-0.5f,0.f); v[2].color = color;
v[3].position = Vec3( 0.5f, 0.5f,0.f); v[3].color = color;
geometry->SetVertices(v);
std::vector<uint32> i = {0,1,2, 2,1,3};
geometry->SetIndices(i);
}
std::shared_ptr<Geometry<VertexTextureData>> _geometry; // 텍스처 버전 예시
_geometry = std::make_shared<Geometry<VertexTextureData>>();
// (1) CPU 데이터 채우기
GeometryHelper::CreateRectangle(_geometry);
// (2) VB/IB 생성
_vertexBuffer->Create(_geometry->GetVertices());
_indexBuffer->Create(_geometry->GetIndices());
_inputLayout->Create(VertexTextureData::descs, _vsBlob);
// stride는 실제 정점 타입 크기와 일치해야 함
uint32 stride = sizeof(VertexTextureData);
uint32 offset = 0;
// DrawIndexed의 카운트도 Geometry 기반으로 통일
_deviceContext->DrawIndexed(_geometry->GetIndexCount(), 0, 0);
✅ 기존의
sizeof(Vertex)/_indices.size()사용 부분을 Geometry + 정점 타입 기준으로 변경.
<memory> 포함(링커/컴파일 에러 예방)shared_ptr로 치환(Init/해제 일관성)Geometry<T>는 .h에서 구현(템플릿 링크 에러 방지)VertexData::<descs>와 HLSL 시그니처 시맨틱/포맷 일치D3D11_APPEND_ALIGNED_ELEMENT 사용으로 오프셋 자동 정렬sizeof(정점타입) 스트라이드 정확성 확인_geometry->GetIndexCount() 사용Geometry<T>를 템플릿으로 만든 핵심 이유는?
→ 다양한 정점 구조(텍스처/컬러/노멀 등)를 동일한 틀로 다루기 위해.
VertexTextureData::descs에서 D3D11_APPEND_ALIGNED_ELEMENT의 역할은?
→ 앞 요소 크기를 기준으로 오프셋을 자동으로 계산/정렬.
Render에서 sizeof(Vertex) 대신 sizeof(VertexTextureData)를 쓰는 이유는?
→ 실제 현재 사용 중인 정점 타입의 크기가 스트라이드가 되어야 하기 때문.
DrawIndexed의 카운트를 _indices.size()에서 _geometry->GetIndexCount()로 바꾸는 장점은?
→ 데이터 소유·질의를 일원화하여 일관성/유지보수성 향상.
Geometry<VertexColorData>로 선언 바꾸고, Helper의 컬러 버전 API 사용. IL/HLSL도 COLOR 시그니처로 변경.VertexData::<descs>만 사용하도록 정리.VertexData::<descs> 확장