12
28

한 함수로 3D박스 그리기

더보기
bool DrawTestBox(float angle, float x, float y, float z)
	{
		namespace wrl = Microsoft::WRL;
		HRESULT hr = S_OK;
		struct Vertex
		{
			struct {
				float x;
				float y;
				float z;
			}pos;
		};
		const Vertex vertices[] =
		{
			{-1.0f,-1.0f,-1.0f},
			{ 1.0f,-1.0f,-1.0f},
			{-1.0f,1.0f,-1.0f},
			{ 1.0f,1.0f,-1.0f},
			{-1.0f,-1.0f,1.0f},
			{ 1.0f,-1.0f,1.0f},
			{-1.0f,1.0f,1.0f},
			{ 1.0f,1.0f,1.0f},
		};
		wrl::ComPtr<ID3D11Buffer> pVertexBuffer;
		D3D11_BUFFER_DESC bd;
		bd.BindFlags = D3D11_BIND_VERTEX_BUFFER;
		bd.Usage = D3D11_USAGE_DEFAULT;
		bd.CPUAccessFlags = 0u;
		bd.MiscFlags = 0u;
		bd.ByteWidth = sizeof(vertices);
		bd.StructureByteStride = sizeof(Vertex);

		D3D11_SUBRESOURCE_DATA sd;
		ZeroMemory(&sd,sizeof(D3D11_SUBRESOURCE_DATA));
		sd.pSysMem = vertices;
		hr=g_pd3dDevice->CreateBuffer(&bd, &sd, pVertexBuffer.GetAddressOf());
		if (FAILED(hr)) return false;
		const UINT stride = sizeof(Vertex);
		const UINT offset = 0u;
		//배열이 들어갈수있다.
		m_pImmediateContext->IASetVertexBuffers(0u, 1u, pVertexBuffer.GetAddressOf(), &stride, &offset);

		const unsigned short indices[] =
		{
			0,2,1, 2,3,1,
			1,3,5, 3,7,5,
			2,6,3, 3,6,7,
			4,5,7, 4,7,6,
			0,4,2, 2,4,6,
			0,1,4, 1,5,4,
		};
		wrl::ComPtr<ID3D11Buffer>pIndexBuffer;
		D3D11_BUFFER_DESC ibd = {};
		ibd.BindFlags = D3D11_BIND_INDEX_BUFFER;
		ibd.Usage = D3D11_USAGE_DEFAULT;
		ibd.CPUAccessFlags = 0u;
		ibd.MiscFlags = 0u;
		ibd.ByteWidth = sizeof(indices);
		ibd.StructureByteStride = sizeof(unsigned short);
		D3D11_SUBRESOURCE_DATA isd;
		isd.pSysMem = indices;
		hr = g_pd3dDevice->CreateBuffer(&ibd, &isd, pIndexBuffer.GetAddressOf());
		if (FAILED(hr)) return false;

		m_pImmediateContext->IASetIndexBuffer(pIndexBuffer.Get(),DXGI_FORMAT_R16_UINT,0);  

		//상수버퍼
		struct ConstantBuffer
		{
			dx::XMMATRIX transform;
		};
		const ConstantBuffer cb =
		{
			{
				dx::XMMatrixTranspose(
					dx::XMMatrixRotationZ(angle) *
					dx::XMMatrixRotationY(angle) *
					dx::XMMatrixTranslation(x,y,z+2.0f) *
					dx::XMMatrixPerspectiveLH(1.0f,3.0f / 4.0f,0.1f,1000.0f)
				)
			}
		};
		wrl::ComPtr<ID3D11Buffer>pConstantBuffer;
		D3D11_BUFFER_DESC cbd = {};
		cbd.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
		cbd.Usage = D3D11_USAGE_DYNAMIC;
		cbd.CPUAccessFlags = D3D10_CPU_ACCESS_WRITE;
		cbd.MiscFlags = 0u;
		cbd.ByteWidth = sizeof(cb);
		cbd.StructureByteStride = 0;
		D3D11_SUBRESOURCE_DATA csd;
		csd.pSysMem = &cb;
		hr = g_pd3dDevice->CreateBuffer(&cbd, &csd, pConstantBuffer.GetAddressOf());
		if (FAILED(hr)) return false;
		m_pImmediateContext->VSSetConstantBuffers(0,1,pConstantBuffer.GetAddressOf());

		struct ConstantBuffer2
		{
			struct
			{
				float r;
				float g;
				float b;
				float a;
			}face_colors[6];
		};
		const ConstantBuffer2 cb2 =
		{
			{
				{0.5f,1.0f,0.0f},
				{0.0f,0.5f,1.0f},
				{1.0f,0.0f,0.5f},
				{0.5f,0.5f,1.0f},
				{0.5f,1.0f,0.5f},
				{1.0f,0.5f,0.5f},
			}
		};
		wrl::ComPtr<ID3D11Buffer>pConstantBuffer2;
		D3D11_BUFFER_DESC cbd2 = {};
		cbd2.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
		cbd2.Usage = D3D11_USAGE_DYNAMIC;
		cbd2.CPUAccessFlags = D3D10_CPU_ACCESS_WRITE;
		cbd2.MiscFlags = 0u;
		cbd2.ByteWidth = sizeof(cb2);
		cbd2.StructureByteStride = 0;
		D3D11_SUBRESOURCE_DATA csd2;
		csd2.pSysMem = &cb2;
		hr = g_pd3dDevice->CreateBuffer(&cbd2, &csd2, pConstantBuffer2.GetAddressOf());
		if (FAILED(hr)) return false;
		m_pImmediateContext->PSSetConstantBuffers(0, 1, pConstantBuffer2.GetAddressOf());

		wrl::ComPtr<ID3DBlob> pBlob;

		//1. 픽셀 셰이더 생성
		wrl::ComPtr<ID3D11PixelShader> pPixelShader;
		hr=D3DReadFileToBlob(L"PixelShader.cso", pBlob.GetAddressOf());
		if (FAILED(hr)) return false;
		hr = g_pd3dDevice->CreatePixelShader(pBlob->GetBufferPointer(),
			pBlob->GetBufferSize(), nullptr, pPixelShader.GetAddressOf());
		if (FAILED(hr)) return false;

		//2. 바인드 픽셀 셰이더
		m_pImmediateContext->PSSetShader(pPixelShader.Get(), nullptr, 0u);

		//3. 버텍스 셰이더 생성
		wrl::ComPtr<ID3D11VertexShader> pVertexShader;
		hr=D3DReadFileToBlob(L"VertexShader.cso", pBlob.GetAddressOf());
		if (FAILED(hr)) return false;
		hr = g_pd3dDevice->CreateVertexShader(pBlob->GetBufferPointer(),
			pBlob->GetBufferSize(), nullptr, pVertexShader.GetAddressOf());
		if (FAILED(hr)) return false;

		//4. 바인드 버텍스 셰이더
		m_pImmediateContext->VSSetShader(pVertexShader.Get(), nullptr, 0u);

		//5. Input 버텍스 레이아웃 (2D 위치만)
		wrl::ComPtr<ID3D11InputLayout> pInputLayout;

		const D3D11_INPUT_ELEMENT_DESC layout[] =
		{
			{"POSITION",0,DXGI_FORMAT_R32G32B32_FLOAT,0,0,D3D11_INPUT_PER_VERTEX_DATA, 0},
		};
		hr = g_pd3dDevice->CreateInputLayout(layout, _countof(layout),
			pBlob->GetBufferPointer(),
			pBlob->GetBufferSize(),
			pInputLayout.GetAddressOf()
		);
		if (FAILED(hr)) return false;

		//6. 바인드 버텍스 레이아웃
		m_pImmediateContext->IASetInputLayout(pInputLayout.Get());

		m_pImmediateContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);

		m_pImmediateContext->DrawIndexed((UINT)(std::size(indices)),0u,0u);
		
		return true;
	};

 

 

