DirectX 11 3d

230519 Stencil Buffer

슬뷔 2023. 5. 20. 00:39

Stencil Buffer는 Defurred 타겟을 활용하지 않고, Depth Stencil Texture를 활용한다.

 

 

1. 쉬운 버전

2. 난이도 좀 있는 개선버전

 

이젠 깊이도 통과해야하고 스텐실도 통과해야한다 .

 

1. 

깊이텍스쳐는 디바이스에서 만든다

DepthStencilTex에 보면 DX format d24 s8 이렇게 되어있는데, 3바이트는 깊이값, 정수 1바이트(픽셀 포맷)는 스텐실

저장되는 깊이는 버텍스버퍼->레스터라이저->픽셀쉐이더 거쳐서 올 때,

픽셀단위로 sv position해서 넘기는 최종 ndc 좌표에서 깊이값(0 ~ 1 사이)을 기록한다.

이렇게해서 0 ~ 1 사이로 정점이 오고, 레스터라이저에서 정점단위의 깊이값을 보간해서 픽셀로 깊이값이 정해진다.

 다음 물체가 그려질 때, 내가 그린 것보다 가까워야 통과 (깊이 테스트를 less or less equal로 했을 때)

이제 OM 단계에서 DS를 어떻게 설정했냐에 따라서 깊이 판정 방식이 달라진다.

 

그동안은 물체를 기록할 때 항상 그림을 그렸다..

OM set에서 랜더타겟 안 넣고 깊이 텍스쳐만 넣을 수도 있다. => PS 에서 출력을 해야하는데 할 랜더타겟이 없다..

이럴 때는 파이프라인에서 VS, DS(깊이 설정하는 옵션만 설정)만 설정하여 깊이만 저장하게 할 수 있다.

=> Early Z 라고 부름 (물체들의 깊이값을 미리 뽑아 놓는 것)

 

* Early Z (우리는 사용 안 함)

Deferred 이든 Forwar든 깊이를 저장해야하는 모든 물체들이 있는데, early z 를 미리 돌리면 

모든 물체의 깊이가 1차적으로 싹 다 저장이 된다.

지금은 랜더링을 할 때, 동시에 깊이 테스트를 진행한다. 

그래서 미리 그린 그림이 나중에 그린 그림으로 덮을 수 있고, 반대로 미리 그린 그림 뒤에 있어서 못찍히는 애도 있다.

깊이를 먼저 뽑아놓고, 랜더링 할 때는 깊이를 저장하지 않고 바로 2차 랜더링에 들어갈 수 있다.

깊이 테스틑를 하면서 랜더링을 하는게 아니라, 미리 Early Z로 깊이를 뽑아놓고 랜더링을 하는 것이다.

 

이렇게 했을 때 .. 엄청 이득이 있진 않다 (랜더링을 2번 하는 것이기 때문)

이득을 보려면 물체가 엄청 빽빽하게 채워져 있어야한다 (숲, 뉴욕시티의 빌딩 등)

 

* 오클루전 컬링(Occlusion Culling)

오클루전 컬링(Occlusion Culling)은 다른 오브젝트에 가려(오클루전된) 카메라에 보이지 않는 오브젝트의 렌더링을 비활성화하는 기능이다

https://shung2.tistory.com/884

 

오클루전 컬링(Occlusion Culling)이란?

1. 오클루전 컬링(Occlusion Culling)이란? 오클루전 컬링은 컴퓨터 그래픽 및 비디오 게임 개발에서 렌더링해야 하는 오브젝트의 수를 줄여 렌더링 성능을 개선하는 데 사용되는 기술입니다. 기본 아

shung2.tistory.com

 

FrontFace -> 랜더링을 할 때 앞 면이 랜더링될 때

BackFace -> 랜더링을 할 때 뒷 면이 랜더링될 때

 

D3D11_COMPARISON_FUNC::D3D11_COMPARISON_ALWAYS => 스텐실 테스트 조건 중 무조건 통과

D3D11_STENCIL_OP::D3D11_STENCIL_OP_REPLACE => 내가 지정한 값으로 대체

 

GraphicsShader에서 updatedata에 

