게임 개발 자료/DirectX 스터디

DirectX 11 스터디 - 모듈화 (2), 렌더링을 책임질 Pipeline클래스 작성

원소랑 2023. 9. 20. 14:18
728x90

앞에서 Geometry, VertexData 부터 Output Merger 의 BlendState까지 모두 모듈화 해서 DX COM객체를 캡슐화 했고, 이번엔 모듈화한 객체들을 가지고 Rendering Pipeline 을 실제 수행하는 객체를 작성해본다.

 

파이프라인에서 고유하게 존재하고 사용되어야 하는 모듈들과, 3D 물체마다 값/설정이 변경되어야 하는 모듈을 구분한다. 3D 물체마다 설정이 변경되어야 하는 객체들을 별도로 그룹화 해서 PipelineInfo 구조체로 묶어준다. 렌더링 파이프라인에 이 구조체를 기준으로 설정을 바꿔서 태워주면 새로운 물체를 그릴 수 있게 됨.

 

PipelineInfo 는 아래 구성 요소가 포함됨. 여기서 ConstantBuffer는 3D 메시마다 필요할 수도, 필요하지 않을 수도 있기 때문에 공용 객체, 즉 PipelineInfo 에 포함시키지 않는다. 마찬가지로 Texture, SamplerState 도 공용 Pipeline 갱신에는 참여하지 않도록 제거한다. 필요할 때만 별도로 호출하도록 해야. 옵셔널로 분리.

 

파이프라인 공용 업데이트 함수를 정리하면 아래와 같이 코드를 짤 수 있음.

void Pipeline::Update(PipelineInfo info)
{
	// IA
	_deviceContext->IASetInputLayout(info.inputLayout->GetComPtr().Get());
	_deviceContext->IASetPrimitiveTopology(info.topology);

	// VS
	if (info.vertexShader)
		_deviceContext->VSSetShader(info.vertexShader->GetComPtr().Get(), nullptr, 0);

	// RS
	if (info.rasterizerState)
		_deviceContext->RSSetState(info.rasterizerState->GetComPtr().Get());

	// PS
	if (info.pixelShader)
		_deviceContext->PSSetShader(info.pixelShader->GetComPtr().Get(), nullptr, 0);
	// OM
	if (info.blendState)
		_deviceContext->OMSetBlendState(info.blendState->GetComPtr().Get(), info.blendState->GetBlendFactor(), info.blendState->GetSampleMask());
}

위 Update 에서 제외된 옵셔널한 셋팅을 별도 메소드로 분리해서 제공.

VertexBuffer, IndexBuffer 가 대표적.

void Pipeline::Update(PipelineInfo info)
{
	// ...
}

void Pipeline::SetVertexBuffer(shared_ptr<VertexBuffer> buffer)
{
	uint32 stride = buffer->GetStride();
	uint32 offset = buffer->GetOffset();

	_deviceContext->IASetVertexBuffers(0, 1, buffer->GetComPtr().GetAddressOf(), &stride, &offset);
}

void Pipeline::SetIndexBuffer(shared_ptr<IndexBuffer> buffer)
{
	_deviceContext->IASetIndexBuffer(buffer->GetComPtr().Get(), DXGI_FORMAT_R32_UINT, 0);
}

이렇게 VertexBuffer, IndexBuffer 설정 메소드까지 완료. 이제 ConstantBuffer를 설정해주는 메소드를 template으로 구현해줌. ConstantBuffer는 이전 예제에서 TransformData 구조체를 만들어서 ID3D11Buffer에 복사하고, 렌더 Begin/End 사이에 전달해줬는데, TransformData가 아닌 다른 타입 데이터를 복사/전달할 수 있기 때문.

 

이 때, ConstantBuffer는 VertexShader와 PixelShader각각 전달할 수 있음. 마찬가지로, SetShaderResource(), SetSamplers도 각각 VS용, PS용 따로따로 존재함.

class Pipeline
{
	//...
    template<typename T>
	void SetConstantBuffer(uint32 slot, uint32 scope, shared_ptr<ConstantBuffer<T>> buffer)
	{
		if (scope & SS_VertexShader)
			_deviceContext->VSSetConstantBuffers(slot, 1, buffer->GetComPtr().GetAddressOf());

		if (scope & SS_PixelShader)
			_deviceContext->PSSetConstantBuffers(slot, 1, buffer->GetComPtr().GetAddressOf());
	}
}

void Pipeline::SetTexture(uint32 slot, uint32 scope, shared_ptr<Texture> texture)
{
	if (scope & SS_VertexShader)
		_deviceContext->VSSetShaderResources(slot, 1, texture->GetComPtr().GetAddressOf());

	if (scope & SS_PixelShader)
		_deviceContext->PSSetShaderResources(slot, 1, texture->GetComPtr().GetAddressOf());
}

void Pipeline::SetSamplerState(uint32 slot, uint32 scope, shared_ptr<SamplerState> samplerState)
{
	if (scope & SS_VertexShader)
		_deviceContext->VSSetSamplers(slot, 1, samplerState->GetComPtr().GetAddressOf());

	if (scope & SS_PixelShader)
		_deviceContext->PSSetSamplers(slot, 1, samplerState->GetComPtr().GetAddressOf());
}

여기까지. Pipeline 클래스 선언부만 요약하면 아래와 같음.

class Pipeline
{
	// Pseudo code.
	void Update(PipelineInfo info);
	void SetVertexBuffer(shared_ptr<VertexBuffer> buffer);
	void SetIndexBuffer(shared_ptr<IndexBuffer> buffer);

