DirectX

21. 프레임워크 제작_Pipeline

devRiripong 2024. 1. 5.
반응형

1. rasterizer, samplerState, blendState 클래스

애매한 3 총사, rasterizer, samplerState, blendState 3 총사를 정리하고,

다시 조립하는 것을 해볼 것이다.

 

03.Rasterizer에 RasterizerState,

04.PixelShader에 SamplerState,

05.OutputMeger에 BlendState 클래스를 추가한다.

 

Rasterizer는 VS에서 PS로 넘어가기 전에 Rasterizer에 대한 옵션을 선택할 수 있는 거,

SamplerState는 uv매핑을 할 때 어떤 식으로 할지 옵션과 관련

BlendState는 최종적으로 화면이 출력이 될 때 어떤 식으로 생상을 섞을지와 관련이 있었다.

#pragma once
class RasterizerState
{
public: 
	RasterizerState(ComPtr<ID3D11Device> device);
	~RasterizerState(); 

	ComPtr<ID3D11RasterizerState> GetComPtr() { return _rasterizerState; }

	void Create();

private: 
	ComPtr<ID3D11Device> _device;
	ComPtr<ID3D11RasterizerState> _rasterizerState;
};
#include "pch.h"
#include "RasterizerState.h"

RasterizerState::RasterizerState(ComPtr<ID3D11Device> device) 
	: _device(device)
{
}

RasterizerState::~RasterizerState()
{
}

void RasterizerState::Create()
{
	D3D11_RASTERIZER_DESC desc;
	ZeroMemory(&desc, sizeof(desc));
	desc.FillMode = D3D11_FILL_SOLID;
	desc.CullMode = D3D11_CULL_BACK;
	desc.FrontCounterClockwise = false; // 이것들은 실습을 하면서 얘기를 해볼거야. 

	HRESULT hr = _device->CreateRasterizerState(&desc, _rasterizerState.GetAddressOf());
	CHECK(hr);
}

Rasterizer는 이렇게 만들어 주면 되고,

 

마찬가지로 SamplerState도

#pragma once
class SamplerState
{
public:
	SamplerState(ComPtr<ID3D11Device> device); 
	~SamplerState(); 

	ComPtr<ID3D11SamplerState> GetComPtr() { return _samplerState;  }

	void Create(); 

private:
	ComPtr<ID3D11Device> _device;
	ComPtr<ID3D11SamplerState> _samplerState;
};
#include "pch.h"
#include "SamplerState.h"

SamplerState::SamplerState(ComPtr<ID3D11Device> device)
	: _device(device)
{
}

SamplerState::~SamplerState()
{
}

void SamplerState::Create()
{
	D3D11_SAMPLER_DESC desc;
	ZeroMemory(&desc, sizeof(desc));
	// 여기다가 마음에 드는 옵션들을 세팅해 보면된다. 
	desc.AddressU = D3D11_TEXTURE_ADDRESS_BORDER;
	desc.AddressV = D3D11_TEXTURE_ADDRESS_BORDER;
	desc.AddressW = D3D11_TEXTURE_ADDRESS_BORDER;
	desc.BorderColor[0] = 1;
	desc.BorderColor[1] = 0;
	desc.BorderColor[2] = 0;
	desc.BorderColor[3] = 1;
	// 여기부턴 별로 안중요해서 그냥 블로그에서 본 아무 값
	desc.ComparisonFunc = D3D11_COMPARISON_ALWAYS;
	desc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR;
	desc.MaxAnisotropy = 16;
	desc.MaxLOD = FLT_MAX;
	desc.MinLOD = FLT_MIN;
	desc.MipLODBias = 0.0f;

	HRESULT hr = _device->CreateSamplerState(&desc, _samplerState.GetAddressOf());
	CHECK(hr); 
}

이렇게 해줄 수 있다.

 

BlendState도 비슷한 느낌으로 채워주면 된다.

