Mesh(정점정보), Shader(픽셀정보), Texture(이미지정보) 는 하나의 오브젝트를 표현하기 위한 클래스인데 하나로 묶을수 있을까??
Material이라는 클래스를 새로 만들어보자
단, 주의해야할 점은 하나의 Material마다 각각 Mesh, Shader, Texture를 만든다고 생각하면안된다
Mesh, Shader, Texture는 다른 Material에도 사용될 수 있기에 재활용성을 고려하여 구현되어야 한다(자세한건 아래서)
Material에 이용될 정보를 b1에 담도록 하겠다
cbuffer TEST_B0 : register(b0)
{
float4 offset0;
};
cbuffer MATERIAL_PARAMS : register(b1)
{
// 아직은 모르지만 material정보를 여기 담겠다.
int int_0;
int int_1;
int int_2;
int int_3;
int int_4;
float float_0;
float float_1;
float float_2;
float float_3;
float float_4;
};
b1에 담기위해 constant buffer도 수정이 되어야 한다.
enum class CONSTANT_BUFFER_TYPE : uint8
{
TRANSFORM, // Transform 용도인가
MATERIAL, // Material 용도인가
END
};
enum
{
CONSTANT_BUFFER_COUNT = static_cast<uint8>(CONSTANT_BUFFER_TYPE::END)
};
우선 버퍼의 생성
/*
CreateConstantBuffer(CBV_REGISTER::b0, sizeof(Transform), 256);
CreateConstantBuffer(CBV_REGISTER::b1, sizeof(MaterialParams), 256);
*/
void Engine::CreateConstantBuffer(CBV_REGISTER reg, uint32 bufferSize, uint32 count)
{
// CBV_REGISTER는 b0, b1 ... 순차적으로 만들어줘야함
// 중간에 누락된게 있을까봐 검수하는 과정임.
uint8 typeInt = static_cast<uint8>(reg);
assert(_constantBuffers.size() == typeInt);
shared_ptr<ConstantBuffer> buffer = make_shared<ConstantBuffer>();
buffer->Init(reg, bufferSize, count);
_constantBuffers.push_back(buffer);
}
초기화
// CBV_REGISTER reg를 통해 b0, b1인지 확인
void ConstantBuffer::Init(CBV_REGISTER reg, uint32 size, uint32 count)
{
_reg = reg;
_elementSize = (size + 255) & ~255;
_elementCount = count;
CreateBuffer();
CreateView();
}
void ConstantBuffer::PushData(void* buffer, uint32 size)
{
assert(_currentIndex < _elementCount);
assert(_elementSize == ((size + 255) & ~255));
::memcpy(&_mappedBuffer[_currentIndex * _elementSize], buffer, size);
D3D12_CPU_DESCRIPTOR_HANDLE cpuHandle = GetCpuHandle(_currentIndex);
// Constant Buffer 업로드
GEngine->GetTableDescHeap()->SetCBV(cpuHandle, _reg);
/*
void TableDescriptorHeap::SetCBV(D3D12_CPU_DESCRIPTOR_HANDLE srcHandle, CBV_REGISTER reg)
{
D3D12_CPU_DESCRIPTOR_HANDLE destHandle = GetCPUHandle(reg);
uint32 destRange = 1;
uint32 srcRange = 1;
DEVICE->CopyDescriptors(1, &destHandle, &destRange, 1, &srcHandle, &srcRange, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
}
*/
_currentIndex++;
}
참고로 PushData호출시점은
void Mesh::Render()
{
// ...
CONST_BUFFER(CONSTANT_BUFFER_TYPE::TRANSFORM)->PushData(&_transform, sizeof(_transform));
Material 클래스 만들기
#pragma once
class Shader;
class Texture;
enum
{
MATERIAL_INT_COUNT = 5, // int 5개
MATERIAL_FLOAT_COUNT = 5, // float 5개
MATERIAL_TEXTURE_COUNT = 5, // texture 5개 를 쓸것이다.
};
struct MaterialParams
{
void SetInt(uint8 index, int32 value) { intParams[index] = value; }
void SetFloat(uint8 index, float value) { floatParams[index] = value; }
array<int32, MATERIAL_INT_COUNT> intParams;
array<float, MATERIAL_FLOAT_COUNT> floatParams;
// int32 intParams[MATERIAL_INT_COUNT];
// array는 vector처럼 길이를 늘릴수 있는것도 아니고
// int32 [] 이거랑 무슨 차이인가??
// -> 범위 체크가 불가능하다.(은근 유용함)
// intParams[index] = value; 이걸할때 범위를 벗어나면 Crash!!
};
class Material
{
public:
shared_ptr<Shader> GetShader() { return _shader; }
void SetShader(shared_ptr<Shader> shader) { _shader = shader; }
void SetInt(uint8 index, int32 value) { _params.SetInt(index, value); }
void SetFloat(uint8 index, float value) { _params.SetFloat(index, value); }
void SetTexture(uint8 index, shared_ptr<Texture> texture) { _textures[index] = texture; }
void Update();
private:
shared_ptr<Shader> _shader;
MaterialParams _params;
array<shared_ptr<Texture>, MATERIAL_TEXTURE_COUNT> _textures;
};
void Material::Update()
{
// CBV 업로드
CONST_BUFFER(CONSTANT_BUFFER_TYPE::MATERIAL)->PushData(&_params, sizeof(_params));
// SRV 업로드
for (size_t i = 0; i < _textures.size(); i++)
{
if (_textures[i] == nullptr)
continue;
SRV_REGISTER reg = SRV_REGISTER(static_cast<int8>(SRV_REGISTER::t0) + i);
GEngine->GetTableDescHeap()->SetSRV(_textures[i]->GetCpuHandle(), reg);
}
// 파이프라인 세팅
_shader->Update();
}
실제 사용은 아래와 같이 한다.
void Game::Init(const WindowInfo& info)
{
GEngine->Init(info);
vector<Vertex> vec(4);
vec[0].pos = Vec3(-0.5f, 0.5f, 0.5f);
vec[0].color = Vec4(1.f, 0.f, 0.f, 1.f);
vec[0].uv = Vec2(0.f, 0.f);
vec[1].pos = Vec3(0.5f, 0.5f, 0.5f);
vec[1].color = Vec4(0.f, 1.f, 0.f, 1.f);
vec[1].uv = Vec2(1.f, 0.f);
vec[2].pos = Vec3(0.5f, -0.5f, 0.5f);
vec[2].color = Vec4(0.f, 0.f, 1.f, 1.f);
vec[2].uv = Vec2(1.f, 1.f);
vec[3].pos = Vec3(-0.5f, -0.5f, 0.5f);
vec[3].color = Vec4(0.f, 1.f, 0.f, 1.f);
vec[3].uv = Vec2(0.f, 1.f);
vector<uint32> indexVec;
{
indexVec.push_back(0);
indexVec.push_back(1);
indexVec.push_back(2);
}
{
indexVec.push_back(0);
indexVec.push_back(2);
indexVec.push_back(3);
}
mesh->Init(vec, indexVec);
shared_ptr<Shader> shader = make_shared<Shader>();
shared_ptr<Texture> texture = make_shared<Texture>();
shader->Init(L"..\\Resources\\Shader\\default.hlsli");
texture->Init(L"..\\Resources\\Texture\\veigar.jpg");
shared_ptr<Material> material = make_shared<Material>();
material->SetShader(shader);
material->SetFloat(0, 0.3f);
material->SetFloat(1, 0.4f);
material->SetFloat(2, 0.3f);
material->SetTexture(0, texture);
mesh->SetMaterial(material);
GEngine->GetCmdQueue()->WaitSync();
}