[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 |