#pragma once
class BlendState
{
public:
	BlendState(ComPtr<ID3D11Device> device);
	~BlendState(); 

	const float* GetBlendFactor() { return &_blendFactor; } 
	uint32 GetSampleMask() { return _sampleMask; } 
	ComPtr<ID3D11BlendState> GetComPtr() { return _blendState; }

	void Create(D3D11_RENDER_TARGET_BLEND_DESC blendDesc =
		{
			true,
			D3D11_BLEND_SRC_ALPHA,
			D3D11_BLEND_INV_SRC_ALPHA,
			D3D11_BLEND_OP_ADD,
			D3D11_BLEND_ONE,
			D3D11_BLEND_ZERO,
			D3D11_BLEND_OP_ADD,
			D3D11_COLOR_WRITE_ENABLE_ALL
		}, float factor = 0.f); 

private:
	ComPtr<ID3D11Device> _device;
	ComPtr<ID3D11BlendState> _blendState;
	float _blendFactor = 0.f; 
	uint32 _sampleMask = 0xFFFFFFFF; 
};
#include "pch.h"
#include "BlendState.h"

BlendState::BlendState(ComPtr<ID3D11Device> device) : _device(device)
{
}

BlendState::~BlendState()
{
}

void BlendState::Create(D3D11_RENDER_TARGET_BLEND_DESC blendDesc, float factor)
{
	_blendFactor = factor; 

	D3D11_BLEND_DESC desc;
	ZeroMemory(&desc, sizeof(D3D11_BLEND_DESC));
	desc.AlphaToCoverageEnable = false;
	desc.IndependentBlendEnable = false;
	desc.RenderTarget[0] = blendDesc; 

	HRESULT hr = _device->CreateBlendState(&desc, _blendState.GetAddressOf());
	CHECK(hr);
}

Game클래스에서 옮겨준 부분은 삭제한다.

 

 

2. rasterizer, samplerState, blendState 클래스 적용

pch.h에 가서,

#include "RasterizerState.h"
#include "SamplerState.h"
#include "BlendState.h"

이렇게 추가하고,

Game.h로 가서 옮겨줬던 부분

//ComPtr<ID3D11RasterizerState> _rasterizerState = nullptr;
shared_ptr<RasterizerState> _rasterizerState;
//ComPtr<ID3D11SamplerState> _samplerState = nullptr; 
shared_ptr<SamplerState> _samplerState;
//ComPtr<ID3D11BlendState> _blendState = nullptr; 
shared_ptr<BlendState> _blendState;

이렇게 해주면,

#pragma once
class Game
{
public:
	Game();
	~Game();

public:
	void Init(HWND hwnd);
	void Update();
	void Render();

private:
	HWND _hwnd;

	shared_ptr<Graphics> _graphics; 

private: 
	shared_ptr<Geometry<VertexTextureData>> _geometry;
	shared_ptr<VertexBuffer> _vertexBuffer; 
	shared_ptr<IndexBuffer> _indexBuffer; 
	shared_ptr<InputLayout> _inputLayout; 

	shared_ptr<VertexShader> _vertexShader;
	shared_ptr<RasterizerState> _rasterizerState; 
	shared_ptr<PixelShader> _pixelShader; 
	shared_ptr<Texture> _texture1;
	shared_ptr<SamplerState> _samplerState; 
	shared_ptr<BlendState> _blendState; 

private:
	TransformData _transformData;
	shared_ptr<ConstantBuffer<TransformData>> _constantBuffer; 

	Vec3 _localPosition = { 0.f, 0.f, 0.f }; 
	Vec3 _localRotation = { 0.f, 0.f, 0.f };
	Vec3 _localScale = { 1.f, 1.f, 1.f }; 
};

이제 ComPtr이 없게 정리가 되었다.

 

Game.cpp에서 shared_ptr로 변환한 변수들을 채워준다.

Game::Init에

