본문 바로가기
DirectX11/정보정리

[DirectX11] 스텐실 적용

by 마두식 2023. 1. 7.
반응형

※  아래의 내용들은 DirectX 11을 이용한 3D 게임 프로그래밍 입문 책의 내용을 바탕으로 작성된 것입니다.

 

10장 스텐실 적용
  • 깊이ㆍ스텐실 버퍼의 자료 형식과 버퍼 지우기(10.1절 421p)

-  깊이ㆍ스텐실 버퍼 또한 하나의 텍스처다.

=>  깊이ㆍ스텐실 버퍼를 생성할 때 특정 자료 형식을 지정할 필요가 있다.

 

  • 스텐실 판정(10.2절 422p)

-  주어진 한 픽셀을 후면 버퍼에 기록할 것인지 아닌지를 스텐실 버퍼에 근거해서 판정하는 것을 "스텐실 판정(stencil test)"라고 부른다.

=>  스텐실 버퍼를 이용하면 후면 버퍼의 특정 영역으로의 렌더링을 방지할 수 있다.


-  스텐실 적용이 활성화된 경우 스텐실 판정은 픽셀이 래스터화되는 과정에서 (즉, 출력 병합기 단계 도중에) 일어난다.

-  스텐실 판정은 두 피연산자를 응용 프로그램이 지정한 비교 연산자로 비교하는 것으로, 두 피연산자는 다음과 같다.

1.  연산자 좌변의 피연산자는 응용 프로그램이 정의한 스텐실 기준 값(stencil reference value)과 응용 프로그램이 정의한 마스킹 값(masking value)을 비트별 논리곱으로 결합한 것
2.  우변의 피연산자는 스텐실 버퍼에 이미 있던 해당 픽셀의 값과 응용 프로그램이 정의한 마스킹 값을 비트별 논리곱으로 결합한 것
=>  좌변과 우변의 마스킹 값이 같음을 주의해야 한다.


-  응용 프로그램은 스텐실 판정의 비교 연산자로 사용할 비교 함수를 제공한다.

=>  함수의 결과가 참이면 후면 버퍼에 기록된다. (깊이 판정도 통과했다고 가정)
=>  함수의 결과가 거짓이면 후면 버퍼에 기록되지 않으며, 깊이 버퍼에도 기록되지 않는다.
=>  비교 연산자로 사용할 수 있는 함수의 종류는 D3D11_COMPARISON_FUNC 열거형으로 정의되어 있다.

 

  • 깊이ㆍ스텐실 상태 집합(10.3절) - 깊이 설정들(10.3.1절 424p)

 

  • 깊이ㆍ스텐실 상태 집합(10.3절) - 스텐실 설정들(10.3.2절 425p)

-  전면 삼각형과 후면 삼각형에 대한 스텐실 적용 방식을 각각 따로 설정할 수 있다.

=>  일반적으로 렌더링 시 후면 선별을 활성화하므로 후면 삼각형은 렌더링되지 않으며, 따라서 BackFace 설정은 무시된다.
==>  특정 그래픽 알고리즘 혹은 투명 기하구조를 그릴 때에는 일부 다각형을 후면 선별 없이 렌더링해야 하는 겨우가 있다. 그 경우 BackFace 설정이 실제로 효과를 가진다

 

  • 깊이ㆍ스텐실 상태 집합(10.3절) - 깊이ㆍ스텐실 상태의 생성 및 결속(10.3.3절 427p)

 

  • 깊이ㆍ스텐실 상태 집합(10.3절) - 효과 파일의 깊이ㆍ스텐실 상태(10.3.4절 428p)

-  깊이ㆍ스텐실 상태를 효과 파일에서 직접 정의하고 설정하는 것도 가능하다.

※  효과 파일의 상태 값들은 대소문자를 구분하지 않는다.

 

  • 평면거울의 구현(10.4절)

-  프로그램에서 거울을 구현하려면 두 가지 문제를 해결해야 한다.

1.  반사상을 제대로 그리기 위해서는 물체를 임의의 평면에 대해 반사시키는 방법을 알아야 한다.
2.  반사상을 거울 안에만 표시해야 한다. (렌더링 시 거울이 있는 평면 중 거울에 해당하는 표면을 어떤 방식으로든 '식별'할 수 있어야 한다.)

