[DirectX] Rasterize State
in Study on WinAPI&DirectX
3D 렌더링을 할 때에는 렌더링의 최적화를 위한 Cull Mode가 있다.
여기서 말하는 Culling이란 그리지 말아야 하는 것을 설정하는 것이다.
그렇다면 어떤 기준으로 그리지 말아야 할까?
앞면만 그리는 기본 culling
레스터라이저에서 표면의 방향벡터가 어딘지를 판단하는데 외적이 쓰인다.
여기서 Index Buffer가 삼각형을 그릴 정점을 받는 순서가 시계방향이 될 수도있고, 반시계 방향이 될 수도 있다.
삼각형을 그리는 벡터가 시계방향으로 향하고 있을 때 그 벡터들을 외적해보면 카메라가 바라보는 방향과 마주하는 방향, 즉 카메라는 삼각형의 전면을 바라보는 것으로 보이고,
삼각형을 그리는 벡터가 반시계방향으로 향하고 있을 때 그 벡터들을 외적해보면 카메라가 바라보는 방향과 같은 방향, 즉 카메라는 삼각형의 후면을 바라보는 것으로 보인다.
이 때 카메라가 바라보는 방향과 같은 방향일 때 앞면, 그 반대 방향일 때 뒷면으로 본다.
그래서 기본적인 culling 과정에서 앞면인 것만 그려지고, 뒷면인 것은 그리는 과정에서 제외된다.
RasterizerState 형성
앞으로 Rasterizer State를 만들어서 사용할 것이다.
각기 알맞은 상황에 따라서 Rasterizing을 하기 위해서이다.
define 헤더의 변화
// 코드 추가
enum class RS_TYPE
{
CULL_BACK,
CULL_FRONT,
CULL_NONE,
WIRE_FRAME,
END
}
레스터 라이징 모드를 지정하는 코드이다.
CULL_FRONT 모드를 사용할 일이 있을까?
있다. 우리가 거대한 우주 안에서 살고 있듯이, 게임의 배경은 결국 거대한 구이다. 우리는 거대한 구 안에서 활동을 하는 게임을 만들 것이다.
이때, 구 안에서 구를 바라보면 항상 구의 뒷면 혹은 안쪽면을 바라보게 되는 것이므로 이 때에는 오히려 CULL_FRONT 옵션을 사용할 것이다.
CDevice 클래스의 변화
// 헤더파일의 코드 추가
// RasterizerState
ComPtr<ID3D11RasterizerState> m_RSState[(UINT)RS_TYPE::END];
private:
int CreateRasterizerState();
// cpp파일의 코드 추가
int CDevice::CreateRasterizerState()
{
m_RSState[(UINT)RS_TYPE::CULL_BACK] = nullptr;
D3D11_RASTERIZER_DESC Desc = {};
Desc.CullMode = D3D11_CULL_MODE::D3D11_CULL_FRONT;
Desc.FillMode = D3D11_FILL_MODE::D3D11_FILL_SOLID;
DEVICE->CreateRasterizerState(&Desc, m_RSState[(UINT)RS_TYPE::CULL_FRONT].GetAddressOf());
Desc.CullMode = D3D11_CULL_MODE::D3D11_CULL_NONE;
Desc.FillMode = D3D11_FILL_MODE::D3D11_FILL_SOLID;
DEVICE->CreateRasterizerState(&Desc, m_RSState[(UINT)RS_TYPE::CULL_NONE].GetAddressOf());
Desc.CullMode = D3D11_CULL_MODE::D3D11_CULL_NONE;
Desc.FillMode = D3D11_FILL_MODE::D3D11_FILL_WIREFRAME;
DEVICE->CreateRasterizerState(&Desc, m_RSState[(UINT)RS_TYPE::WIRE_FRAME].GetAddressOf());
return S_OK;
}
ComPtr을 이용해서 레스터라이저 모드를 지정한다.
그리고 CreateRasterizerState() 함수를 통해서 레스터라이저 모드를 만드는 과정을 넣어준다.
이때 D3D11_RASTERIZER_DESC 구조체에서 CullMode, FillMode를 건드린다.
- CullMode : 어떤 CullMode를 사용할 것인가?
- FillMode : 메쉬를 어떻게 채울 것인가?
State를 지정할 때 nullptr로 지정하면 기본 값을 사용하겠다는 의미!
CGraphicsShader 클래스의 변화
// 헤더파일의 코드 추가
RS_TYPE m_RSType;
public:
void SetRSType(RS_TYPE _Type) { m_RSType = _Type; }
// cpp파일
void CGraphicsShader::UpdateData()
{
CONTEXT->IASetInputLayout(m_Layout.Get());
CONTEXT->IASetPrimitiveTopology(m_eTopology);
CONTEXT->VSSetShader(m_VS.Get(), nullptr, 0);
//CONTEXT->HSSetShader(m_HS.Get(), nullptr, 0);
//CONTEXT->DSSetShader(m_DS.Get(), nullptr, 0);
//CONTEXT->GSSetShader(m_GS.Get(), nullptr, 0);
CONTEXT->PSSetShader(m_PS.Get(), nullptr, 0);
CONTEXT->RSSetState(CDevice::GetInst()->GetRSState(m_RSType).Get());
}
Rasterizer State를 사용할 곳은 그래픽 쉐이더이다.
Device에서 생성한 State를 쉐이더에서 사용할 수 있도록 코드를 추가한다.
CResMgr 클래스의 변화
// cpp 파일
void CResMgr::CreateDefaultGraphicsShader()
{
Ptr<CGraphicsShader> pShader = nullptr;
// ===========
// Test Shader
// ===========
pShader = new CGraphicsShader;
pShader->SetKey(L"TestShader");
pShader->CreateVertexShader(L"shader\\test.fx", "VS_Test");
pShader->CreatePixelShader(L"shader\\test.fx", "PS_Test");
pShader->SetRSType(RS_TYPE::CULL_NONE);
AddRes(L"TestShader", pShader);
}
여기서 CreateDefaultGraphicShader 함수에서 쉐이더마다의 Cull Mode를 지정한다.