위 함수는 이렉트의 파이프라인을 타고
"박스"를 그리는 함수이다.

렌더링 함수에서 4개의 박스를 그리도록 해보자
타이머 뒤에 인자는 x y z로 (왼손좌표계)
y축으로 올라갈수록 z축으로 더 멀어지고 있는 모습이다.

가장 위에 있는 박스가 가장 멀리 있다고 결과를 유추해 볼 수 있다.

 

DepthStencilView 적용전

깊이 버퍼 적용전


가장 위에 있는 박스가 가장 앞에 있는 모습이다.
이렇게 되는 이유는 함수를 실행한 순서대로 그려지고 있기 때문이다.

도화지에 함수를 통해 그림을 그린다고 생각하면 된다. 아크릴 물감처럼!
정점들의 깊이를 저장해서 최종 Draw가 되는 것을 결정하는 텍스쳐가 필요하다.

https://docs.microsoft.com/ko-kr/windows/uwp/graphics-concepts/depth-and-stencil-buffers

 

깊이 및 스텐실 버퍼 - UWP applications

깊이 버퍼는 보기에서 숨기는 대신 렌더링하는 다각형의 영역을 제어하기 위한 깊이 정보를 저장합니다.

docs.microsoft.com

 

Depth & Stencil

depth와 stencil 둘다 마스킹하는 역활을 한다.
미묘한 차이가 있다.