=> 스텐실 버퍼를 이용해 해결 가능

 

  • 평면거울의 구현(10.4절) - 거울 예제의 개요(10.4.1절 429p)

-  반사상을 그릴 때에는 광원도 거울 평면에 대해 반사시켜야 한다.

=>  그러지 않으면 반사상에 조명이 제대로 적용되지 않는다.


-  거울을 포함한 장면을 그리는 대략의 단계

1.  바닥과, 벽 등을 보통의 방법으로 렌더링 한다. 거울은 아직 그리지 않는다.
=>  이 단계에서는 스텐실 버퍼를 갱신할 필요가 없다.

 

2.  스텐실 버퍼를 0으로 지운다.

3.  거울을 스텐실 버퍼에만 렌더링한다.
=>  후면 버퍼에 색상 성분들이 기록되지 않게 하려면 깊이ㆍ스텐실 상태의 RenderTargetWriteMask를 0으로 설정하면 된다.
=>  거울을 스텐실 버퍼에 렌더링할 때에는 스텐실 판정이 항상 성공하도록 설정한다.
==>  D3D11_COMPARISON_ALWAYS 설정


=>  판정 성공 시, 스텐실 버퍼 항목이 1(StencilRef)로 대체되도록(D3D11_STENCIL_OP_REPLACE) 설정한다.
==>  깊이 판정이 실패하는 경우의 행동 방식은 D3D11_STENCIL_OP_KEEP으로 설정한다.
===>  이러면 깊이 판정이 실패해도(물체가 거울의 일부를 가리는 경우에 그럴 수 있다) 스텐실 버퍼는 변하지 않는다.


=>  거울만 스텐실 버퍼에 렌더링하는 것이므로 스텐실 버퍼의 항목들 중 거울의 가시적인 부분(반사상이 나타날 수 있는 영역)의 픽셀에 해당하는 항목들은 모두 1, 나머지는 모두 0이 된다.
=>  이 단계에 의해 스텐실 버퍼는 거울의 가시적 픽셀들이 표시된 상태가 된다.
※  장면의 보통 물체들을 먼저 그린 후 거울을 그린다는 점이 중요하다. 그래야 거울 픽셀들 중 물체에 가려진 픽셀들이 깊이 판정에 실패해도 스텐실 버퍼가 수정되지 않는다.

4.  반사된 두개골을 후면 버퍼와 스텐실 버퍼에 렌더링한다.
=>  픽셀이 스텐실 판정을 통과한 경우에만 후면 버퍼에 기록되게 해야 한다.

5.  마지막으로, 거울 자체를 후면 버퍼에 통상적인 방식으로 렌더링하되, 반사된 물체(거울 평면 뒤에 있음)가 가려지지 않도록 하기 위해 투명도 혼합을 적용한다.

 

  • 평면거울의 구현(10.4절) - 거울 에졔의 깊이ㆍ스텐실 상태 정의(10.4.2절 432p)

-  위의 알고리즘 구현을 위해 두 개의 깊이ㆍ스텐실 상태 집합이 필요하다.

=>  하나는 거울 픽셀들을 스텐실 버퍼에 기록하기 위해 거울을 렌더링할 때 사용하는 것, 하나는 반사된 물체를 거울의 가시적 부분에만 그릴 때 사용하는 것

 

  • 평면거울의 구현(10.4절) - 장면 그리기(10.4.3절 433p)

-  바닥과 벽, 두개골을 통상적인 방식으로 렌더링 한다.

-  거울을 스텐실 버퍼에 그린다.

=>  거울의 가시적 부분에 해당하는 스텐실 항목들을 1로 설정하여 결과적으로 '거울 픽셀'들을 스텐실 버퍼에 표시해두는 효과를 낸다.


-  거울을 제일 마지막에 그려야 한다.

=>  두개골을 먼저 렌더링해서 두개골의 깊이 값들이 깊이 버퍼에 기록되어 있어야, 거울을 렌더링할 때 두개골에 가려진 부분이 깊이 판정에 실패해서 스텐실 버퍼가 갱신되지 않는다.
==>  그렇게 하지 않으면 반사상이 두개골을 통과해서 보이게 된다.

 

  • 평면거울의 구현(10.4절) - 정점 감기 순서와 반사(10.4.4절 435p)

