ftftgop3 2023. 12. 6. 22:14

스텐실 버퍼는 특정 픽셀 단편이 후면 버퍼에 렌더링 되지 않게 하는 용도,

깊이 버퍼와 같은 저장 공간을 공유 하면 같은 해상도를 가지고 있다

 

특정 픽셀이 후면 버퍼에 기록 되는지 아닌지는 스텐실 판정으로 결정

∇ : 비교 연산자로 사용

if( StencilRef & StencilReadMask ∇ Value & StencilReadMask)

    픽셀을 허용(후면 버퍼, 깊이 버퍼에 기록)

else

    픽셀을 기각(후면 버퍼, 깊이 버퍼에 기록하지 않는다)

 

깊이 스텐실 상태는 PSO 서술의 일부로 들어가면 깊이 스텐실 버퍼 상태의 필드를 채워서 설정

위에 StencilRef 설정하는 OMsetstencilRef 메서드로 변경 해서 올바른 스텐실 판정 하게 한다

 

연습 문제

1. 범용 그림자 행렬 S, $L_w = 0$ 일때 $S_{dir}$ 같아지고 $L_w = 1$ 일때 $S_{point}$ 증명

$L_w = 0, {-L_wn_x -L_wn_y -L_wn_z } = -L \cdot n $ 

$L_w =1 $ 경우 

2. 

3. 그림 11.1 해골이 거울 밖에서도 렌더링 되는 현상

거시적으로 보이는 거울의 스텐실 버퍼의 값을 1로 변경 해야 하지만 스텐실 값을 0으로 변경 후

제대로 스텐실 판정을 할 수 없게 해서 해골이 거울 밖에서도 렌더링 된다.

 

픽셀 단편이 스텐실 판정과 깊이 판정 모두 통과 하면 지정된 스텐실 값을 넣는 POS 상태

현재 스텐실 값이 0으로, 거울 픽셀의 스텐실 값은 0으로 변경

	// Mark the visible mirror pixels in the stencil buffer with the value 1
    //mCommandList->OMSetStencilRef(1);
	mCommandList->OMSetStencilRef(0);
	mCommandList->SetPipelineState(mPSOs["markStencilMirrors"].Get());
	DrawRenderItems(mCommandList.Get(), mRitemLayer[(int)RenderLayer::Mirrors]);

 

연습 문제 3

4. 이중 혼합이 발생하게 코드 수정

그림자 렌더링 시 스텐실 버퍼 사용 안하게 주석 처리

연습 문제 4

5. 스텐실 버퍼 비 활성화 후 아래와 같이 깊이 설정 변경 후 렌더링 시 결과를 설명 하라

  1. 벽을 depth enable = false  후 렌더링 후 벽  렌더링
  2. 벽 뒤의 해골을 그릴때는 depth enable = true 후 렌더링
  3. 벽 뒤에 해골 있어도 깊이 값을 비활성화 후 렌더링 했기 때문에 깊이 값 영향을 받지 않는다

void StencilApp::BuildPSOs()
{
	D3D12_DEPTH_STENCIL_DESC depthStencilDesc;
	depthStencilDesc.DepthEnable = false;
	depthStencilDesc.DepthWriteMask = D3D12_DEPTH_WRITE_MASK_ALL;
	depthStencilDesc.DepthFunc = D3D12_COMPARISON_FUNC_LESS;

	depthStencilDesc.FrontFace.StencilFailOp = D3D12_STENCIL_OP_KEEP;
	depthStencilDesc.FrontFace.StencilDepthFailOp = D3D12_STENCIL_OP_KEEP;
	depthStencilDesc.FrontFace.StencilPassOp = D3D12_STENCIL_OP_KEEP;
	depthStencilDesc.FrontFace.StencilFunc = D3D12_COMPARISON_FUNC_ALWAYS;

	// We are not rendering backfacing polygons, so these settings do not matter.
	depthStencilDesc.BackFace.StencilFailOp = D3D12_STENCIL_OP_KEEP;
	depthStencilDesc.BackFace.StencilDepthFailOp = D3D12_STENCIL_OP_KEEP;
	depthStencilDesc.BackFace.StencilPassOp = D3D12_STENCIL_OP_KEEP;
	depthStencilDesc.BackFace.StencilFunc = D3D12_COMPARISON_FUNC_ALWAYS;

	D3D12_GRAPHICS_PIPELINE_STATE_DESC depthStencilPsoDesc = opaquePsoDesc;
	depthStencilPsoDesc.DepthStencilState = depthStencilDesc;
	
	ThrowIfFailed(md3dDevice->CreateGraphicsPipelineState(&depthStencilPsoDesc,
    IID_PPV_ARGS(&mPSOs["depthenable_flase"])));
}

