본문 바로가기

OpenGL Study

OpenGL "Stencil testing"

[STENCIL TESTING]

1. Stencil test

- stencil test : fragment가 폐기될 지 안될 지 테스트 하는 것

: fragment shader가 fragment를 처리하고 나면 stencil test이 실행된다

: stencil test 이후 남아있는 fragment는 depth test로 보내져 폐기될 지 안될 지 재차 테스트한다

- stencil test는 지금까지 사용한 buffer와는 다른 stencil buffer를 기반으로 수행된다

: stencil buffer는 렌더링 동안에 수정할 수 있다

2. Stencil Buffer

- stencil buffer는 일반적으로 8bit의 stencil value를 가지고 있고, 이 값은 pixel/fragment마다 256개의 값으로 나타내어진다

: stencil value를 설정하여 특정한 stencil value를 가지고 있는 특정 fragment를 폐기할지, 유지할지를 제어할 수 있다

- stencil buffer는 먼저 0으로 채워지고 나서 속이 비어있는 사각형 모앵의 1을 설정한다

: 이 작업이 수행되면 해당 scene의 fragment 중에서 stencil 값이 1인 fragment만 렌더링 되고 다른 것들은 폐기된다

+) 각 window 라이브러리들은 stencil buffer를 세팅해야한 작동하는데, GLFW는 이를 자동적으로 수행하기 때문에 따로 GLFW에게 생성하라고 지시하지 않아도 된다 

-  stencil buffer는 우리가 fragment를 렌더링해야할 곳에 특정 값을 설정할 수 있도록 한다

: 렌더링하는 도중에 stencil buffer를 수정함으로써 stencil buffer를 작성할 수 있다

: stencil buffer를 작성한 렌더링 루프에서, 특정 fragment들을 폐기하거나 유지하기 위해서 이 값을 읽을 수 있다

- stencil buffer를 사용하는 일반적인 틀

: stencil buffer 작성 활성화

: object 렌더링, stencil buffer 수정

: stencil buffer 작성 비활성화

: stencil buffer를 기반으로 특정 fragment를 폐기하여 object 렌더링

- stencil buffer를 사용함으로써 scene에 그려진 다른 object의 fragment들을 기반으로 하여 특정 fragment를 폐기시킬 수 있다

glEnable(GL_STENCIL_TEST);

- GL_STENCIL_TEST를 활성화하여 stencil testing을 활성화시킬 수 있다

: 이 시점부터 모든 렌더링 명령은 stencil buffer의 영향을 받는다

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);

- color, depth buffer와 마찬가지로 매 렌더링 루프마다 stencil buffer를 비워주어야한다

glStencilMask(0xFF); // each bit is written to the stencil buffer as is
glStencilMask(0x00); // each bit ends up as 0 in the stencil buffer (disabling writes)

- depth testing의 glDepthMask 함수와 동일한 기능을 하는 glStencilMask를 사용해 곧 buffer에 작성될 stencli 값에 AND 연산을 시킬 bitmask를 설정할 수 있도록 한다

: 기본적으로 bitmask는 모두 1로 설정되어 출력에 아무런 영향을 주지 않지만 이를 0x00으로 설정하면 buffer에 작성되는 모든 stencil 값들은 0이 된다 (glDepthMack(GL_FALSE)와 유사하다)

- 대부분의 경우에서 stencil mask를 0x00이나 0xFF로 설정하지만 임의의 bitmask들을 설정할 수 있는 option들도 존재한

3. Stencil functions

- Depth testing과 마찬가지로 stencil test를 통과시킬지 말지에 대한 기준을 설정할 수 있다

 - stencil testing을 설정할 수 있는 함수는 glStencilFunc와 glStencilOp가 있다

glStencilFunc(GL_EQUAL, 1, 0xFF)

glStencilFunc(option, reference value, mask)

- option

: stencil test 함수를 설정한다

: 설정된 test 함수는 저장된 stencil value와 glStencilFunc의 reference value에 적용된다

: 가능한 option으로는 GL_NEVER, GL_LESS, GL_LEQUAL 등이 있고 이것들의 의미는 depth buffer의 함수들과 비슷하다

- reference value

: stencil test에 대한 reference value를 지정한다

: stencil buffer의 내용은 이 값과 비교된다

- mask

: stencil buffer와 reference value를 비교하기 전에 reference value와 저장된 stencil 값 모두에 AND 연산이 수행되어질 mask를 지정한다

: 초기값으로는 모두 1로 설정된다

- 상단에 보이는 glStencilFunc는 fragment의 stencil 값이 reference value인 1과 동일하다면 test를 통과시킨 후 렌더링하고, 그렇지 않으면 폐기하라고 지시하도록 설정되어있다

- glStencilFunc는 오직 OpenGL이 stencil buffer의 내용으로 무엇을 해야하는지에 대해서만 묘사하고 사용자가 실제로 buffer를 수정할 수 있는 방법에 대해서는 다루지 않기 때문에 이는 glStencilOp 함수에서 다루어진다

glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP)

glStencilOp(sfail, dpfail, dppass)

- sfail : stencil test가 실패했을 때의 행동을 설정한다

- dpfail : stencil test는 통과했으나 depth test를 실패했을 때의 행동을 설정한다

- dppass : 모든 test를 통과했을 때의 행동을 설정한다

- 상단에 있는 glStenciOP는 모든 option들을 기본값으로 설정해두었고, 함수에서 사용 가능한 option들은 위 이미지와 같다

: GL_KEEP을 사용하면 test의 결과에 상관없이 stencil buffer의 값이 유지되므로 stencil buffer를 수정하려면 option 중 하나라도 다른 option으로 바꾸어야한다

 

[OBJECT OUTLINING]

- object outlining 

: stencil testing으로 구현할 수 있는 각 object에 대해 색이 입혀진 작은 외곽선을 생성하는 기능이다

- object outlining의 과정

01] object를 그리기 전에 stencil 함수를 GL_ALWAYS로 설정하고 object의 fragment가 렌더링될 때마다 stencil buffer를 1로 수정한다

02] object를 렌더링한다

03] stencil을 작성하고 depth testing을 비활성화한다

04] 각 object들을 약간 확대한다

05] 하나의 외곽선 컬러를 출력하는 별도의 fragment shader를 사용한다

06] object를 다시 그리지만 stencil value가 1이 아닌 fragment만 그린다

07] 다시 stencil을 작성하고 depth testing을 활성화한다

- 이 과정은 각 object의 fragment들에 대해 stencil buffer의 내용을 1로 설정하고 외곽선을 그리고 싶을 때 object의 확대된 버전을 stencil test가 통과된 부분만 그린다

: 확대된 버전은 object의 외곽선으로 그려진다

: 기본적으로 stencil bufffer를 사용하여 확대된 버전과 원본 object가 겹치는 부분의 fragment는 폐기한다

void main()
{
    FragColor = vec4(0.04, 0.28, 0.26, 1.0);
}

- 외곽선 컬러를 출력하는 fragment shader를 생성하기 위해 우선 간단히 컬러 값을 하드코딩하여 shaderSinglecolor shader를 생성한다

glEnable(GL_STENCIL_TEST);
glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);

- stencil testing을 활성화하고 glStencilOp의 option을 설정한다

: 어떠한 test에서든 실패했다면 저장된 값을 유지하고, test를 모두 통과했다면 저장된 stencil 값을 지정된 reference 값으로 수정한다

glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);  
glStencilFunc(GL_ALWAYS, 1, 0xFF); // all fragments should pass the stencil test
glStencilMask(0xFF); // enable writing to the stencil buffer
normalShader.use();
DrawTwoContainers();

- stencil buffer를 0으로 비우고 컨테이너를 그리기 위해 그려진 fragment들에 대해 stencil buffer를 1로 수정한다

- GL_REPLACE를 사용하여 컨테이너의 각 fragment들이 stencil value 1로 stencil buffer를 수정하는 지 확인한다

: fragment가 항상 stencil test를 통과하기 때문에 stencil buffer는 그려진 모든 곳에서 reference value로 수정된다

glStencilFunc(GL_NOTEQUAL, 1, 0xFF);
glStencilMask(0x00); // disable writing to the stencil buffer
glDisable(GL_DEPTH_TEST);
shaderSingleColor.use(); 
DrawTwoScaledUpContainers();

- stecil buffer 작성을 비활성화한다

- glStencilFunc를 GL_NOTEQUAL로 설정하여 1이 아닌 부분만 그리도록 한다 (이전에 그린 컨테이너의 바깥쪽 부분만 그린다)

- depth testing을 비활성화하여 바닥에 가려지지 않도록 설정한다

- 모두 수행하고 나면 depth buffer를 다시 활성화 시킨다

glEnable(GL_DEPTH_TEST);
glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);  
  
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); 

glStencilMask(0x00); // make sure we don't update the stencil buffer while drawing the floor
normalShader.use();
DrawFloor()  
  
glStencilFunc(GL_ALWAYS, 1, 0xFF); 
glStencilMask(0xFF); 
DrawTwoContainers();
  
glStencilFunc(GL_NOTEQUAL, 1, 0xFF);
glStencilMask(0x00); 
glDisable(GL_DEPTH_TEST);
shaderSingleColor.use(); 
DrawTwoScaledUpContainers();
glStencilMask(0xFF);
glStencilFunc(GL_ALWAYS, 1, 0xFF);   
glEnable(GL_DEPTH_TEST);

- 전체적인 object outlining 과정

- 전체 소스 코드를 실행한 출력 결과이다

'OpenGL Study' 카테고리의 다른 글

OpenGL "Face Culling"  (0) 2023.02.01
OpenGL "Blending"  (1) 2023.02.01
OpenGL "Depth Testing"  (0) 2023.01.31
OpenGL "Model"  (0) 2023.01.25
OpenGL "Mesh"  (0) 2023.01.24