Depth는 깊이 버퍼로 깊이 정보를 담는다. 가릴수는 있어도 렌더를 안하게 할 수는 없다.
Stencil은 픽셀을 그릴지 말지, 이미지의 픽셀을 특수효과로 처리하는데에 쓰인다.

그래픽스 파이프라인에서 OM단계에 속한다. 제일 마지막 단계이다.

Init()
1. 텍스쳐 생성 : 깊이, 스텐실 값을 저장하는 버퍼용
2. 뎁스 스텐실 뷰 생성 : 텍스쳐를 담아 뎁스 스텐실 뷰를 만든다.
3. 뎁스 스텐실 스테이트 생성 : 속성 설정으로 가려지면 렌더 안할지, 가려질지 등 구조체 설정 (DepthFunc의 Comparison)

PreRender()
4. 뎁스스텐실 뷰 클리어 : 렌더타겟뷰를 클리어 하듯이 뎁스스텐실도 매번 클리어해줘야함 (스왑체인)
5. OMSetRenderTarget에 적용 : OMSetRenderTargets() 함수에는 스텐실뷰를 넣는 인자가 있다.
6. 뎁스스텐실 스테이트 적용 : 만들었던 상태 구조체를 pContext에 적용

 

텍스쳐 생성, DepthStencilView 생성

더보기
HRESULT KDevice::SetDepthStencilView()
{
	// 1)텍스처 생성 : 깊이,스텐실 값을 저장하는 버퍼용
	HRESULT hr = S_OK;
	DXGI_SWAP_CHAIN_DESC SDesc;
	m_pSwapChain->GetDesc(&SDesc);
	ID3D11Texture2D* pDSTexture = nullptr;
	D3D11_TEXTURE2D_DESC DescDepth;
	DescDepth.Width = SDesc.BufferDesc.Width;
	DescDepth.Height = SDesc.BufferDesc.Height;
	DescDepth.MipLevels = 1;
	DescDepth.ArraySize = 1;
	//RGB는 텍스쳐 리소스 D24는 뎁스
	DescDepth.Format = DXGI_FORMAT_D24_UNORM_S8_UINT;
	DescDepth.SampleDesc.Count = 1;
	DescDepth.SampleDesc.Quality = 0;
	DescDepth.Usage = D3D11_USAGE_DEFAULT;
	DescDepth.BindFlags = D3D11_BIND_DEPTH_STENCIL;
	DescDepth.CPUAccessFlags = 0;
	DescDepth.MiscFlags = 0;
	hr = g_pd3dDevice->CreateTexture2D(&DescDepth, nullptr, &pDSTexture);
	if (FAILED(hr))
	{
		return hr;
	}
	if (pDSTexture == nullptr)
	{
		return E_FAIL;
	}
	//뎁스 스텐실 뷰 만듬
	D3D11_DEPTH_STENCIL_VIEW_DESC descDSV;
	ZeroMemory(&descDSV, sizeof(D3D11_DEPTH_STENCIL_VIEW_DESC));
	descDSV.Format = DXGI_FORMAT_D24_UNORM_S8_UINT;
	descDSV.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2D;
	descDSV.Texture2D.MipSlice = 0;
	hr = g_pd3dDevice->CreateDepthStencilView(pDSTexture, &descDSV,
		&m_DepthStencilView);
	if (FAILED(hr))
	{
		return hr;
	}
	return hr;
}


텍스쳐를 만들고 BindFlags를 뎁스스텐실용으로 한다.
Format도 보통 RGB인데 D인 이유는 뎁스용이기 때문이다.
SampleDesc.Count = 1;
SampleDesc.Quality = 0;
이 위에 있는 코드는 안티엘리어싱 때 필요한 코드로

