주제

  • 본 강의의 핵심은 GameObject의 렌더링 책임을 MeshRenderer 컴포넌트로 완전히 분리하고, Camera/Transform 데이터를 독립적으로 Shader에 전달하는 효율적 구조를 설계하는 데 있다.

  • 유니티와 유사한 조합형(Component-Based) 아키텍처로 전환하며, Mesh(기하 정보)와 Material(렌더링 방식) 을 분리할 수 있는 기반을 구축한다.

  • 또한 DirectX 11 렌더링 파이프라인을 기반으로, 셰이더 자원/버퍼 분리, 파이프라인 정보 통합 구조, GPU 전송 효율화 등을 실습한다.

  • GameObjectMeshRendererTransform을 조합하여 렌더링 흐름을 제어하는 방식

  • Update, Render 단계에서 컴포넌트 기반 객체가 어떻게 GPU에 데이터를 넘기고, 파이프라인을 통해 화면에 출력되는지

  • 컴포넌트 기반 설계를 통해 Material, Shader, Texture의 분리 구조로 확장 가능성 확보

  • 전체 흐름의 의존성, 생명주기, 데이터 전달 위치를 시각화


개념

1. MeshRenderer란?

  • MeshRendererGameObject에 부착되어 실제 물체를 GPU에 렌더링하는 컴포넌트이다.
  • 렌더링 파이프라인(IA-VS-RS-PS-OM)의 핵심 구성 요소인 정점/인덱스 버퍼, 셰이더, 텍스처, 샘플러 등을 포함하고 설정한다.
  • GameObject는 오직 컴포넌트들을 관리하는 컨테이너로 기능하며, 실제 렌더링은 MeshRenderer가 담당한다.

2. TransformData와 CameraData의 분리

  • 초기 구현에서는 matWorld, matView, matProjection하나의 상수 버퍼(TransformData) 로 GPU에 전송했으나, 이는 비효율적이다.
  • 카메라(View/Projection)는 프레임마다 1회만 전송되면 되며, Transform(World 행렬)은 오브젝트별로 다르므로 반복 전송되어야 한다.
  • 이를 개선하기 위해 CameraData(b0 슬롯)TransformData(b1 슬롯)Shader 및 C++에서 완전히 분리하여 구성한다.

3. Mesh와 Material의 개념 분리

  • Mesh: 렌더링할 기하학적 정보(정점, 인덱스 등)
  • Material: 그 기하학을 어떻게 렌더링할 것인지 정의 (셰이더, 텍스처, 샘플러 등 포함)
  • 본 강의에서는 유니티의 MeshFilter, MeshRenderer 개념을 통합하여, MeshRenderer 안에 Mesh와 Material 책임을 임시 통합하여 구현함.

📘 용어 정리

용어설명
MeshRenderer렌더링을 수행하는 컴포넌트. 파이프라인 설정, 셰이더, 버퍼 관리
TransformData오브젝트의 월드 변환 행렬(matWorld)을 담는 구조체
CameraData카메라의 View 및 Projection 행렬을 담는 구조체
ConstantBufferCPU → GPU로 데이터를 전송하는 상수 버퍼 구조
Shader Slot상수 버퍼가 Shader에 할당되는 위치 (예: register(b0))
VertexBuffer/IndexBuffer정점 및 인덱스 데이터를 GPU에 전달하는 버퍼
InputLayout정점 구조(포맷)를 GPU 셰이더에 명시하는 설정
SamplerState텍스처 샘플링 방식을 정의하는 GPU 상태 객체
BlendState픽셀 색상 혼합 방식을 정의하는 GPU 상태 객체

🧩 Shader 및 구조체 정의

📄 Default.hlsl – 카메라/변환 행렬 분리

cbuffer CameraData : register(b0)
{
    row_major matrix matView;
    row_major matrix matProjection;
};

cbuffer TransformData : register(b1)
{
    row_major matrix matWorld;
};
  • b0: 카메라 전용 – 프레임마다 한 번만 전달
  • b1: 오브젝트 전용 – 오브젝트마다 개별 전송

📄 Struct.h – C++ 구조체 정의

struct CameraData
{
    Matrix matView = Matrix::Identity;
    Matrix matProjection = Matrix::Identity;
};

struct TransformData
{
    Matrix matWorld = Matrix::Identity;
};
  • 이 두 구조체는 각각 ConstantBuffer<CameraData>, ConstantBuffer<TransformData> 로 생성됨