_rasterizerState = make_shared<RasterizerState>(_graphics->GetDevice());
_samplerState = make_shared<SamplerState>(_graphics->GetDevice()); 
_blendState = make_shared<BlendState>(_graphics->GetDevice());
_rasterizerState->Create(); 
_samplerState->Create(); 
_blendState->Create(); 
//CreateRasterizerState();
//CreateSamplerState();
//CreateBlendState();

인자를 넣어줄 수도 있지만 일단 기본 인자로 한다.

 

빌드를 하면 에러가 나는데 하나씩 수정해 주면 된다.

Game::Render에서

// RS
_deviceContext->RSSetState(_rasterizerState->GetComPtr().Get());
_deviceContext->PSSetSamplers(0, 1, _samplerState->GetComPtr().GetAddressOf());

// OM
_deviceContext->OMSetBlendState(_blendState->GetComPtr().Get(), nullptr, 0xFFFFFFFF);

이렇게 수정하고 빌드를 하면 통과된다.

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

이렇게 하면 조금 더 맞춰 준 것에 정확해진다.

실행하면 이미지가 잘 나온다.

 

이렇게 1차적으로 정리가 끝났다.

 

3. Pipeline 클래스 공용부

부품들을 래핑 해서 관리할 수 있다 까지는 됐지만,

추가적으로 어떤 애들을 뭉쳐서 관리해야 될 것인가가 관건이다.

Game::Render에서 나중에 가면 물체 단위로 뭔가를 만들어 주게 된다.

다양한 게임 엑터들을 배치해서 하나씩 그려 주세요가 코드 흐름이 된다고 볼 수 있다.

그리고 매번 _deviceContext→IASet~~ 으로 세팅하는 부분들도 별도의 클래스로 빼가지고 관리를 해야 되겠다는 얘기가 된다.

렌더링 파이프라인 자체를 하나의 클래스로 빼가지고 이중으로 관리를 하는 거다. 장점은 대포를 쏜 다음에 대포에 총알 장전을 하듯, 렌더링 파이프라인이 하나의 큰 대포라고 보면 된다.

물체마다 갈아 끼우는 부분이 있을 텐데, 갈아 끼우는 부분만 하나의 포탄으로 만들어서 갈아 끼울 수 있게끔 만들어 주는 게 핵심이다.

현재 렌더링 파이프라인을 보면

void Game::Render()
{
	_graphics->RenderBegin();

	// IA - VS - RS - PS - OM
	{
		uint32 stride = sizeof(VertexTextureData);
		uint32 offset = 0; 

		auto _deviceContext = _graphics->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->PSSetShaderResources(0, 1, _texture1->GetComPtr().GetAddressOf());
		// _deviceContext->PSSetShaderResources(1, 1, _shaderResourceView2.GetAddressOf()); 
		_deviceContext->PSSetSamplers(0, 1, _samplerState->GetComPtr().GetAddressOf());

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

		// _deviceContext->Draw(_vertices.size(), 0); 
		_deviceContext->DrawIndexed(_geometry->GetIndexCount(), 0, 0);
	}

	_graphics->RenderEnd();
}

_deviceContext를 이용해서 세팅을 하고 있다.

이 부분 자체를 편리하게 세팅할 수 있게 Pipeline이라는 이름으로 만들어 본다.

 

Pilpeline 필터에 Pipeline이란 이름으로 클래스를 추가한다.

deviceContext가 핵심이다.

다른 device가 다른 애들을 만들어 주는 데 사용되기 때문에 device가 핵심이었지만,

파이프라인은 deviceContext가 렌더링 파이프라인과 나머지 리소스들을 묶어주는, 자원을 매핑해 주는 게 역할을 해주기 때문에, deviceContext가 핵심이다.

#pragma once