swapchain 생성

스왑체인때도 봤던 똑같은 구조체 변수이다.
둘은 같아야한다. 왜냐하면 둘다 같은 뷰이기 때문이다.

 


텍스쳐를 만들었으면 뎁스스텐실뷰를 만들 수 있다.
하지만 스테이트(상태)까지 해야 우리가 원하는 화면을 얻을 수 있다.

 

DepthStencilView State 생성

더보기
HRESULT KState::CreateDepthStenState()
{
    HRESULT hr = S_OK;
    D3D11_DEPTH_STENCIL_DESC dsd;
    ZeroMemory(&dsd, sizeof(D3D11_DEPTH_STENCIL_DESC));
    dsd.DepthEnable = TRUE;
    dsd.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ALL;
    //1.0, 0.6과 0.5를 비교해서 크면 실패
    //제일 앞에 있는 면이 뿌려지면 뒤에있는애들은 렌더 안됨
    dsd.DepthFunc = D3D11_COMPARISON_LESS;
    dsd.StencilEnable = TRUE;
    dsd.StencilReadMask = D3D11_DEFAULT_STENCIL_READ_MASK;
    dsd.StencilWriteMask = D3D11_DEFAULT_STENCIL_WRITE_MASK;
    dsd.FrontFace.StencilFailOp = D3D11_STENCIL_OP_KEEP;
    dsd.FrontFace.StencilDepthFailOp = D3D11_STENCIL_OP_KEEP;
    dsd.FrontFace.StencilPassOp = D3D11_STENCIL_OP_KEEP;
    dsd.FrontFace.StencilFunc = D3D11_COMPARISON_ALWAYS;
    dsd.BackFace.StencilFailOp = D3D11_STENCIL_OP_KEEP;
    dsd.BackFace.StencilDepthFailOp = D3D11_STENCIL_OP_KEEP;
    dsd.BackFace.StencilPassOp = D3D11_STENCIL_OP_KEEP;
    dsd.BackFace.StencilFunc = D3D11_COMPARISON_ALWAYS;
    hr = g_pd3dDevice->CreateDepthStencilState(&dsd, &g_pDSState);
    if (FAILED(hr))
    {
        return hr;
    }
    //깊이 스텐실 제일 마지막 결과 기반으로 렌더하는 것이기에 OM
    return hr;
}


많은 속성이 있지만, 여기서 제일 중요한건
dsd.DepthEnable = TRUE;
dsd.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ALL;
dsd.DepthFunc = D3D11_COMPARISON_LESS;

Comparison은 less로 되어있는데
뎁스값이 0~1인데 해당 객체의 뎁스값이~ 작으면 마스킹된다는 뜻이다.
당연히 같거나 작으면도 있다.

 

ClearDepthStencilView(), OMSetRenderTargets, Apply DepthStencilState

더보기
bool	KCore::PreRender() {
    float ClearColor[4] = { 0.5f, 0.5f, 0.5f, 1.0f }; //red,green,blue,alpha
    m_pImmediateContext->ClearRenderTargetView(m_pRenderTargetView, ClearColor);

    m_pImmediateContext->ClearDepthStencilView(
        m_DepthStencilView,
        D3D11_CLEAR_DEPTH | D3D11_CLEAR_STENCIL, 1.0f, 0);
    m_pImmediateContext->OMSetRenderTargets(1,
        &m_pRenderTargetView, m_DepthStencilView);

    ApplyDSS(m_pImmediateContext, KState::g_pDSState);

    return true;
}

 

매번 렌더링하는 Render 함수 전에 PreRender()에서
이전 화면을 깨끗이 지우는 역활을 하는 RenderTargetView() 있다.
DepthStencilView도 이와 같은 뷰라고 했듯이 지워줘야한다.
ClearDepthStencilView를 하고,
마지막 파이프라인 단계인 OMSetRenderTargets에서 뎁스스텐실뷰 포인터를 넣어준다.
마지막으로 State를 적용해주면 끝이다.

 

결과

 

가장 뒤에 있는 박스가 앞에 있는 박스로 인해 가려진 모습이다.

 

 

참고 : ChiliTomatoNoodle

https://www.youtube.com/watch?v=C0x87s-dTdE&list=PLqCJpWy5Fohd3S7ICFXwUomYW0Wv67pDD&index=22 

 

COMMENT