Mirror and Reflection (DepthStencil Test)
카테고리: Graphics
태그: DirectX
홍정모님의 그래픽스 새싹코스 강의를 듣고 정리한 내용입니다.
🐥 Mirror and Reflection
거울에 비치는 물체를 표현하기 위한 방법 중 가장 먼저 떠오르는 것은 Ray Tracing을 이용한 방법일 것이다. 하지만 Rasterization을 이용하는 전통적인 렌더링 파이프라인에서는 ray tracing을 사용하지 않는다. 월드에 존재하는 오브젝트들을 통쨰로 거울 뒤의 세상으로 반사시킨 뒤, Stencil Test를 이용해 거울 속 세상을 별도로 렌더링 한다.
Stencil Buffer
Stencil Buffer의 대표적인 활용법 중 하나는 Rasterize 이후 0/1 값으로 렌더링 할 부분을 표기해주는 버퍼이다. 거울 평면의 경우 거울 속 전체 월드를 그리는 것은 아주 큰 낭비이다. 이럴 경우 Stencil Buffer를 이용해여 거울의 크기만큼만 렌더링을 해주면 된다.
🐥 DepthStencil State
Stencil Buffer를 이용한 거울을 구현할 경우 여러가지의 DepthStencil State가 필요하다.
- 기존 World를 렌더링하기 위한 DSS (Default State)
- Stencil Buffer에 거울 영역을 표기해주기 위한 DSS
- Stencil Test를 통과한 오브젝트들 렌더링하기 위한 DSS
Default State
// DepthStencilView 생성
D3D11_TEXTURE2D_DESC desc;
// ...
desc.BindFlags = D3D11_BIND_DEPTH_STENCIL;
desc.Format = DXGI_FORMAT_D24_UNORM_S8_UINT;
ThrowIfFailed(m_device->CreateTexture2D(
&desc, 0, m_depthStencilBuffer.GetAddressOf()));
ThrowIfFailed(m_device->CreateDepthStencilView(
m_depthStencilBuffer.Get(), NULL, m_depthStencilView.GetAddressOf()));
- BindFlags : D3D11_BIND_DEPTH_STENCIL
- Format : DXGI_FORMAT_D24_UNORM_S8_UINT
- 보통 버퍼들은 픽셀 당 32bit 데이터 들고 있음
- 메모리를 효율적으로 사용하기 위해 (depth 24bit + stencil 8bit)를 함께 들고다님
// default DSS
D3D11_DEPTH_STENCIL_DESC dsDesc;
ZeroMemory(&dsDesc, sizeof(dsDesc));
dsDesc.DepthEnable = true;
dsDesc.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ALL; // 쓰기 킬지 말지
dsDesc.DepthFunc = D3D11_COMPARISON_LESS;
dsDesc.StencilEnable = false; // Stencil 불필요
dsDesc.StencilReadMask = D3D11_DEFAULT_STENCIL_READ_MASK;
dsDesc.StencilWriteMask = D3D11_DEFAULT_STENCIL_WRITE_MASK;
- DepthWriteMask :
- D3D11_DEPTH_WRITE_MASK_ZERO : DepthStencil Buffer 쓰기 끄기
- D3D11_DEPTH_WRITE_MASK_ALL : DepthStencil Buffer 쓰기 켜기
- DepthFunc : D3D11_COMPARISON_LESS (작을 경우 통과)
- StencilReadMask : D3D11_DEFAULT_STENCIL_READ_MASK;
- StencilWriteMask : D3D11_DEFAULT_STENCIL_WRITE_MASK;
// 앞면에 대해서 어떻게 작동할지 설정
dsDesc.FrontFace.StencilFailOp = D3D11_STENCIL_OP_KEEP;
dsDesc.FrontFace.StencilDepthFailOp = D3D11_STENCIL_OP_KEEP;
dsDesc.FrontFace.StencilPassOp = D3D11_STENCIL_OP_KEEP;
dsDesc.FrontFace.StencilFunc = D3D11_COMPARISON_ALWAYS;
// 뒷면에 대해 어떻게 작동할지 설정 (뒷면도 그릴 경우)
dsDesc.BackFace.StencilFailOp = D3D11_STENCIL_OP_KEEP;
dsDesc.BackFace.StencilDepthFailOp = D3D11_STENCIL_OP_KEEP;
dsDesc.BackFace.StencilPassOp = D3D11_STENCIL_OP_REPLACE;
dsDesc.BackFace.StencilFunc = D3D11_COMPARISON_ALWAYS;
ThrowIfFailed(
m_device->CreateDepthStencilState(&dsDesc, m_drawDSS.GetAddressOf()));
- StencilPassOp : depth, stencil 둘 다 pass일 경우
- StencilDepthFailOp : depth fail, stencil pass일 경우
- StencilFailOp : depth,stencil 둘 다 fail일 경우
Stencil Test 통과한 픽셀만 처리
dsDesc.DepthEnable = true;
dsDesc.StencilEnable = true; // Stencil 사용
dsDesc.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ALL;
dsDesc.DepthFunc = D3D11_COMPARISON_LESS_EQUAL;
dsDesc.FrontFace.StencilFailOp = D3D11_STENCIL_OP_KEEP;
dsDesc.FrontFace.StencilDepthFailOp = D3D11_STENCIL_OP_KEEP;
dsDesc.FrontFace.StencilPassOp = D3D11_STENCIL_OP_KEEP;
dsDesc.FrontFace.StencilFunc = D3D11_COMPARISON_EQUAL;
ThrowIfFailed(m_device->CreateDepthStencilState(
&dsDesc, m_drawMaskedDSS.GetAddressOf()));
- StencilFunc = D3D11_COMPARISON_EQUAL;
- OMSetDepthStecilState(.., 1) 에서 설정한 값 1과 같을 경우에만 stencil test 통과하도록 설정
- 참고로 거울 속 세상을 그릴 때에는 Rasterizer State의 Clockwise를 바꿔줘야 함(반사시키면서 ccw로 변형)
Blending
D3D11_BLEND_DESC mirrorBlendDesc;
// ...
mirrorBlendDesc.RenderTarget[0].SrcBlend = D3D11_BLEND_BLEND_FACTOR;
mirrorBlendDesc.RenderTarget[0].DestBlend = D3D11_BLEND_INV_BLEND_FACTOR;
mirrorBlendDesc.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD;
ThrowIfFailed(m_device->CreateBlendState(&mirrorBlendDesc,
m_mirrorBS.GetAddressOf()));
- OMSetBlendState(…, blendColor, …)
- 이때 넘어온 color값들이 Blend Factor
- (Source color * blend factor) + (Destination color * (1 - blend factor))
- source는 거울 자체의 color
- destination은 반사된 물체의 color
🐥 Results
댓글 남기기