void StencilApp::Draw(const GameTimer& gt)
{
	mCommandList->SetPipelineState(mPSOs["depthenable_flase"].Get());
    DrawRenderItems(mCommandList.Get(), mRitemLayer[(int)RenderLayer::Opaque]);
	
	// Mark the visible mirror pixels in the stencil buffer with the value 1
	mCommandList->OMSetStencilRef(1);
	//mCommandList->SetPipelineState(mPSOs["markStencilMirrors"].Get());
	DrawRenderItems(mCommandList.Get(), mRitemLayer[(int)RenderLayer::Mirrors]);

	// Draw the reflection into the mirror only (only for pixels where the stencil buffer is 1).
	// Note that we must supply a different per-pass constant buffer--one with the lights reflected.

	mCommandList->SetPipelineState(mPSOs["opaque"].Get());
	mCommandList->SetGraphicsRootConstantBufferView(2, passCB->GetGPUVirtualAddress() + 1 * passCBByteSize);
	//mCommandList->SetPipelineState(mPSOs["drawStencilReflections"].Get());
	DrawRenderItems(mCommandList.Get(), mRitemLayer[(int)RenderLayer::Reflected]);
}

6. 거울 반사상을 렌더링시 삼각형 감기 순서를 통산적인 관례의 반대로 설정 하지 않게 수정 

https://study-room.tistory.com/23 참고 

 

7. 혼합 예제에 장면 중앙에 원기둥을 렌더링 후 번개 애니메이션을 가상 혼합 하여 텍스처를 맵핑

 

void BlendApp::LoadTextures()
{
	//...
    //bolt.dds 로드 후 텍스처 리스트에 추가
    auto BoltTex = std::make_unique<Texture>();
	BoltTex->Name = "boltTex";
	BoltTex->Filename = L"../../Textures/Bolt.dds";
	ThrowIfFailed(DirectX::CreateDDSTextureFromFile12(md3dDevice.Get(),
		mCommandList.Get(), BoltTex->Filename.c_str(),
		BoltTex->Resource, BoltTex->UploadHeap));
    mTextures[BoltTex->Name] = std::move(BoltTex);
}

void BlendApp::BuildDescriptorHeaps()
{
	// Create the SRV heap.
	//
	D3D12_DESCRIPTOR_HEAP_DESC srvHeapDesc = {};
	srvHeapDesc.NumDescriptors = 4; // Srv 개수(텍스터 추가 시 값 증가)
	srvHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV;
	srvHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;
	ThrowIfFailed(md3dDevice->CreateDescriptorHeap(&srvHeapDesc, IID_PPV_ARGS(&mSrvDescriptorHeap)));
    //텍스처 데이터 로드
    auto boltTex = mTextures["boltTex"]->Resource;
    
    //...
    //blot tex을 srv heap 지정
	hDescriptor.Offset(1, mCbvSrvDescriptorSize);

	srvDesc.Format = boltTex->GetDesc().Format;
	md3dDevice->CreateShaderResourceView(boltTex.Get(), &srvDesc, hDescriptor);
}

void BlendApp::BuildMaterials()
{
	//bolt 머티리얼 생성 
	auto bolt = std::make_unique<Material>();
	bolt->Name = "bolt";
	bolt->MatCBIndex = 3; // 머티리얼 인덱스
	bolt->DiffuseSrvHeapIndex = 3; //위에 지정한 srv 힙의 인덱스 
	bolt->DiffuseAlbedo = XMFLOAT4(1.0f, 1.0f, 1.0f, 1.0f);
	bolt->FresnelR0 = XMFLOAT3(0.1f, 0.1f, 0.1f);
	bolt->Roughness = 0.25f;
    mMaterials["bolt"] = std::move(bolt);
}