⚙️ MeshRenderer 컴포넌트 구현

1. 생성자 – 모든 리소스 생성 및 초기화

MeshRenderer::MeshRenderer(ComPtr<ID3D11Device> device, ComPtr<ID3D11DeviceContext> deviceContext)
    : Super(ComponentType::MeshRenderer), _device(device)
{
    _geometry = make_shared<Geometry<VertexTextureData>>();
    GeometryHelper::CreateRectangle(_geometry);

    _vertexBuffer = make_shared<VertexBuffer>(device);
    _vertexBuffer->Create(_geometry->GetVertices());

    _indexBuffer = make_shared<IndexBuffer>(device);
    _indexBuffer->Create(_geometry->GetIndices());

    _vertexShader = make_shared<VertexShader>(device);
    _vertexShader->Create(L"Default.hlsl", "VS", "vs_5_0");

    _inputLayout = make_shared<InputLayout>(device);
    _inputLayout->Create(VertexTextureData::descs, _vertexShader->GetBlob());

    _pixelShader = make_shared<PixelShader>(device);
    _pixelShader->Create(L"Default.hlsl", "PS", "ps_5_0");

    _rasterizerState = make_shared<RasterizerState>(device);
    _rasterizerState->Create();

    _blendState = make_shared<BlendState>(device);
    _blendState->Create();

    _texture1 = make_shared<Texture>(device);
    _texture1->Create(L"chiikawa.png");

    _samplerState = make_shared<SamplerState>(device);
    _samplerState->Create();

    _cameraBuffer = make_shared<ConstantBuffer<CameraData>>(device, deviceContext);
    _cameraBuffer->Create();

    _transformBuffer = make_shared<ConstantBuffer<TransformData>>(device, deviceContext);
    _transformBuffer->Create();
}

2. Update() – 카메라/월드 행렬 GPU 전송

void MeshRenderer::Update()
{
    _cameraData.matView = Camera::S_MatView;
    _cameraData.matProjection = Camera::S_MatProjection;
    _cameraBuffer->CopyData(_cameraData);

    _transformData.matWorld = GetTransform()->GetWorldMatrix();
    _transformBuffer->CopyData(_transformData);
}
  • View/Projection은 고정값 복사
  • World 행렬은 오브젝트마다 변화하므로 매 프레임 갱신

3. Render() – 파이프라인 설정 및 렌더링

void MeshRenderer::Render(shared_ptr<Pipeline> pipeline)
{
    PipelineInfo info;
    info.inputLayout = _inputLayout;
    info.vertexShader = _vertexShader;
    info.pixelShader = _pixelShader;
    info.rasterizerState = _rasterizerState;
    info.blendState = _blendState;
    pipeline->UpdatePipeline(info);

    pipeline->SetVertexBuffer(_vertexBuffer);
    pipeline->SetIndexBuffer(_indexBuffer);

    pipeline->SetConstantBuffer(0, SS_VertexShader, _cameraBuffer);
    pipeline->SetConstantBuffer(1, SS_VertexShader, _transformBuffer);

    pipeline->SetTexture(0, SS_PixelShader, _texture1);
    pipeline->SetSamplerState(0, SS_PixelShader, _samplerState);

    pipeline->DrawIndexed(_geometry->GetIndexCount(), 0, 0);
}

🧩 GameObject와 MeshRenderer 연동 구조

1. GameObject에서 MeshRenderer 컴포넌트 추가

// Game.cpp

_monster = make_shared<GameObject>(...);
_monster->GetOrAddTransform();  // Transform 컴포넌트 기본 포함
_monster->AddComponent(make_shared<MeshRenderer>(device, deviceContext));
  • GameObject는 본질적으로 컴포넌트의 컨테이너 객체
  • Transform, MeshRenderer, Camera 등의 기능은 모두 컴포넌트로 분리
  • Unity의 AddComponent<>(), GetComponent<>() 패턴을 그대로 반영

2. GameObject 내부 Update 처리 흐름

void GameObject::Update()
{
    if (GetCamera())
        return; // 카메라는 Transform을 갱신하지 않음

    _cameraData.matView = Camera::S_MatView;
    _cameraData.matProjection = Camera::S_MatProjection;
    _cameraBuffer->CopyData(_cameraData);

    _transformData.matWorld = GetOrAddTransform()->GetWorldMatrix();
    _transformBuffer->CopyData(_transformData);
}
  • 카메라 오브젝트는 Transform 갱신 불필요 → View/Projection만 유지
  • 일반 렌더링 오브젝트는 CameraData + TransformData 모두 Shader에 복사

