(DirectX : Basic) 10. Material

Posted by : at

Category : DirectX



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();
}

About Taehyung Kim

안녕하세요? 8년차 현업 C++ 개발자 김태형이라고 합니다. 😁 C/C++을 사랑하며 다양한 사람과의 협업을 즐깁니다. ☕ 꾸준한 자기개발을 미덕이라 생각하며 노력중이며, 제가 얻은 지식을 홈페이지에 정리 중입니다. 좀 더 상세한 제 이력서 혹은 Private 프로젝트 접근 권한을 원하신다면 메일주세요. 😎

Star
Useful Links