Shader 전역 변수의 문제점
World, View, Projection, Texture0, LightDir 등 개별 요소를 직접 정의하고 CPU 코드에서 각각 세팅했다.ConstantBuffer 사용의 이점
cbuffer를 이용해 관련된 데이터를 하나의 구조체로 묶어 GPU에 전달할 수 있다.World는 오브젝트 개별 상태이므로 TransformBuffer에, View, Projection은 전역 상태이므로 GlobalBuffer에 분리하여 관리한다.Global.fx 파일로 공통 정의 모듈화
#include를 활용하여 공통 헤더를 가져올 수 있다.RenderManager를 통한 데이터 전달 구조화
MeshRenderer 내에서 직접 Shader 값을 세팅하는 방식은 유지보수와 확장에 비효율적이다.카메라 좌표의 정확한 계산
| 용어 | 설명 |
|---|---|
cbuffer | GPU에 데이터(행렬 등)를 전달하기 위한 Constant Buffer 구조체 |
GlobalBuffer | View, Projection, VP(View * Projection)을 담은 전역 상수버퍼 |
TransformBuffer | 오브젝트 개별 World 행렬을 담는 상수버퍼 |
RenderManager | GlobalBuffer/TransformBuffer 데이터를 Shader에 전달하는 관리자 클래스 |
#include | Shader 코드에서 외부 HLSL 파일을 포함시키는 전처리기 명령어 |
ID3DX11EffectConstantBuffer | ConstantBuffer를 HLSL Shader와 연결하는 Effect11 객체 |
PASS_VP | VertexShader, PixelShader를 간결하게 정의하기 위한 HLSL 매크로 |
RasterizerState | 렌더링 방식 설정 (예: 와이어프레임) |
SamplerState | 텍스처 샘플링 방식 지정 (보간, 반복 등) |
mul() | 행렬과 벡터의 곱 연산 함수 (HLSL 내장) |
#ifndef _GLOBAL_FX_
#define _GLOBAL_FX_
cbuffer GlobalBuffer
{
matrix V;
matrix P;
matrix VP;
};
cbuffer TransformBuffer
{
matrix W;
};
struct Vertex { float4 position : POSITION; };
struct VertexTexture { float4 position : POSITION; float2 uv : TEXCOORD; };
struct VertexColor { float4 Position : POSITION; float4 Color : COLOR; };
struct VertexTextureNormal { float4 position : POSITION; float2 uv : TEXCOORD; float3 normal : NORMAL; };
struct VertexOutput
{
float4 position : SV_POSITION;
float2 uv : TEXCOORD;
float3 normal : NORMAL;
};
SamplerState LinearSampler
{
Filter = MIN_MAG_MIP_LINEAR;
AddressU = Wrap;
AddressV = Wrap;
};
SamplerState PointSampler
{
Filter = MIN_MAG_MIP_POINT;
AddressU = Wrap;
AddressV = Wrap;
};
RasterizerState FillModeWireFrame
{
FillMode = Wireframe;
};
#define PASS_VP(name, vs, ps) \
pass name \
{ \
SetVertexShader(CompileShader(vs_5_0, vs())); \
SetPixelShader(CompileShader(ps_5_0, ps())); \
}
#endif
#ifndef _GLOBAL_FX_ 사용.#include "00. Global.fx"
Texture2D Texture0;
VertexOutput VS(VertexTextureNormal input)
{
VertexOutput output;
output.position = mul(input.position, W);
output.position = mul(output.position, VP);
output.uv = input.uv;
output.normal = mul(input.normal, (float3x3)W);
return output;
}
float4 PS(VertexOutput input) : SV_TARGET
{
return Texture0.Sample(LinearSampler, input.uv);
}
technique11 T0
{
PASS_VP(P0, VS, PS)
};
VS는 World 변환과 VP(View * Projection)을 곱해 최종 위치를 계산.PS는 단순 텍스처 샘플링만 수행함.struct GlobalDesc { Matrix V, P, VP; };
struct TransformDesc { Matrix W; };
class RenderManager
{
DECLARE_SINGLE(RenderManager);
public:
void Init(shared_ptr<Shader> shader);
void PushGlobalData(const Matrix& view, const Matrix& projection);
void PushTransformData(const TransformDesc& desc);
void Update(); // 프레임 단위로 View, Projection 세팅
private:
shared_ptr<Shader> _shader;
GlobalDesc _globalDesc;
TransformDesc _transformDesc;
shared_ptr<ConstantBuffer<GlobalDesc>> _globalBuffer;
ComPtr<ID3DX11EffectConstantBuffer> _globalEffectBuffer;
shared_ptr<ConstantBuffer<TransformDesc>> _transformBuffer;
ComPtr<ID3DX11EffectConstantBuffer> _transformEffectBuffer;
};
void RenderManager::Init(shared_ptr<Shader> shader)
{
_shader = shader;
_globalBuffer = make_shared<ConstantBuffer<GlobalDesc>>();
_globalBuffer->Create();
_globalEffectBuffer = _shader->GetConstantBuffer("GlobalBuffer");
_transformBuffer = make_shared<ConstantBuffer<TransformDesc>>();
_transformBuffer->Create();
_transformEffectBuffer = _shader->GetConstantBuffer("TransformBuffer");
}
void RenderManager::PushGlobalData(const Matrix& view, const Matrix& projection)
{
_globalDesc.V = view;
_globalDesc.P = projection;
_globalDesc.VP = view * projection;
_globalBuffer->CopyData(_globalDesc);
_globalEffectBuffer->SetConstantBuffer(_globalBuffer->GetComPtr().Get());
}
void RenderManager::PushTransformData(const TransformDesc& desc)
{
_transformDesc = desc;
_transformBuffer->CopyData(_transformDesc);
_transformEffectBuffer->SetConstantBuffer(_transformBuffer->GetComPtr().Get());
}
void MeshRenderer::Update()
{
if (_mesh == nullptr || _texture == nullptr || _shader == nullptr)
return;
_shader->GetSRV("Texture0")->SetResource(_texture->GetComPtr().Get());
auto world = GetTransform()->GetWorldMatrix();
RENDER->PushTransformData(TransformDesc{ world });
uint32 stride = _mesh->GetVertexBuffer()->GetStride();
uint32 offset = _mesh->GetVertexBuffer()->GetOffset();
DC->IASetVertexBuffers(0, 1, _mesh->GetVertexBuffer()->GetComPtr().GetAddressOf(), &stride, &offset);
DC->IASetIndexBuffer(_mesh->GetIndexBuffer()->GetComPtr().Get(), DXGI_FORMAT_R32_UINT, 0);
_shader->DrawIndexed(0, 0, _mesh->GetIndexBuffer()->GetCount(), 0, 0);
}
void GlobalTestDemo::Init()
{
_shader = make_shared<Shader>(L"08. GlobalTest.fx");
_camera = make_shared<GameObject>();
_camera->GetOrAddTransform();
_camera->AddComponent(make_shared<Camera>());
_camera->AddComponent(make_shared<CameraScript>());
_obj = make_shared<GameObject>();
_obj->GetOrAddTransform();
_obj->AddComponent(make_shared<MeshRenderer>());
_obj->GetMeshRenderer()->SetShader(_shader);
RESOURCES->Init();
auto mesh = RESOURCES->Get<Mesh>(L"Sphere");
_obj->GetMeshRenderer()->SetMesh(mesh);
auto texture = RESOURCES->Load<Texture>(L"Veigar", L"..\\Resources\\Textures\\veigar.jpg");
_obj->GetMeshRenderer()->SetTexture(texture);
RENDER->Init(_shader); // RenderManager에 Shader 연결
}
void GlobalTestDemo::Update()
{
_camera->Update();
RENDER->Update(); // View/Projection 갱신
_obj->Update(); // World 갱신
}
cbuffer로 구조화해 GPU에 효율적으로 전달한다.Global.fx를 통해 모든 공통 Shader 정의를 모듈화하고 중복을 제거한다.RenderManager를 통해 View/Projection/World 행렬을 Shader에 자동 전달하는 시스템을 구축한다.MeshRenderer와 GlobalTestDemo에서는 역할만 수행하고 세부 데이터 세팅은 RenderManager가 전담한다.