// 기본적으로 모든애들이 공통적으로 사용하는 정보를 빼준다.이걸 세팅해서 포탄을 갈아 끼우는 것 처럼 작업을 해준다.
struct PipelineInfo
{
	// Game.h에서 어떤 애들은 물체마다 고유하게 변하는 애들이 있고, 
	// shader 같이 물체마다 공유해서 사용하는 애들도 있다.
	// 그런 부분들을 분리해서 관리해 본다. 

	// 이런 옵션들을 이용해 렌더링 파이프 라인을 고칠 수 있다고 연결을 해 놓는 거 
	shared_ptr<InputLayout> inputLayout; 
	shared_ptr<VertexShader> vertexShader; // 상용 엔진의 경우 공용 shader 하나를 돌려 쓰고, 일부 특정 물체는 전용 shader를 쓰는 경우가 많다. 
	shared_ptr<PixelShader> pixelShader;
	shared_ptr<RasterizerState> rasterizerState; 
	shared_ptr<BlendState> blendState; 
	D3D11_PRIMITIVE_TOPOLOGY topology = D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST; 
};
// 이 정보를 토대로 포탄을 갈아 끼는 거다.

class Pipeline
{
public:
	Pipeline(ComPtr<ID3D11DeviceContext> deviceContext); 
	~Pipeline(); 
	
	void UpdatePipeline(PipelineInfo info); // 정보를 갈아 끼운다. 

private:
	ComPtr<ID3D11DeviceContext> _deviceContext; 

};
#include "pch.h"
#include "Pipeline.h"

Pipeline::Pipeline(ComPtr<ID3D11DeviceContext> deviceContext)
	: _deviceContext(deviceContext)
{
}

Pipeline::~Pipeline()
{
}

void Pipeline::UpdatePipeline(PipelineInfo info)
{
	// Game::Render의 일부를 꺼내 온다. 일단 다 가져 온 다음에 걸러냈다.

	// IA	
	// vertexBuffer, indexBuffer는 나중에
	_deviceContext->IASetInputLayout(info.inputLayout->GetComPtr().Get()); // 어떻게 생겨먹은 애인지 묘사해줘야 해
	_deviceContext->IASetPrimitiveTopology(info.topology); // 삼각형으로 인지를 해달라 부탁 

	// VS
	// 왜  ConstantBuffer를 세팅하는 부분은 왜 info에 포함되지 않는걸까? ->상수 버퍼는 사용할지 말지가 명확하지 않기 때문이다.
	// 물체 마다 달라질 수 있는 부분은 따로 별도의 함수를 빼줄 것이고, 여기서는 일반적인 공용 부분만 묶어준다. 그래서 여기서 뺐다.
	if(info.vertexShader)
		_deviceContext->VSSetShader(info.vertexShader->GetComPtr().Get(), nullptr, 0);

	// RS
	if(info.rasterizerState) // 이것도 사실 옵션이긴 하다. 있으면 세팅해준다.
		_deviceContext->RSSetState(info.rasterizerState->GetComPtr().Get());

	// PS
	// shaderResource, sampler도 사용할지 말지 모르니 건너 띈다.
	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());
}

여기서 하고 싶은 이야기는 PipelineInfo라는 걸 채워서 넣어주면 기본적인 공통적인 부분들을 다 채워주게 된다.

 

4. Pipeline 클래스 세부

그다음에 세부적으로 필요한 나머지 부분들을 채워주게 된다.

Game::Render에서 지금까지는 테스트 용도로 사용했으니까 상수 버퍼가 있다고 가정을 하고, 텍스쳐가 있어서 셰이더를 넘겨주고 하는 걸 만들어서 잘 맞아떨어졌지만 이건 그때 그 때 달라질 수 있는 거니까 이런 식으로 하나의 함수로 다 만들어 주면 안되는 거였다.

그럼 나머지 부분을 하나씩 챙겨 준다.

// 버텍스 버퍼, 인덱스 버퍼를 채워주는 거 
	void SetVertexBuffer(shared_ptr<VertexBuffer> buffer); 
	void SetIndexBuffer(shared_ptr<IndexBuffer> buffer);