	template<typename T>
	void SetConstantBuffer(uint32 slot, uint32 scope, shared_ptr<ConstantBuffer<T>> buffer)
	void SetTexture(uint32 slot, uint32 scope, shared_ptr<Texture> texture);
	void SetSamplerState(uint32 slot, uint32 scope, shared_ptr<SamplerState> samplerState);

	void Draw(uint32 vertexCount, uint32 startVertexLocation);
	void DrawIndexed(uint32 indexCount, uint32 startIndexLocation, uint32 baseIndexLocation);
};

이제 위에서 만든 Pipeline 객체를 기존에 작성했던 Render() 함수에서 Pipeline::Update() 를 호출하도록 수정해보면 아래와 같이 됨.

 

먼저 기존 코드.

Render()
{
    uint32 stride = sizeof(VertexTextureData);
    uint32 offset = 0;
    auto _deviceContext = _RHI->GetDeviceContext();

    // IA
    _deviceContext->IASetVertexBuffers(0, 1, _vertexBuffer->GetComPtr().GetAddressOf(), &stride, &offset); 
    _deviceContext->IASetIndexBuffer(_indexBuffer->GetComPtr().Get(), DXGI_FORMAT_R32_UINT, 0);
    _deviceContext->IASetInputLayout(_inputLayout->GetComPtr().Get());
    _deviceContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);

    // VS
    _deviceContext->VSSetShader(_vertexShader->GetComPtr().Get(), nullptr, 0);
    _deviceContext->VSSetConstantBuffers(0, 1, _constantBuffer->GetComPtr().GetAddressOf());

    // RS
    _deviceContext->RSSetState(_rasterizerState->GetComPtr().Get());

    // PS
    _deviceContext->PSSetShader(_pixelShader->GetComPtr().Get(), nullptr, 0);
    _deviceContext->PSSetConstantBuffers(0, 1, _constantBuffer->GetComPtr().GetAddressOf());
    _deviceContext->PSSetShaderResources(0, 1, _texture1->GetComPtr().GetAddressOf());
    _deviceContext->PSSetSamplers(0, 1, _samplerState->GetComPtr().GetAddressOf());

    // OM
    _deviceContext->OMSetBlendState(_blendState->GetComPtr().Get(), _blendState->GetBlendFactor(), _blendState->GetSampleMask());

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

아래는 Pipeline::Update() 로 교체한 코드. 2 단계로 구분해서 정리한다.

PipelineInfo info;
info.inputLayout = _inputLayout;
info.vertexShader = _vertexShader;
info.pixelShader = _pixelShader;
info.rasterizerState = _rasterizerState;
info.blendState = _blendState;
_pipeline->Update(info);

auto _deviceContext = _RHI->GetDeviceContext();
uint32 stride = sizeof(VertexTextureData);
uint32 offset = 0;
_deviceContext->IASetVertexBuffers(0, 1, _vertexBuffer->GetComPtr().GetAddressOf(), &stride, &offset); 
_deviceContext->IASetIndexBuffer(_indexBuffer->GetComPtr().Get(), DXGI_FORMAT_R32_UINT, 0);
_deviceContext->VSSetConstantBuffers(0, 1, _constantBuffer->GetComPtr().GetAddressOf());
_deviceContext->PSSetShaderResources(0, 1, _texture1->GetComPtr().GetAddressOf());
_deviceContext->PSSetSamplers(0, 1, _samplerState->GetComPtr().GetAddressOf());
_deviceContext->DrawIndexed(_geometry->GetIndexCount(), 0, 0);

나머지 VertexBuffer, IndexBuffer, ConstantBuffer, ShaderResources 까지 모두 교체해서 마무리.

PipelineInfo info;
info.inputLayout = _inputLayout;
info.vertexShader = _vertexShader;
info.pixelShader = _pixelShader;
info.rasterizerState = _rasterizerState;
info.blendState = _blendState;
_pipeline->Update(info);

_pipeline->SetVertexBuffer(_vertexBuffer);
_pipeline->SetIndexBuffer(_indexBuffer);
_pipeline->SetConstantBuffer(0, SS_VertexShader, _constantBuffer);
_pipeline->SetTexture(0, SS_PixelShader, _texture1);
_pipeline->SetSamplerState(0, SS_PixelShader, _samplerState);
_pipeline->DrawIndexed(_geometry->GetIndexCount(), 0, 0);

여기까지.

렌더링을 책임져줄 Pipeline 클래스 작성과정 끝.

.


<리얼-타임 렌더링(REAL-TIME RENDERING) 4/e> 구입 링크
https://link.coupang.com/a/8VWas

 

리얼-타임 렌더링 4/e

COUPANG

www.coupang.com

<DirectX 12를 이용한 3D 게임 프로그래밍 입문> 구입 링크
https://link.coupang.com/a/8V4Wq

 

DirectX 12를 이용한 3D 게임 프로그래밍 입문:게임 개발 중심으로 익히는 대화식 컴퓨터 그래픽 프

COUPANG

www.coupang.com

<이득우의 게임 수학:39가지 예제로 배운다! 메타버스를 구성하는 게임 수학의 모든 것> 구입 링크

https://link.coupang.com/a/9BqLd

 

이득우의 게임 수학:39가지 예제로 배운다! 메타버스를 구성하는 게임 수학의 모든 것

COUPANG

www.coupang.com

유니티 에셋 스토어 링크

https://assetstore.unity.com?aid=1011lvz7h 

 

에셋스토어

여러분의 작업에 필요한 베스트 에셋을 찾아보세요. 유니티 에셋스토어가 2D, 3D 모델, SDK, 템플릿, 툴 등 여러분의 콘텐츠 제작에 날개를 달아줄 다양한 에셋을 제공합니다.

assetstore.unity.com

(링크를 통해 도서/에셋 구입시 일정액의 수수료를 지급받습니다.)


728x90
반응형