Instancing

이승덱·2021년 8월 17일
0

Graphics&DirectX

목록 보기
32/37

Instancing

  • 동일한 개체를 여러개 렌더링해야할 경우 개체 하나 하나를 렌더링하는 것은 굉장히 많은 연산을 요구하게 된다.

  • 이에 대한 해결방안으로 Draw Call을 그려야할 개체 수 만큼 하지 않고 한번의 Draw Call로 여러개의 개체를 그리도록 요청하는 기술을 Instancing이라고 한다.


//InstancingManager.cpp
void InstancingManager::Render(vector<shared_ptr<GameObject>>& gameObjects)
{
	map<uint64, vector<shared_ptr<GameObject>>> cache;

	for (shared_ptr<GameObject>& gameObject : gameObjects)
	{
		const uint64 instanceId = gameObject->GetMeshRenderer()->GetInstanceID();
		cache[instanceId].push_back(gameObject);
	}

	for (auto& pair : cache)
	{
		const vector<shared_ptr<GameObject>>& vec = pair.second;

		// 물체가 하나
		if (vec.size() == 1)
		{
			vec[0]->GetMeshRenderer()->Render();
		}
		else	// 물체가 두개 이상
		{
			const uint64 instanceId = pair.first;

			for (const shared_ptr<GameObject>& gameObject : vec)
			{
				InstancingParams params;
				params.matWorld = gameObject->GetTransform()->GetLocalToWorldMatrix();
				params.matWV = params.matWorld * Camera::S_MatView;
				params.matWVP = params.matWorld * Camera::S_MatView * Camera::S_MatProjection;

				AddParam(instanceId, params);
			}

			shared_ptr<InstancingBuffer>& buffer = _buffers[instanceId];
			vec[0]->GetMeshRenderer()->Render(buffer);
		}
	}
}

void InstancingManager::ClearBuffer()
{
	for (auto& pair : _buffers)
	{
		shared_ptr<InstancingBuffer>& buffer = pair.second;
		buffer->Clear();
	}
}

void InstancingManager::AddParam(uint64 instanceId, InstancingParams& data)
{
	if (_buffers.find(instanceId) == _buffers.end())
		_buffers[instanceId] = make_shared<InstancingBuffer>();

	_buffers[instanceId]->AddData(data);
}



//MeshRender.cpp
void MeshRenderer::Render()
{
	GetTransform()->PushData();
	_material->PushGraphicsData();
	_mesh->Render();
}

void MeshRenderer::Render(shared_ptr<InstancingBuffer>& buffer)
{
	buffer->PushData();
	_material->PushGraphicsData();
	_mesh->Render(buffer);
}



// InstancingBuffer.cpp
void InstancingBuffer::PushData()
{
	const uint32 dataCount = GetCount();
	if (dataCount > _maxCount)
		Init(dataCount);

	const uint32 bufferSize = dataCount * sizeof(InstancingParams);

	void* dataBuffer = nullptr;
	D3D12_RANGE readRange{ 0, 0 };
	_buffer->Map(0, &readRange, &dataBuffer);
	memcpy(dataBuffer, &_data[0], bufferSize);
	_buffer->Unmap(0, nullptr);

	_bufferView.BufferLocation = _buffer->GetGPUVirtualAddress();
	_bufferView.StrideInBytes = sizeof(InstancingParams);
	_bufferView.SizeInBytes = bufferSize;
}
  • InstancingManager.cpp 를 보면 하나의 개체에 대한 렌더링 요청과 두개 이상의 개체에 대한 렌더링 요청을 오버로딩된 Render함수를 사용하여 각기 다른 방식으로 처리하는 것을 알 수 있다.

  • MeshRenderer.cpp 를 통해 Instancing 객체의 PushData를 활용하여 Instance의 개수를 버퍼에 넣어준다.

  • Instancing을 활용하지 않은 방식: FPS 가 380이 나오고 있다.

  • Instancing을 활용한 방식: FPS 가 666이 나오고 있다.
profile
공부 기록용 블로그입니다

0개의 댓글