를 Pipeline.h에 선언하고, 구현을 해준다.

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

	// Game::Render를 참고해서 만들어 보면
	_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);
}

이런 식으로 공용적이지 않은 부분들, 그 때 그 때 달라지는 부분들은 알아서 조립해 채우라고 열어준다.

ContantBuffer가 조금 복잡하다.

Game::Render에서 보면

_deviceContext->VSSetConstantBuffers(0, 1, _constantBuffer->GetComPtr().GetAddressOf());

_constantBuffer가 어떻게 만들어졌는지 보면,

shared_ptr<ConstantBuffer<TransformData>> _constantBuffer;

ConstantBuffer에 TransformData를 담는 버퍼라고 타입이 정해져 있는데, TransformData는 그 때 그 때 달라질 수 있다. 하드코딩 해 줄 수 없기 때문에

Pipeline.h로 돌아가서 template으로 만들어 준다.

함수에 적용하는 건 적용한 애만 헤더에 만들어 주면 되고, 나머지 애들만 cpp에 만들어 주면 된다.

	template<typename T>
	void SetConstantBuffer(uint32 slot, shared_ptr<ConstantBuffer<T>> buffer)
	{
		_deviceContext->VSSetConstantBuffers(slot, 1, buffer->GetComPtr().GetAddressOf());
	}

보면 VS영역에 Set을 해주는 거라 VS에서만 사용하는 건가 싶지만,

		_deviceContext->PSSetConstantBuffers(slot, 1, buffer->GetComPtr().GetAddressOf());

PS 버전도 있다. 그 말은 상수 버퍼는 꼭 VS단에서만 사용하는 게 아니라는 얘기가 된다.

Default.hlsl을 만들 때 VS에서만 사용했었지만 나중에 다른 식으로 만들 때라면 어떤 용도로 사용할지 명시해 주는 것이고, 그걸 scope라는 걸로 받아 줄 것이다.

Shader.h에

enum ShaderScope
{
	SS_None = 0,
	SS_VertexShader = (1 << 0), // 1
	SS_PixelShader = (1 << 1), // 비트 연산 2, 비트플래그 용도로 활용할 거란 힌트를 주는 거 
	SS_Both = SS_VertexShader | SS_PixelShader
};

이렇게 해줬었어. 비트 플래그가 싫다면 그냥 enum 숫자로 해서 if else로 하는 방식으로 만들어 줄 수도 있다.

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

이렇게 만들어 주면 된다.

그다음에 SetTexture, SetSamplerState도 마찬가지다.

// slot은 chiikawa일지 hachiware일지
// 텍스쳐를 받아서 세팅해 주는 부분까지 연동
void SetTexture(uint32 slot, uint32 scope, shared_ptr<Texture> texture);

void SetSamplerState(uint32 slot, uint32 scope, shared_ptr<SamplerState> samplerState);

선언하고, 구현부를 만든다.

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(0, 1, samplerState->GetComPtr().GetAddressOf());

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

이렇게 Pipeline은 완성이 되었고,

공용적인 부분, 그리고 세부적으로 하나씩 채우는 부분까지 만들어 놨다.

 

그리고 마지막에 DrawIndexed라고 그림을 그려주는 부분이 포함되어야 하다.

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

이 둘의 차이는 무엇일까?

DrawIndexed는 indexBuffer까지 적용하는 경우 사용한다.

일반적인 정점만 이용할 때는 draw 버전을 사용했다. Game::Render에서 주석 처리 했던 부분을 복원해서 사용해서 구현해 준다.

void Pipeline::Draw(uint32 vertexCount, uint32 startVertexLocation)
{		
	_deviceContext->Draw(vertexCount, startVertexLocation);
}