CONTEXT->OMSetDepthStencilState(CDevice::GetInst()->GetDSState(m_DSType).Get(), 0);

두 번째 자리를 0으로 뒀었다. 

저 자리는 스텐실 참조값으로 그동안은 스텐실을 사용하지 않아 0으로 넣어둔 것이다.

저 자리에 10을 적어뒀다면, 물체가 깊이가 기록될 때 픽셀 하나를 까보면 스텐실 자리에는 10이 들어가 있을 것이다.

 

오로지 물체의 형체를 본따서 스텐실만 남기는게 목적이어서 아래와 같이 짬.

D3D11_DEPTH_STENCIL_DESC Desc = {};
Desc.DepthEnable = true;
// 깊이 테스트 조건
Desc.DepthFunc = D3D11_COMPARISON_LESS;
        
Desc.StencilEnable = true;
Desc.FrontFace.StencilFunc = D3D11_COMPARISON_FUNC::D3D11_COMPARISON_ALWAYS;
Desc.FrontFace.StencilPassOp = D3D11_STENCIL_OP::D3D11_STENCIL_OP_REPLACE;
Desc.FrontFace.StencilPassOp = D3D11_STENCIL_OP::D3D11_STENCIL_OP_ZERO;
Desc.FrontFace.StencilDepthFailOp = D3D11_STENCIL_OP::D3D11_STENCIL_OP_ZERO;

Desc.BackFace.StencilFunc = D3D11_COMPARISON_FUNC::D3D11_COMPARISON_ALWAYS;
Desc.BackFace.StencilPassOp = D3D11_STENCIL_OP::D3D11_STENCIL_OP_ZERO;
Desc.BackFace.StencilPassOp = D3D11_STENCIL_OP::D3D11_STENCIL_OP_ZERO;
Desc.BackFace.StencilDepthFailOp = D3D11_STENCIL_OP::D3D11_STENCIL_OP_ZERO;

//DEVICE->CreateDepthStencilState(&Desc, m_DSState[(UINT)DS_TYPE::FRONT_CHECK].GetAddressOf());

위는 구의 앞면만 그리는 FRONT_CHECK 이다.

 

전체 픽셀을 호출시키려고 렉트매쉬 사용해서 화면 전체영역을 잡아서 호출.

그럼 모든 PS가 호출된다.

그때 새로운 DepthStencil을 하나 더 셋팅한다 (이럴 경우에 사용할..) 

D3D11_COMPARISON_LESS => 볼륨매쉬 앞뚜껑보다 먼저 그려진게 멀어야 구 내부에 있다는 뜻.

볼륨매쉬 앞뚜껑 < 먼저 그려진 땅

볼륨매쉬가 먼저 그려져 있는 땅보다 더 앞에 있어야 통과 => 먼저 그려져 있는 것이 볼륨매쉬보다 더 뒤에 있었다는 의미

초록색 색칠된 곳만 출력된다.

D3D11_DEPTH_STENCIL_DESC Desc = {};
Desc.DepthEnable = true;
// 깊이 테스트 조건
Desc.DepthFunc = D3D11_COMPARISON_GREATER;
        
Desc.StencilEnable = true;
Desc.FrontFace.StencilFunc = D3D11_COMPARISON_FUNC::D3D11_COMPARISON_ALWAYS;
Desc.FrontFace.StencilPassOp = D3D11_STENCIL_OP::D3D11_STENCIL_OP_REPLACE;
Desc.FrontFace.StencilPassOp = D3D11_STENCIL_OP::D3D11_STENCIL_OP_ZERO;
Desc.FrontFace.StencilDepthFailOp = D3D11_STENCIL_OP::D3D11_STENCIL_OP_ZERO;

Desc.BackFace.StencilFunc = D3D11_COMPARISON_FUNC::D3D11_COMPARISON_EQUAL;
Desc.BackFace.StencilDepthFailOp;
Desc.BackFace.StencilPassOp = D3D11_STENCIL_OP::D3D11_STENCIL_OP_KEEP;
Desc.BackFace.StencilPassOp = D3D11_STENCIL_OP::D3D11_STENCIL_OP_ZERO;
Desc.BackFace.StencilDepthFailOp = D3D11_STENCIL_OP::D3D11_STENCIL_OP_ZERO;