void BlendApp::CreateCylinder()
{
	GeometryGenerator geoGen;
    //CreateCylinder 생성 시 아래 면, 윗면 생성 부분은 주석 처리
	GeometryGenerator::MeshData Cylinder = geoGen.CreateCylinder(8.0f,
		8.0f, 16.0f, 20, 20);
    // 버텍스, 인덱스, uv, noraml 값 지정
    std::vector<Vertex> vertices(Cylinder.Vertices.size());
	for (size_t i = 0; i < Cylinder.Vertices.size(); ++i)
	{
		auto& p = Cylinder.Vertices[i].Position;
		vertices[i].Pos = p;
		vertices[i].Normal = Cylinder.Vertices[i].Normal;
		vertices[i].TexC = Cylinder.Vertices[i].TexC;
	}

	const UINT vbByteSize = (UINT)vertices.size() * sizeof(Vertex);

	std::vector<std::uint16_t> indices = Cylinder.GetIndices16();
	const UINT ibByteSize = (UINT)indices.size() * sizeof(std::uint16_t);

	auto geo = std::make_unique<MeshGeometry>();
	geo->Name = "cylinderGeo";

	ThrowIfFailed(D3DCreateBlob(vbByteSize, &geo->VertexBufferCPU));
	CopyMemory(geo->VertexBufferCPU->GetBufferPointer(), vertices.data(), vbByteSize);

	ThrowIfFailed(D3DCreateBlob(ibByteSize, &geo->IndexBufferCPU));
	CopyMemory(geo->IndexBufferCPU->GetBufferPointer(), indices.data(), ibByteSize);

	geo->VertexBufferGPU = d3dUtil::CreateDefaultBuffer(md3dDevice.Get(),
		mCommandList.Get(), vertices.data(), vbByteSize, geo->VertexBufferUploader);

	geo->IndexBufferGPU = d3dUtil::CreateDefaultBuffer(md3dDevice.Get(),
		mCommandList.Get(), indices.data(), ibByteSize, geo->IndexBufferUploader);

	geo->VertexByteStride = sizeof(Vertex);
	geo->VertexBufferByteSize = vbByteSize;
	geo->IndexFormat = DXGI_FORMAT_R16_UINT;
	geo->IndexBufferByteSize = ibByteSize;

	SubmeshGeometry submesh;
	submesh.IndexCount = (UINT)indices.size();
	submesh.StartIndexLocation = 0;
	submesh.BaseVertexLocation = 0;

	geo->DrawArgs["cylinder"] = submesh;
	
	mGeometries["cylinderGeo"] = std::move(geo);
     
 }
 void BlendApp::BuildRenderItems()
 {
 	//...
    auto CylinderRitem = std::make_unique<RenderItem>();
	XMStoreFloat4x4(&CylinderRitem->World, XMMatrixTranslation(0.0f, 5.0f, -9.0f));
	CylinderRitem->ObjCBIndex = 3;
	CylinderRitem->Mat = mMaterials["bolt"].get();
	CylinderRitem->Geo = mGeometries["cylinderGeo"].get();
	CylinderRitem->PrimitiveType = D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST;
	CylinderRitem->IndexCount = CylinderRitem->Geo->DrawArgs["cylinder"].IndexCount;
	CylinderRitem->StartIndexLocation = CylinderRitem->Geo->DrawArgs["cylinder"].StartIndexLocation;
	CylinderRitem->BaseVertexLocation = CylinderRitem->Geo->DrawArgs["cylinder"].BaseVertexLocation;
	//알파 테스트 레이아웃에 추가, 알파 테스트는 컬링은 하지 않는 PSO 상태를 뜻한다
	mRitemLayer[(int)RenderLayer::AlphaTested].push_back(CylinderRitem.get());
    mAllRitems.push_back(std::move(CylinderRitem));
 }
 void BlendApp::Draw(const GameTimer& gt)
 {
 	//..불 투명 오브젝트 렌더링 후 bolt 렌더링
 	mCommandList->SetPipelineState(mPSOs["alphaTested"].Get());
	DrawRenderItems(mCommandList.Get(), mRitemLayer[(int)RenderLayer::AlphaTested]);
 }

참고 및 내용 인용
프랭크 D 루나 지음, 류광 옮김 
한빛 미디어
Directx 12를 이용한 3D 게임 프로그래밍 입문(2017)