
unity나 뭐 다른엔진 사용해보면
오브젝트의 특정 부분만 가리고 싶을때 mask라는걸 사용할거임

이렇게 특정 부분에서 fragment를 지워주는게 stencil임

이렇게 stencil buffer를 통해
screen space에 출력되어야할 fragment들을 지워버리는거지비 ㅇㅇ
이런 작업을 수행하는 연산을
stencil test라고 함
stencil test는 fragment shader이후에 실행되고,
early depth test가 아닌 경우에
fragment shader -> stencil test -> depth test순서로 실행됨
stencil buffer는 보통 8비트의 값을 가짐
따라서 픽셀당 의 경우의 수를 가지므로 최대 총 256개의 서로다른 스텐실 값을 가지게 되는거지
간단함
depth buffer처럼
glEnable을 통해 먼저 설정을 enable시켜주고
매 프레임마다 stencil buffer를 초기화해주면 됨
glEnable(GL_STENCIL_TEST);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
Advenced OpenGL(1) - Depth test
여기서 glDepthMask를 보면
GL_TRUE, GL_FALSE로 DepthBuffer에
쓰기, 읽기로 바꿔주는걸 확인할 수 있음
stencil test도 마찬가지임
스텐실 값과 AND연산을 통해 연산이 수행될 비트마스크를 설정할 수 있음
glStencilMask(0xFF);
glStencilMask(0x00);
0xFF(255) : 쓰기 허용, 특정 값 n과 AND연산을 하면 n이 됨0x00(0) : 쓰기 차단, 어떠한 값 n과 AND연산을 해도 0이됨stencil설정에 필요한 메서드임
glStencilFunc(GLenum func, GLint ref, GLuint mask);
이렇게 되어있음
glDepthFunc에 사용되는 값과 의미가 동일함0xFFglStencilFunc는 스텐실 버퍼에 따라 특정 fragment를 통과시킬지 없애버릴지 결정을 하는거임
stencil test의 성공여부에 따라 스텐실 버퍼를 어떻게 업데이트 할건지 정할 수 있음
glStencilOp(GLenum sfail, GLenum dpfail, GLenum dppass)
각각의 옵션에 사용할 수 있는 값은 다음과 같음
| 조치 | 설명 |
|---|---|
| GL_KEEP | 현재 저장된 스텐실 값을 유지 |
| GL_ZERO | 스텐실 값을 0으로 설정 |
| GL_REPLACE | 스텐실 값을 glStencilFunc로 설정된 참조 값으로 대체 |
| GL_INCR | 스텐실 값이 최대값보다 낮으면 1을 증가 |
| GL_INCR_WRAP | GL_INCR과 동일하지만 최대값(기본 8비트 0xFF)을 초과하면 0으로 돌아옴 |
| GL_DECR | 스텐실 값이 최소값보다 높으면 1을 감소 |
| GL_DECR_WRAP | GL_DECR과 동일하지만 0보다 낮아지면 최대값으로 돌아옴 |
| GL_INVERT | 현재 스텐실 버퍼 값을 비트 단위로 반전 |
기본옵션은 순서대로
GL_KEEP, GL_KEEP, GL_KEEP임
stencil 예시로 외곽선 그리는걸 해볼거임

이 배낭의 외곽선을 흰색으로 그려보도록 하게뜸
먼저 rendering 외부에 stencil 설정을 해줌
glEnable(GL_STENCIL_TEST);
glStencilFunc(GL_NOTEQUAL, 1, 0xFF);
glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
전략은 다음과 같음
1)을 0xFF와 비교한 후,GL_NOTEQUAL) stencil test통과그리고 외곽선을 위한 쉐이더를 만들음
//vsh
#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec2 aTexCoords;
out vec2 TexCoords;
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
void main()
{
TexCoords = aTexCoords;
gl_Position = projection * view * model * vec4(aPos, 1.0f);
}
//----------------------------
//fsh
#version 330 core
out vec4 FragColor;
void main()
{
FragColor = vec4(1.0, 1.0, 1.0, 1.0);
}
이제 while문에서 수정만 해주면 됨
while (!glfwWindowShouldClose(window))
{
// ...
// render
// ------
glClearColor(0.05f, 0.05f, 0.05f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
//1. 기본배낭 shader 설정
modelMatShader.use();
glm::mat4 projection = glm::perspective(glm::radians(camera.Zoom), (float)SCR_WIDTH / (float)SCR_HEIGHT, 0.1f, 100.0f);
glm::mat4 view = camera.GetViewMatrix();
glm::mat4 model = glm::mat4(1.0f);
modelMatShader.setMat4("projection", projection);
modelMatShader.setMat4("view", view);
modelMatShader.setMat4("model", model);
//1. 기본크기 배낭 렌더링
glStencilFunc(GL_ALWAYS, 1, 0xFF);
glStencilMask(0xFF);
myModel.Draw(modelMatShader);
//2. 약간 큰 크기 배낭 shader 설정
float scale = 1.01f;
stencilShader.use();
model = glm::scale(model, glm::vec3(scale, scale, scale));
stencilShader.setMat4("model", model);
stencilShader.setMat4("view", view);
stencilShader.setMat4("projection", projection);
//2. 외곽선용 약간 큰 크기 배낭 렌더링
glStencilFunc(GL_NOTEQUAL, 1, 0xFF);
glStencilMask(0x00);
//3
glDisable(GL_DEPTH_TEST);
myModel.Draw(stencilShader);
//mode redering logic...
//4
glStencilMask(0xFF);
glStencilFunc(GL_ALWAYS, 0, 0xFF);
glEnable(GL_DEPTH_TEST);
//...
}
1,2,3,4가 있지?
그걸 차례대로 설명해줌
//1. 기본크기 배낭 렌더링
glStencilFunc(GL_ALWAYS, 1, 0xFF);
glStencilMask(0xFF);
1 & 0xFF를 한 값을 GL_ALWAYS전략을 이용해 stencil buffer 채우는 옵션 설정 //2. 외곽선용 약간 큰 크기 배낭 렌더링
glStencilFunc(GL_NOTEQUAL, 1, 0xFF);
glStencilMask(0x00);
1 & 0xFF를 한 값을 GL_NOTEQUAL전략을 이용해 stencil buffer 채우는 옵션 설정 //3
glDisable(GL_DEPTH_TEST);
//4
glStencilMask(0xFF);
glStencilFunc(GL_ALWAYS, 0, 0xFF);
glEnable(GL_DEPTH_TEST);
glStencilMask : 0xFF로 돌려놓음으로써 다음 프레임에 버퍼를 clear할때, 성공적으로 쓰기가 되도록 만들어놓음glStencilFunc : 매 프레임 초기에 buffer clear를 하기때문에 필요는 없음glEnable : 다음 프레임 실행될때 depth test가 꺼져있으면 buffer clear를 하나마나 소용이 없어 이상하게 렌더링 됨
크으~~~~