//DEVICE->CreateDepthStencilState(&Desc, m_DSState[(UINT)DS_TYPE::BACK_CHECK].GetAddressOf());

위는 구의 뒷면을 그리는 BACK_CHECK 이다.

이제 backoption이 중요하다

D3D11_COMPARISON_GREATER => 구의 뒷통수가 먼저 그려진 그림보다 멀어야 통과.

볼륨매쉬 뒷뚜껑 > 먼저 그려진 땅

노란색 색칠된 곳만 출력된다.

 

D3D11_COMPARISON_FUNC::D3D11_COMPARISON_EQUAL => 내가 지정한 스텐실값인 애만 통과

10으로 적어뒀다면, 10이어야 통과.. 

 

D3D11_STENCIL_OP_KEEP => 실패했을 땐 유지

 

1차테스트 => 앞 면보다 뒤에 있는 것은 다 통과

2차테스트 => 뒷 면보다 앞에 있는 것은 다 통과 (1차테스트 통과한 애들에서)

D3D11_DEPTH_STENCIL_DESC Desc = {};
Desc.DepthEnable = false;

Desc.StencilEnable = true;
// 스텐실 테스트 조건
Desc.FrontFace.StencilFunc = D3D11_COMPARISON_FUNC::D3D11_COMPARISON_EQUAL;
Desc.FrontFace.StencilPassOp = D3D11_STENCIL_OP::D3D11_STENCIL_OP_ZERO;
Desc.FrontFace.StencilPassOp = D3D11_STENCIL_OP::D3D11_STENCIL_OP_ZERO;
// 스텐실은 성공 했는데, 깊이가 실패 했을 때
Desc.FrontFace.StencilDepthFailOp = D3D11_STENCIL_OP::D3D11_STENCIL_OP_ZERO;

// 사용하지 않더라도 Default 값을 채워놔야 한다.
Desc.BackFace.StencilFunc = D3D11_COMPARISON_FUNC::D3D11_COMPARISON_ALWAYS;
Desc.BackFace.StencilPassOp = D3D11_STENCIL_OP::D3D11_STENCIL_OP_ZERO;
Desc.BackFace.StencilPassOp = D3D11_STENCIL_OP::D3D11_STENCIL_OP_ZERO;
Desc.BackFace.StencilDepthFailOp = D3D11_STENCIL_OP::D3D11_STENCIL_OP_ZERO;

//Desc.BackFace.
//DEVICE->CreateDepthStencilState(&Desc, m_DSState[(UINT)DS_TYPE::STENCIL_EQUAL].GetAddressOf());

3차 테스트 => 원하는 스텐실이 들어가 있는거만 출력되게! (위의 코드가 3번째..)

 

 

CMRT 의 clear에서 D3D11_CLEAR_DEPTH | D3D11_CLEAR_STENCIL, 1.f, 0) 을 보면,

Depth는 1로 Stencil은 0으로 초기화를 한다. (default 값)

if (nullptr != m_DSTex)
	{
		CONTEXT->ClearDepthStencilView(m_DSTex->GetDSV().Get(), D3D11_CLEAR_DEPTH | D3D11_CLEAR_STENCIL, 1.f, 0);
	}

 

 

 

뎁스스탠실 조합을 잘 하면 이를 활용해서 내부판정을 할 수 있다.

스탠실 뿐만 아니라 깊이까지 활용해야한다!

 

2. 

한번에 프론트와 백을 작성하고 값을 대체하는게 아니라 옵션을 increase, decrease 를 사용한다.

FrontCheck와 BackCheck를 성공할 때마다 스텐실 값을 increase, decrease를 해준다..

 

성공을 했을 때, 앞면보다 뒤에 있다고 판정이 된다면 increase

뒷면보다 앞에 있다고 판정되어도 increase

=> + + 돼서 2가 되게 한다.

 

실패했을 때는 decrease를 준다. => +-가 돼서 0이 되게 한다.

 

2가 된 것만 내부에 있다고 판정

 

==> 아직 구현은 못함.. 스스로 해야함 수업에는 X