-  삼각형을 한 평면에 대해 반사시켜도 정점들이 감기는 순서가 뒤집히지는 않으며, 따라서 면 법선도 뒤집히지 않는다.

=>  결과적으로 원래 바깥을 향해 있던 법선이 반사 후에는 안쪽을 향하게 된다.


-  이를 해결하기 위해 Direct3D에게 반시계방향으로 감긴 삼각형을 전면 삼각형으로 간주(교재의 통상적인 관례와는 반대이다.)하라고 알려주어야 한다.

=>  본질적으로 법선 방향이 반대가 되어서 반사 후에도 면들이 바깥을 향하는 효과가 난다.

 

  • 평면 그림자의 구현(10.5절 437p)

-  평면 그림자를 구현하려면 우선 물체가 평면에 드리우는 그림자를 기하학적으로 계산해 내야한다. 그 후, 그림자 기하구조의 삼각형들을 50% 투명한 검은색으로 그리면 된다.

=>  이런 방식의 그림자 렌더링에서는 '이중 혼합'이라고 부르는 렌더링 결함이 생길 수 있다.

 

  • 평면 그림자의 구현(10.5절 437p) - 평행광 그림자(10.5.1절 438p)
  • 평면 그림자의 구현(10.5절 437p) - 점광 그림자(10.5.2절 440p)
  • 평면 그림자의 구현(10.5절 437p) - 범용 그림자 행렬(10.5.3절 441p)

 

  • 평면 그림자의 구현(10.5절 437p) - 스텐실 버퍼를 이용한 이중 혼합 방지(10.5.4절 442p)

-  그림자를 묘사하기 위해 물체를 평면에 평평하게 투영하면, 둘 이상의 투영된 삼각형이 서로 겹칠 수 있다.

=>  이럴 가능성이 높다.


-  혼합을 이용해 그림자를 반투명하게 렌더링하면 삼각형들이 겹치는 영역이 여러 번 혼합되어 더 어둡게 나타난다.

=>  이 문제를 스텐실 버퍼를 이용해 해결할 수 있다.

1.  그림자를 렌더링할 스텐실 버퍼의 픽셀들이 0으로 지워져 있다고 가정.
=>  이번 장의 거울 예제는 그림자를 바닥 평면에만 드리우며 스텐실 버퍼 항목(픽셀)들 중 1로 설정된 것은 거울의 가시적인 부분 뿐이다.


2.  스텐실 버퍼 항목이 0인 픽셀만 허용하도록 스텐실 판정을 설정한다. 현재 픽셀이 스텐실 판정을 통과하면 해당 버퍼 항목을 1로 증가하도록 설정한다.
=>  이렇게 하면 이중 혼합이 방지된다.

 

  • 평면 그림자의 구현(10.5절 437p) - 평면 그림자 구현 코드(10.5.5절 442p)

-  화면에 렌더링 된 이미지 속의 원점 좌표 및 x, y, z축 방향이 헷갈린다.


     (7.5, 0.0, 0.0)
              ㅁ ------------------- ㅁ  (-3.5, 0.0, 0.0)
                  |                     |
                  |                     |
                  |                     |
                  |                     |
              ㅁ ------------------- ㅁ  (-3.5, 00, -10.0)
     (7.5, 0.0, -10.0)


-  화면 속 바닥의 각 꼭짓점의 좌표가 다음과 같다.

=>  위를 바탕으로 렌더링 된 이미지 속의 원점 좌표를 생각하고 바라보면 빛의 방향 등을 이해할 수 있다.

 

 

 


 

 

 

틀린 부분이나 이상한 부분이 있으면 댓글로 편하게 지적해주세요.

감사합니다!

반응형

'DirectX11 > 정보정리' 카테고리의 다른 글

[DirectX11] 기하 셰이더  (0) 2023.01.25
[DirectX11] 혼합  (0) 2023.01.05
[DirectX11] 텍스처 적용  (2) 2022.11.29
[DirectX11] 조명  (0) 2022.11.23
[DirectX11] 효과 프레임워크  (0) 2022.11.08

댓글