3. GameObject에서 MeshRenderer 호출

// Game::Render()
_monster->GetMeshRenderer()->Render(_pipeline);
  • GetMeshRenderer()는 GameObject의 컴포넌트 관리 테이블에서 검색
  • 안전한 다운캐스팅을 거쳐 shared_ptr<MeshRenderer> 획득
  • 오직 MeshRenderer가 있는 오브젝트만 실제 Draw 호출이 발생

🔗 파이프라인 흐름 정리

전체 구성 흐름 요약:

[GameObject]
 └── [Transform 컴포넌트]
 └── [MeshRenderer 컴포넌트]
       └── Update() → GPU 버퍼 복사
       └── Render() → Pipeline 구성 및 Draw
  • 모든 GPU 자원(Vertex/Index Buffer, Shader 등)은 MeshRenderer가 소유
  • 오직 MeshRenderer만이 GPU 파이프라인을 구성하고 Draw를 호출
  • Transform은 World 행렬 제공, Camera는 View/Projection 행렬 제공

🎯 구조 확장 방향

1. Material 구조 분리

MeshRenderer 내부에는 현재 Shader/Texture/Sampler 등이 전부 직접 포함되어 있음.
→ 이를 다음과 같이 Material 클래스로 추상화할 수 있음.

class Material
{
public:
    shared_ptr<VertexShader> vertexShader;
    shared_ptr<PixelShader> pixelShader;
    shared_ptr<Texture> texture;
    shared_ptr<SamplerState> sampler;
    shared_ptr<BlendState> blendState;
};
class MeshRenderer : public Component
{
    shared_ptr<Material> _material;
};
  • 이렇게 분리함으로써, 여러 MeshRenderer가 하나의 Material을 공유하거나, 재사용성 높은 렌더링 설정을 관리할 수 있음

2. Shader/텍스처의 유연한 바인딩 구조

현재는 Default.hlsl 고정.
→ 향후 다양한 Shader 조합 지원을 위해, Shader 이름과 경로를 외부에서 설정 가능하게 설계 가능

_material->vertexShader->Create(L"MyShader.hlsl", "VS_Main", "vs_5_0");
_material->pixelShader->Create(L"MyShader.hlsl", "PS_Main", "ps_5_0");
  • 이를 통해 애니메이션 셰이더, 조명용 셰이더, 물리 기반 머티리얼 셰이더 등 확장 가능

3. MeshRenderer 외 컴포넌트 분리 기반 확장

컴포넌트 이름책임
Transform위치, 회전, 크기 등 공간 정보 제공
MeshRenderer렌더링 책임 담당
Camera뷰/투영 행렬 설정
Material (추후)Shader, Texture, 상태 객체 등 렌더링 세부 설정
Animator (확장 예정)스켈레탈/스프라이트 애니메이션 처리
Collider (확장 예정)충돌 영역 및 물리 엔진 연동


✅ 핵심

핵심 내용설명
✅ 렌더링 책임 분리GameObject는 컴포넌트만 보유하고, 렌더링은 MeshRenderer가 수행
✅ 조합형 구조GameObject + MeshRenderer + Transform 조합으로 구성
✅ 행렬 분리 전송View/Projection은 한 번만 전송, World는 매 오브젝트 전송
✅ 파이프라인 구조화InputLayout, Shader, State 객체를 분리 관리하고 PipelineInfo로 구성
✅ 확장성 확보추후 Material 컴포넌트, Shader/Texture 분리 가능하도록 설계 기반 구축
✅ 컴포넌트 조합 기반 구조GameObject는 Transform, MeshRenderer, Camera 등 컴포넌트를 조합
✅ GameObject는 컨테이너내부에 렌더링/버퍼 관련 직접 처리 X
✅ Render는 MeshRenderer 전담GameObject가 직접 렌더링하지 않고, 컴포넌트 위임
✅ 확장성 중심 설계Material, Shader 구조 분리, 다양한 셰이더 조합 지원 가능
✅ 유니티 구조와 유사MeshRenderer가 Shader, 텍스처 등 렌더링 책임을 독립적으로 관리

profile
李家네_공부방

0개의 댓글