void Pipeline::DrawIndexed(uint32 indexCount, uint32 startIndexLocation, uint32 baseVertexLocation)
{
	_deviceContext->DrawIndexed(indexCount, startIndexLocation, baseVertexLocation);
}

여기까지 적용하면 끝난 거다.

 

빌드를 하면 성공한다.

전체적인 파이프라인 과정을 파이프라인과 클래스로 묶어준 것이다.

결국 한번 더 원하는 방식으로 묶어 줄 수 있게 되었다.

 

5. Pipeline 클래스 적용

파이프라인이라는 게 결국 렌더링 파이프라인을 묘사하는 것이니까,

pch.h에 가서

#include "Pipeline.h"

을 추가하고,

Game.h에 가서,

shared_ptr<Pipeline> _pipeline;

을 추가한다.

Game::Init에 가서

_pipeline = make_shared<Pipeline>(_graphics->GetDeviceContext());

을 추가한다.

 

Pipeline으로 Game::Render에서 하나씩 묶어줬던 것을 공용적인 건 하나로 묶어서 할 수 있게 되었다.

Game::Render에 가서 PipelineInfo를 만들어 준다. 한 번 더 묶을 것이기 때문에 아직 끝난 게 아니다.

void Game::Render()
{
	_graphics->RenderBegin();

	// IA - VS - RS - PS - OM
	{
		PipelineInfo info; 
		info.inputLayout = _inputLayout; 
		info.vertexShader = _vertexShader; 
		info.pixelShader = _pixelShader; 
		info.rasterizerState = _rasterizerState; 
		info.blendState = _blendState; 

		_pipeline->UpdatePipeline(info);

이렇게 하면 굉장히 많은 부분이 한 번에 챙겨지게 된다.

핵심적인 부분들은 일단 다 챙겨진다고 볼 수 있다.

챙겨진 부분들은 Game::Render에서 삭제해 준다.

나머지 부분도 한번 더 묶어서 오브젝트로 관리할 수 있게 된다.

void Game::Render()
{
	_graphics->RenderBegin();

	// IA - VS - RS - PS - OM
	{
		PipelineInfo info; 
		info.inputLayout = _inputLayout; 
		info.vertexShader = _vertexShader; 
		info.pixelShader = _pixelShader; 
		info.rasterizerState = _rasterizerState; 
		info.blendState = _blendState; 
		_pipeline->UpdatePipeline(info); 

		auto _deviceContext = _graphics->GetDeviceContext(); 
		_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);
	}

	_graphics->RenderEnd();
}

일단 여기까지 정리가 된 것이다.

 

이제 직은 하나하나 부분들은 인위적으로 세팅하는 부분이 들어간다고 볼 수 있다.

void Game::Render()
{
	_graphics->RenderBegin();

	// IA - VS - RS - PS - OM
	{
		PipelineInfo info; 
		info.inputLayout = _inputLayout; 
		info.vertexShader = _vertexShader; 
		info.pixelShader = _pixelShader; 
		info.rasterizerState = _rasterizerState; 
		info.blendState = _blendState; 
		_pipeline->UpdatePipeline(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); 
	}

	_graphics->RenderEnd();
}

일단 1차적으로 이렇게 정리가 되었다.

조금 더 쉽게 사용할 수 있게 되었다.

빌드하고, 실행하면 예전과 같이 잘 작동한다.

세세한 함수들에 대한 걸 기억할 필요 없이 똑같이 만들어줘서 pipeline이란 걸 묶어서 렌더링 파이프라인을 묘사할 수 있게 되었다는 특징이 생기게 된 거다.

반응형

'DirectX' 카테고리의 다른 글

24. 엔진구조_Component  (0) 2024.01.17
22. 프레임워크 제작_GameObject  (0) 2024.01.05
20. 프레임워크 제작_Shader  (0) 2024.01.03
19. 프레임워크 제작_Geometry  (0) 2024.01.02
18. 프레임워크 제작_InputAssembler  (0) 2024.01.01

댓글