=================================
=================================
=================================
출처: http://wecansoar.tistory.com/103
문을 페인트칠 하려고 할때
문고리까지 도색하지 않기 위해 마스킹테이프 같은걸로 덧씌워놓는데
이 마스킹테이프 같은 용도로 사용하는게 Stencil Test (=Stencil Buffer)이다.
특정 부분을 제외한 채 Rendering 하는 예제
void RenderScene(void)
{
GLdouble dRadius = 0.1; // 원의 반지름 값 설정
GLdouble dAngle;
// 화면을 파란색으로 채움
glClearColor(0.0f, 0.0f, 1.0f, 0.0f);
// 스텐실 버퍼의 초기값 지정
glClearStencil(0);
// 위에서 지정한 값으로 Stencil Buffer를 채움 (스텐실 버퍼를 0으로 채움)
glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
// 스텐실 테스트 기능 활성화
glEnable(GL_STENCIL_TEST);
/* ---- 스텐실 테스트를 활성화 시킨 후부터는 Frame Buffer 에 쓰는 작업이 Stencil Buffer 에도 그대로 반영된다 ---- */
// 스텐실 버퍼내의 값과 두번째 인자와 세번째 인자를 And 연산한 값을 비교값을 비교해 첫번째 인자인 조건에 맞춰 스텐실 테스트를 통과시킬지 지정
glStencilFunc(GL_NEVER, 0, 0);
/* GL_NEVER : 어떠한 Fragment(pixel)도 Test를 통과하지 못함. 두번째 세번째 매개변수 값이 뭐가 되던지 아무것도 그려지지 않게 됨
GL_ALWAYS : 모든 값 통과. 즉 두번째 세번째 매개변수와 상관없이 화면에 그리고자 하는 그림이 그대로 보임.
GL_LESS
GL_LEQUAL
GL_EQUAL
GL_NOTEQUAL : 스텐실 버퍼내의 값과 비교값이 같지 않을 경우 통과. 여기서는 (GL_NOTEQUAL, 0, 0) 도 같은 의미가 된다. 초기값으로 스텐실 버퍼를 모두 0으로 채웠기 때문에 0이 아닌 경우가 없어 모든 스텐실 테스팅이 실패하기 때문이다.
*/
// 스텐실 버퍼에 접근해 버퍼내의 값을 변경할 조건을 설정하는 부분
glStencilOp(GL_INCR, GL_INCR, GL_INCR);
/* 첫번째 인자는 스텐실 테스트가 실패 => 스텐실 버퍼의 다음 동작 정의
두번째 인자는 스텐실 테스트 성공, depth 테스트 실패 => 스텐실 버퍼의 다음 동작 정의
세번째 인자는 스텐실 테스트, depth 테스트 모두 성공 => 스텐실 버퍼의 다음 동작 정의
GL_KEEP : 스텐실 버퍼 내의 값을 그대로 유지
GL_ZERO
GL_REPLACE
GL_INCR : 스텐실 버퍼에 있는 값을 하나 증가시킨다. 여기서는 GL_NEVER,0,0 으로 했기 때문에 모든 스텐실 테스트는 실패하므로 첫번째 인자인 GL_INCR이 동작하고, 화면에 보이지는 않지만 해당 위치의 버퍼 내용만 0에서 1로 바뀌게 된다. (종이에 구멍이 뚫린다는 개념??)
GL_DECR
*/
// 스텐실 패턴을 생성. 서로 지름이 다른 흰색 선 나선형 패턴을 그림
glColor3f(1.0f, 1.0f, 1.0f);
glBegin(GL_LINE_STRIP);
for(dAngle = 0; dAngle < 400.0; dAngle += 0.1)
{
glVertex2d(dRadius * cos(dAngle), dRadius * sin(dAngle));
dRadius *= 1.002;
}
glEnd();
/* ---- 여기까지... 나선원은 화면에 보이지는 않지만 나선원이 그려지는 부분에 있는 스텐실 버퍼의 값이 1로 변경되어 있음 ---- */
// 스텐실 테스팅 방식이 변경됨. 스텐실 버퍼내에 1이 아닌 위치에 픽셀들만 Test 통과되게 설정
// 나선원을 그린 후에 나선원 부분의 스텐실 버퍼 값은 1로 변경됐는데 1과 같지 않은 것만 통과시키겠다는 뜻이므로 나선원 부분만 제외하고 그려질 것이다.
// 만약 (GL_NOTEQUAL, 2, 1); 이렇게 하면 2 & 1 = 0 이므로 0이 아닌것만 통과시키겠다는 뜻이므로 나선원 부분만 그려지게 된다.
glStencilFunc(GL_NOTEQUAL, 1, 1);
// 나선원 그려진 부분의 스텐실 버퍼값이 1이고 나머지 부분은 0이기 때문에 나선원이 있는 부분은 실패. 나머지는 성공.
// 나선원이 있는 부분은 첫번째 인자 GL_KEEP 실행. 나머지 부분은 두번째 인자 GL_KEEP 실행.
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
// 빨간색 사각형 그림
glColor3f(1.0f, 0.0f, 0.0f);
glRectf(x, y, x + rsize, y - rsize);
/* ---- 여기까지... 나선원 부분만 제외하고 사각형이 그려짐 ------ */
glSwapBuffers();
}
출처: http://202psj.tistory.com/1033 [알레폰드의 IT 이모저모]
=================================
=================================
=================================
출처: http://blog.naver.com/kzh8055/140043022533
[OpenGL] Fragment TEST - 1.스텐실 테스트(Stencil TEST)
흠...
이번에 살펴볼 내용은 Stencil Buffer(혹은 Stencil Test)에 대해서다.
*정점 변환 하다 말고 왜 갑자기 Rendering PipeLine 의 막바지인 Fragment Test로
건너 띄게 된 이유는 따지지마라.
Direct Draw 때와 마찬가지로 순전히 본인 X리는데로 적는것 뿐이니.
Stencil TEST
우선 Stencil Test 가 뭔 짓거리를 하는 건지 책에서 읽었던 기억을 더듬어
아주 대략 설명해 보겄다.
문을 새로이 도색하고자 한다. 보통 페인트 칠에 앞서 문꼬리에 신문지와 같이 무언가를
덧 씌워놓는데 이것은 문꼬리까지 도색하지 않기 위해서일것이다.
Stencil Buffer 용도가 바로 이런것이다.
즉,문을 칠하는데 있어 자신이 칠하기 싶은 않은곳에 Mask를 씌어 그곳만은
페인트 칠의 영향을 받지 않게 하는것이다.
뭐 여기까지야 Stencil Buffer 를 거론 하는 대부분의 지문들에 나와있는 기본적인 내용들이고
이해도 그리 어렵지 않을것이다.
다만, 본인이 Stencil TEST를 이해 하는데 가장 어려웠던 부분인
'대체 언제 Stencil Buffer에 쓰는거여?'
즉,그리지 말아야 할 부분의 형태(MASK)를 만드는 부분에 대해선 관련 내용을 찾기 힘들었다.
하여간 지금 부터 Stencil Test 사용법과 예제를 통해 위에 대한 질문에 답해 보겄다.
1.Stencil Buffer 요청
처음으로 해야할일은 Stencil Buffer를 요청하는 일이다.
이 작업은 GLUT 를 사용해 OpenGL Programming을 할경우 아주 간단하게
Display Mode 초기화 함수 호출시 인자에 GLUT_STENCIL (상수)를 추가해 주면 된다.
glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_STENCIL )
2.Stencil Test 활성화
glutEnable(GL_STENCIL_TEST)
3. Stencil Test 설정
glStencilFunc(GLenum 함수,GLint 비교값, GLint Mask)
위 함수는 Stencil buffer내에 있는 내용과 비교값에 지정된 값을 어떠한 기준으로
비교할것인지 를 지정하는 용도로 사용된다.
함수, 즉 비교방법에 올 수있는 enum 값은
GL_NEVER,GL_ALWAYS,GL_LESS,GL_LEQUAL,GL_EQUAL,GL_NOTEQUAL
등이 있고 가령 GL_NEVER의 경우 아무것도 TEST 를 통과 하지 못하게 하고
GL_ALWAYS 는 모두 통과 GL_NOTEQUAL은 Stencil Buffer내의 값과 비교 값이 같지 않을
경우 통과 된다.
4. Stencil Pattern 생성
glStencilOp(GLenum 실패, GLenum z_실패, GLenum z_성공)
이 부분이 바로 Stencil Buffer에 접근해 Buffer내의 값을 변경할 조건을 설정하는것이다.
첫번째 인자는 Stencil Test가 실패 했을시 Stencil Buffer 어떻게 변경할것인가를 나타낸다.
여기엔 GL_KEEP, GL_ZERO, GL_REPLACE, GL_INCR, GL_DECR 등이 올수 있다.
한가지 예를 들면 GL_KEEP 은 Stencil Buffer 내의 값을 그대로 유지한다.
두 번째 인자와 세 번째 인자는 Stencil Test가 성공했다는 전제 하에 Depth(Z) Test 의 성공
여부에 따라 적용될 Stencil Buffer내의 값 변경 방식을 나타낸다.
흠...이론은 이정도로 됐고 예제 하나를 훑어보며 Stencil Test 개념을 잡아보자.
*위의 내용과 지금부터 등장할 예제는 OpenGL SuperBible을 거의 그대로 옮긴것이다.
void RenderScence(void)
{
GLdouble dRadius = 0.1;
GLdouble dAngle;
glClearColor(0.0f,0.0f,1.0f,0.0f);
glClearStencil(0.0f);
glEnable(GL_STENCIL_TEST);
glClear(GL_COLOR_BUFFER | GL_STENCIL_BUFFER_BIT);
glStencilFunc(GL_NEVER,0x0,0x0);
glStencilOp(GL_INCR , GL_INCR , GL_INCR);
glColor3f(1.0f,1.0f,1.0f);
glBegin(GL_LINE_STRIP);
for( dAngle = 0 ; dAngle < 400 ; dAngle += 0.1)
{
glVertex2d( dRadius * cos(dAngle) , dRadius * sin( dAngle));
dRadius *= 1.002;
}
glEnd();
glStencilFunc(GL_NOTEQUAL , 0x1, 0x1 );
glStencilOp(GL_KEEP,GL_KEEP,GL_KEEP);
glColor3f(1.0f,0.0f,0.0f);
glRectf(x,y, x + rsize, y - rsize);
glSwapBuffer();
}
위 예제는 Stencil Test 를 이용해 특정 부분을 제외한 채 Rendering하는 예제다.
하여간 차근차근 살펴보자.
glClearStencil(0.0f) 함수를 통해 Clear 할 Stencil Buffer의 초기값을 지정해주고
glClear(GL_COLOR_BUFFER | GL_STENCIL_BUFFER_BIT) 함수를 호출함으로써 실제로
위에서 지정한 값으로 Stencil Buffer를 채운다.
그리고 나서 Stencil TEST를 활성화 시킨다. -> glEnable(GL_STENCIL_TEST)
다음은 Stencil Test 방법을 지정하는것인데
여기서 알아야 할 중요한 부분은
Stencil Test를 활성화 시킨 후 부터는 Frame Buffer 에 쓰는 작업이
Stencil Buffer에도 그대로 반영된다는 것이다.
일단 그렇게 알고 계속 가보자.
glStencilFunc(GL_NEVER,0x0,0x0)
glStencilOp(GL_INCR , GL_INCR , GL_INCR);
glStencilFunc(...)함수는 Stencil Buffer 내의 값과 비교값(두번째 인자)를 비교해
조건(첫번째 인자)과 맞다면 Stencil Test를 통과하게끔 하기 위해 조건과 비교값을 지정한다.
그러나 조건 방식인 GL_NEVER 이므로 어떠한 Fragment(pixel)도 Test를 통과 하지 못한다.
그런데 아래 함수 glStencilOp의 첫번째 인자, 즉 Stencil Test를 통과하지 못했을때
Stencil Buffer에 적용되는 변경 방법은 GL_INCR, Buffer내의 해당 값을 증가 시키는 것이다.
이게 뭔 말이냐면 일반적인 Rendering 즉 Frame Buffer에 뭔가를 그릴려 시도한다해도
현 상태에서 무조건 Stencil TEST가 실패로 간주되므로 Frame Buffer에 아무 것도 그려지지
않고 그 Frame Buffer의 대응되는 동일 위치의 Stencil Buffer내의 값이 증감된다는 말이다.
glBegin(GL_LINE_STRIP) 과 glEnd() 사이의 Loop 는 서로 지름이 다른 동심원을 그린다.
이때 위의 조건에 의해 Frame Buffer에는 어떤 Pixel(Fragment)도 써지지 못하지만
Stencil Buffer에는 아마도 Frame Buffer에 쓰려던 형태(값은 다르다. 0x1 )가 그대로 써질 것이다.
바로 여기까지가 Stencil Pattern 즉, Mask를 만든것이다.
<Frame Buffer> <Stencil Buffer>
<A. glClear() 함수를 통해 초기화된 상태>
<B. 동심원을 Rendering 한 후 >
<C. 사각형을 Rendring 한 후 >
여기서 다시 Stencil TEST 방식이 변경됐는데 이번엔
참조 방식이 GL_NOTEQUAL 이고 비교 값이 0x1 이됐다.
풀어서 얘기하자면 Frame Buffer에 무언가를 그릴시에
Stencil Buffer내에 0x1이 아닌 위치에 Pixel들만 Test가 통과 되게 설정했다는것이다.
이런 상태에서 Rectf(...) 함수를 이용해 사각형을 그리면 Stencil Buffer에 그려진
동심원의 선 부분을 제외한 곳이 그려질것이다.
반대로 Stencil Buffer에 정의된 부분만 Frame Buffer에 그리고자 한다면
마지막 glStencilFunc(...)의 첫번째 인자를 GL_EQUAL 로 바꾸면 될것이다.
하여간 정리하자면
Stencil Buffer를 이용해 특정 부분만 Rendering 하고자 하는 방법은 다음과 같다.
1. Stencil TEST 를 활성화 시킨다.
2. 일단 모든 Stencil TEST 실패하게 한상태에서 Mask 형태 Rendering 한다.
->결국, 이것은 Frame Buffer가 아닌 Stencil Buffer에 그려진다.
3. Stencil Test Option을 변경한뒤 객체를 Rendering 한다.
=================================
=================================
=================================
출처: http://202psj.tistory.com/1033
I'm working on a game for the iPhone that has a drawing/paint mechanic involved and I'm having problems trying to create a tool that would erase things already painted.
The main problem is that the background being painted on is not a solid color but a static image or animation. I've tried using different blending options and logical operations in drawing but nothing seemed to work. I'm new to OpenGL so I must be missing something.
Any tips?
EDIT: To give a little more information, I'm using textures for my brushes and using glVertexPointer() and glDrawArrays() to render them. For example:
glBindTexture(GL_TEXTURE_2D, circleBrush); glVertexPointer(3, GL_FLOAT, 0, verts); glTexCoordPointer(2, GL_FLOAT, 0, coords); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
EDIT 2: Unfortunately, stencil buffers are not available on the iPhone. : (
EDIT 3: Framebuffer objects are available on the iPhone and that is the road I took. I haven't fully implemented it yet, but so far it looks like it works the way I wanted it to. Thanks everyone!
----------------------------------------------------------------------------------------------------------------------------------------------------------------
Draw a full-screen textured quad over your scene. When the user draws a brush stroke, use glTexSubImage2D
to update the your texture.
glReadPixels/glDrawPixels is slow.
Using FrameBufferObjects is even better, but I doubt this extention is available on the iPhone (then again, I don't know for sure, so maybe try it). FBO's allow you to draw directly into a texture as if it were another rendering context.
---------------------------------------------------------------------------------------------------------------------------------------------------------------
Stencil Buffer is the best way to approach this for sure...You'll save time, cpu, and potential problems..,They are available on Iphone, you just have to created an OpenGlES 2.0 surface (not gles 1.0 or 1.1).
//Turn off writing to the Color Buffer and Depth Buffer //We want to draw to the Stencil Buffer only glColorMask(false, false, false, false); glDepthMask(false); //Enable the Stencil Buffer glEnable(GL_STENCIL_TEST); //Set 1 into the stencil buffer glStencilFunc(GL_ALWAYS, 1, 0xFFFFFFFF); glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE); //CALL YOUR DRAWING METHOD HERE //Turn on Color Buffer and Depth Buffer glColorMask(true, true, true, true); glDepthMask(true); //Only write to the Stencil Buffer where 1 is set glStencilFunc(GL_EQUAL, 1, 0xFFFFFFFF); //Keep the content of the Stencil Buffer glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); //CALL OUR DRAWING METHOD AGAIN
Lookup this example :http://www.opengl.org/resources/code/samples/glut_examples/examples/stenciltst.c
Here's the visual result :http://www.opengl.org/resources/code/samples/glut_examples/examples/stenciltst.jpg
I've implented the same kind of eraser tool with this, on both android and iPhone and it works like a charm !
Good luck !
Cheers !
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
You don't give much info, but I assume that your storing whatever they "paint" into a buffer and then drawing it on screen like this:
glWindowPos2i(X, Y); glDrawPixels(drawing->Width, drawing->Height, drawing->Format, GL_UNSIGNED_BYTE, drawing->ImageData);
With drawing->ImageData being the buffer. What you could do is have a separate background buffer and draw it first. Then the erase tool will simply white-out the the drawing buffer(turning all the values, including alpha, all the way up).
For this solution to work you'd have to turn on blending and turn off depth testing.
glEnable(GL_BLEND); glDisable(GL_DEPTH_TEST); glWindowPos2i(X, Y); //background never changes glDrawPixels(background->Width, background->Height, background->Format, GL_UNSIGNED_BYTE, background->ImageData); glWindowPos2i(X, Y); glDrawPixels(drawing->Width, drawing->Height, drawing->Format, GL_UNSIGNED_BYTE, drawing->ImageData); glEnable(GL_DEPTH_TEST); glDisable(GL_BLEND);
Is that what you're looking for?
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------
You could use the stencil buffer for this operation. The stencil buffer is a special buffer which holds information for every pixel, similiar to the depth buffer. Unlike the depth buffer, you decide how the stencil buffer is altered while drawing and how it influences the decision to draw into the color buffer or not. To do so, you can set the specific states before any drawing operation. Here is what you do:
- At the time the users erases, draw the "erased" region into the stencil buffer (using StencilOp, see below). You can use any GL drawing function for that.
- In every rendering pass, first draw the background (if you want, you can use a negative stencil test here to only draw the "erased" part of the background - may increase performance).
- Then, enable the stencil test for all other drawing operations. OpenGL will then only draw to the "non-erased" regions.
The function to set how the stencil buffer should influence drawing (or not): glStencilFunc()
The function to set how the stencil buffer itself is influenced by drawing: glStencilOp()
Further explanation, also with the arguments for these:http://www.opengl.org/resources/code/samples/sig99/advanced99/notes/node117.html
-------------------------------------------------------------------------------------------------------------------------------------------------------------------
Using glBlendFunc(GL_ONE, GL_ZERO) mode can earse.
출처: http://202psj.tistory.com/1033 [알레폰드의 IT 이모저모]
=================================
=================================
=================================
출처: http://blog.naver.com/PostView.nhn?blogId=gidools&logNo=20131734261
OpenGL stencil buffer
오랜만에 하나 올린다. 오늘은 OpenGL ES 버전이 아니라 OpenGL 에 대한 내용이다.
Stencil buffer - 첨 봤을 때는 어떻게 쓰는 것인지 전혀 감이 안 왔었는데 다시 보니까 이해가 된다. 인터넷에서 검색해 봐도 제대로 된 설명이 안 나와 있어서 초보의 마음으로 접근해 본다.
전체 예제 소스는 첨부 파일을 열어서 실행하면 된다.
핵심 내용에 대해서만 설명하겠다.
void RenderScene(void)
{
GLdouble dRadius = 0.1; // Initial radius of spiral
GLdouble dAngle; // Looping variable
// Clear blue window
glClearColor(0.0f, 0.0f, 1.0f, 0.0f);
// Use 0 for clear stencil, enable stencil test
glClearStencil(0.0f);
glEnable(GL_STENCIL_TEST);
// Clear color and stencil buffer
glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
// All drawing commands fail the stencil test, and are not
// drawn, but increment the value in the stencil buffer.
glStencilFunc(GL_NEVER, 0x0, 0x0);
glStencilOp(GL_INCR, GL_INCR, GL_INCR);
// Spiral pattern will create stencil pattern
// Draw the spiral pattern with white lines. We
// make the lines white to demonstrate that the
// stencil function prevents them from being drawn
glColor3f(1.0f, 1.0f, 1.0f);
glBegin(GL_LINE_STRIP);
for(dAngle = 0; dAngle < 400.0; dAngle += 0.1)
{
glVertex2d(dRadius * cos(dAngle), dRadius * sin(dAngle));
dRadius *= 1.002;
}
glEnd();
// Now, allow drawing, except where the stencil pattern is 0x1
// and do not make any further changes to the stencil buffer
glStencilFunc(GL_NOTEQUAL, 0x1, 0x1);
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
// Now draw red bouncing square
// (x and y) are modified by a timer function
glColor3f(1.0f, 0.0f, 0.0f);
glRectf(x, y, x + rsize, y - rsize);
// All done, do the buffer swap
glutSwapBuffers();
}
아무것도 수정하지 않은 상태의 결과 화면은 아래 그림이다.
그럼, 이제부터 설명..
glClearStencil(0.0f);
스텐실 버퍼의 값을 모두 0으로 채운다는 뜻이다. glClearColor(0.0f, 0.0f, 1.0f) 함수를 호출한 다음 glClear(GL_COLOR_BUFFER_BIT) 함수를 실행하면 컬러 버퍼의 값이 모두 파란색 컬러 값으로 채워지듯이 마찬가지로 스텐실 버퍼를 0 으로 채운다는 개념으로 이해하면 된다. 책 본문의 소스는 0.0f 값을 줬는데 사실 glClearStencil() 함수의 매개변수는GLint 값으로 들어가야 한다. 타입 캐스팅 돼서 GLint 형으로 변경될 것이고 돌아가는데 문제는 없지만 바람직한 코드는 아니다. 따라서 glClearStencil(0) 과 같은 식으로 코드를 수정하는 것이 올바른 코드가 되겠다.
glEnable(GL_STENCIL_TEST);
스텐실 테스팅 기능을 사용할거라고 선언하는 부분이다. 이거 안 넣으면 나선원이던 붉은 사각형이던 모두 화면에 그냥 그려진다.
glStencilFunc(GL_NEVER, 0x0, 0x0);
이 함수의 첫번째 parameter 는 몇 가지가 있다. GL_ALWAYS, GL_LESS 등등이 있다. 그런데 여기서는 GL_NEVER 를 쓰고 있다. 즉, 스텐실 버퍼가 어떤 값이 들어가 있던지 무엇을 그리던지 아무것도 그려지지 않게 하겠다는 뜻이다. 그러므로 GL_NEVER 가 첫번째 값이면 두번째와 세번째 매개 변수가 의미가 없게 된다. 비슷한 의미로 GL_ALWAYS 로 설정되면 모든 값을 통과 시킨다는 뜻. 즉 화면에 그리고자 하는 그림이 그대로 보인다는 뜻이다.GL_ALWAYS 로 설정하고 테스트 해 보면 두번째 세번째 매개 변수의 값이 뭐가 되던지 아래 처럼 화면에 그려진다.
여기서는 glStencilFunc(GL_NOTEQUAL, 0x0, 0x0); 도 glStencilFunc(GL_NEVER, 0x0, 0x0); 와 같은 의미가 된다. 왜냐면 스텐실 버퍼의 값이 모두 0 으로 채워져 있기 때문에 0 이 아닌 경우가 없어 모든 스텐실 테스팅이 실패하기 때문이다.
glStencilOp(GL_INCR, GL_INCR, GL_INCR);
교재에도 나와 있듯이 첫번째 매개 변수는 스텐실 테스트가 실패했을 때 그 다음 동작을 두번째 매개 변수는 스텐실 테스트가 성공하고 depth 테스트가 실패했을 때 세번째는 스텐실 테스트와 깊이 테스트가 모두 성공했을 때 그 다음 동작을 정의하는 것이다. 첫번째 매개변수가 GL_INCR 로 돼 있으므로 스텐실 테스트가 실패했을 때 스텐실 버퍼에 있는 값을 하나 증가시킨다는 뜻이다. 바로 위 함수에서 glStencilFunc(GL_NVER, 0, 0) 으로 했기 때문에 화면에 어떤 그림을 그리던지 모든 스텐실 테스트가 실패한다. 즉, 화면에 아무것도 보이지 않게 된다. 스텐실 테스트가 성공해야 그 부분이 화면에 보이게 되는데 일단은 모든 테스트가 실패하도록 설정했기 때문에 화면에 아무것도 보이지 않는 것이고 그렇기 때문에 첫번째 옵션인 GL_INCR 이 동작하게 된다.그래서 결국 glClearStencil(0.0f)을 실행해서 0 으로 채워진 스텐실 버퍼의 내용이 1로 바뀌게 된다. 화면에 뭔가를 그리고자 했을 때 보이지는 않지만 해당 위치의 버퍼 내용만 1로 바뀌게 되는 것이다(종이에 구멍이 뚫린다는 개념으로 이해하면 된다). 버퍼의 나머지 부분은 그대로 0 으로 유지된다.
glBegin(GL_LINE_STRIP);
화면에 나선형의 그림을 그리는 부분이다. 스텐실 테스트 기능을 꺼 버린다면 - glEnable(GL_STENCIL_TEST) 함수를 실행하지 않는다면 – 서로 이어지는 나선형(?) 원들이 화면에 보일 것이다. 그런데 스텐실 테스트 기능이 ON 상태이고 glStencilFunc(GL_NEVER, 0x0, 0x0) 함수를 실행했기 때문에 화면에 아무것도 보이지 않는 상태인 것이다. 하지만 변화된 내용이 있는데 그것은 glStencilOp(GL_INCR, GL_INCR, GL_INCR) 를 실행하면서 나선원이 그려지는 부분에 있는 스텐실 버퍼의 값이 1 로 변경된다는 것이다. (일단 스텐실 버퍼의 위치와 화면 픽셀이 1:1 매핑이 된다고 생각하면 이해하기 편하다)
glStencilFunc(GL_NOTEQUAL, 0x1, 0x1);
두번째 param 과 세번째 param 을 And 연산한 값이 스텐실 버퍼에 있는 값과 같지 않을 때 통과 시키겠다는 뜻. 나선원을 그린 후에 스텐실 버퍼는 그리고자 하는 위치에 있는 버퍼의 값이 1로 변경됐는데 이 함수를 실행하면 1과 같지 않은 것만 통과 시키겠다는 뜻이다. 즉 화면에 뭔가를 그린다면 나선원 부분만 제외하고 그려질 것이다. 만약 glStencilFunc(GL_NOTEQUAL, 0x2, 0x2); 이렇게 하면 어떻게 그려질까? 0x2 & 0x2 = 0x2 이고 스텐실 버퍼에 있는 값 중에서 0x2 가 아닌 것은 모두 통과 시키겠다는 뜻이므로 결국 화면에 뭘 그리면 그대로 모두 그려지게 된다. 그럼 glStencilFunc(GL_NOTEQUAL, 0x2, 0x1); 요렇게 하면? 0x2 & 0x1 = 0x0 이므로 0 이 아닌 것만 통과 시키게다는 뜻. 결국 나선원 부분만 그려지게 된다. 직접 실행해 보면 아래처럼 보인다.
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
첫 번째 매개변수는 스텐실 테스팅이 실패했을 경우 다음 동작이라고 설명했다. 나선원이 그려진 부분의 스텐실 버퍼값이 1 이고 나머지 부분은 모두 0 이기 때문에 glStencilFunc(GL_NOTEQUAL, 0x1, 0x1); 함수를 실행하면 나선원이 있는 버퍼의 내용과 스텐실 테스트했을 때는 실패, 나머지 부분에서는 모두 성공이 나온다. 왜냐면 0x1 & 0x1 = 0x1 이 아닌 값은 모두 통과 시킨다고 했는데 나선원이 그려진 부분만 1 이기 때문이다. 그래서 0x1 인 버퍼와 테스트했을 때는 실패이므로 첫번째 매개변수인 GL_KEEP을 실행하고 0x0 인 스텐실 버퍼값과 테스트했을 때는 성공이므로 두번째 매개변수인 GL_KEEP 이 실행된다. 만약 두번째 매개변수를 GL_INCR로 하면 어떻게 될까나? 그러면 0 의 값이 들어가 있던 스텐실 버퍼의 내용이 모두1로 되므로 두번째 프레임 부터는 화면에 아무것도 그려지지 않을 것이다라고 예상할 수 있으나 사실은 glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);함수 실행은 이 예제에서 의미가 없는 코드다. 왜냐면 glStencilFunc(GL_NOTEQUAL, 0x1, 0x1); 를 실행하면서 어떤 값들을 통과 시킬지 이미 결정이 나 있는 상태이기 때문에 glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); 를 실행한다고 해도 RenderScene() 함수가 다시 호출되면서 스텐실 버퍼의 값이 모두 0 으로 초기화 되기 때문이다.만약 첫번째 glStencilOp(GL_ INCR, GL
_
INCR, GL_ INCR); 함수를 실행할 때 glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); 와 같은 식으로 실행했다면 스텐실 버퍼의 내용이 변하는 게 없어서 결국 glStencilFunc(GL_NOTEQUAL, 0x1, 0x1); 실행하면서 화면에 그리고자 하는 내용이 모두 그려지게 된다.
glColor3f(1.0f, 0.0f, 0.0f);
glRectf(x, y, x + rsize, y - rsize);
스텐실 테스트와 관련된 함수들을 차례대로 실행하면서 나선원이 그려진 부분은 스텐실 테스팅이 실패하고 나머지 부분에서는 모두 성공할 것이라는 세팅이 돼 있기 때문에 붉은 사각형을 그리면 나선원 부분만 보이지 않고 나머지는 모두 그려진다.
부디 이해가 되셨기를..에고 힘들당..
=================================
=================================
=================================
출처: http://hururuc.tistory.com/14
OpenGL은 Stencil buffer를 가지고 stencil 하는데 사용한다.
Stencil의 기본 기능은, 구멍뚫린 마스크(stencil)를 만들어서 구멍뚫린 부분의 각 pixel들을 비교해서 함수처리(같거나 다르거나 등등)를 해서 동작(색깔을 덧입히거나, 지우거나 등등)을 수행한다.
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
//3개의 buffer를 초기화 한다. 즉 색상버퍼, 깊이버퍼, 스텐실 버퍼를 초기화 한다.
glClearStencil(GLint s);
//stencil buffer 초기화시에 초기화 값으로 사용될 값을 지정한다.
glStencilFunc(GLenum 함수, GLint 참조, GLuint mask);
함수는 비교에 사용되는 기능들 중 하나를 지정하는데, GL_NEVER,GL_ALWAYS,GL_LESS,GL_EQUAL,GL_GEQUAL,GL_GREATER,GL_NOTEQUAL 이 있다. 참조는 비교시에 사용되는 참조 값이다. mask는 비교전에 AND 연산을 수행한다.
즉, (해당bits AND mask) 값이 (참조bits AND mask)와 비교해서 같은지,다른지 확인한다.
glStencilOp(GLenum 실패, GLenum z실패, GLenum z성공);
비교시 값이 거짓인경우, "실패"를 수행하고, 그리고 비교값이 참인경우에 깊이 test가 실패했을때 "z실패"를 수행하고, 깊이 test가 성공시에는 "z성공"을 수행한다.
즉,
1. 비교값이 거짓 : 실패 수행.
2. 비교값이 참:
2.1 깊이 test가 실패: z실패 수행.
2.2 깊이 test가 성공: z성공 수행.
수행되는 값은 GL_KEEP,GL_ZERO, GL_REPLACE, GL_INCR, GL_DECR,GL_INVERT,GL_INCR_WRAP,GL_DECR_WRAP 등이 있다
[Sample code]
어떤 object를 화면의 특정 부분에서만 그리고 싶을 때 stencil buffer를 활용한 예.
void DrawScreen(void)
{
...
glClear(GL_STENCIL_BUFFER_BIT);
glStencilFunc(GL_NEVER, 0x0, 0x0); // 이후에 나오는 drawing 명령은 모두 stencil test를 pass하지 못하므로 실제로 그려지지 않는다.
glStencilOp(GL_INCR, GL_INCR, GL_INCR); // test후 stencil buffer의 값을 증가시킨다.
=================================
=================================
=================================
// draw screen boundary
DrawScreenBoundary();
glStencilFunc(GL_EQUAL, 0x1, 0x1); // 0x1인 부분에 대해서만 드로잉을 허용한다.
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
// 아래 부분은 DrawScreenBoundary()에서 정해준 영역 안에만 렌더링 된다.
// ... Renderinig codes
glStencilFunc(GL_NOTEQUAL, 0x1, 0x1); // 0x1이 아닌 부분에 대해서 드로잉을 허용
// 아래 부분은 DrawScreenBoundary()에서 정해준 영역 밖에만 렌더링 된다.
return;
}
출처: http://hururuc.tistory.com/14 [Impossible is nothing]
=================================
=================================
=================================
출처: http://diehard98.tistory.com/entry/Stencil-Buffer%EC%9D%98-%EC%82%AC%EC%9A%A9%EB%B2%95
출처: http://diehard98.tistory.com/entry/Stencil-Buffer의-사용법 [Unavailable]
=================================
=================================
=================================
출처: http://egloos.zum.com/keugbang/v/5858105
OpenGL Stencil 기능을 이용한 HUD 심볼 그리기
- Depth and Stencils : https://open.gl/depthstencils
{
glEnable(GL_STENCIL_TEST);
// draw occlusion regions, such as airspeed box, to the stencil buffer
drawMode = Draw_Stencil;
glStencilFunc(GL_NEVER, 1, 0xFF); // only occlusion regions will be written
glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
glStencilMask(0xFF); // write to stencil buffer
glColorMask(false, false, false, false);
glClear(GL_STENCIL_BUFFER_BIT);
// first pass to draw stencil
renderHudScreen();
// draw actual symbols
drawMode = Draw_Normal;
glStencilFunc(GL_EQUAL, 0, 0xFF); // pass symbols outside occlusion regions
// texts inside occlusion regions are written if 1
glStencilMask(0x00); // don't write to stencil buffer
glColorMask(true, true, true, true);
// last pass to render symbols
renderHudScreen();
glDisable(GL_STENCIL_TEST);
}
// Occulusion control
typedef enum HudDrawMode {
Draw_Stencil,
Draw_Normal,
TotalHudDrawMode
} HudDrawMode;
extern HudDrawMode drawMode;
{
if (drawMode == Draw_Stencil)
{
glStencilFunc(GL_ALWAYS, 1, 0xFF); // was glColor4d(0,0,0,1)
drawOcclusionBox();
glStencilFunc(GL_NEVER, 1, 0xFF); // was glColor4d(0,1,0,1)
}
}
{
if (drawMode == Draw_Normal)
{
glStencilFunc(GL_ALWAYS, 1, 0xFF);
drawNumericTextBox();
glStencilFunc(GL_EQUAL, 0, 0xFF);
}
}
Stenciling을 위해 HUD 심볼을 그리는 renderHudScreen()을 두 번 실행하는 것에 성능 저하를 우려하였으나, 예상외로 큰 차이가 없이 거의 완벽하게 원하는 효과를 이룰 수 있었다.
=================================
=================================
=================================
'프로그래밍 관련 > 3D,2D DRAW 관련' 카테고리의 다른 글
OpenGL MFC, WIN32 배경투명하게 transparent, glClearColor, Clear 하기 (0) | 2017.07.14 |
---|---|
3D구현 - 2D 물체 겹침 표현하기 관련 (0) | 2017.07.12 |
OpenGL 을 window DC 에 따로 렌더링 하기 다른부분에 랜더링 관련 (0) | 2017.06.20 |
[OpenGL] GL_POLYGON_SMOOTH 안티 엘리어싱 적용후 네모난 사각형도형의 POLYGON 또는 GL_QUADS의 텍스쳐에 깨진 대각선 선이 보이는경우 (0) | 2017.06.08 |
OpenGL 안티 얼라이싱(Anti-Aliasing)과 멀티샘플링(MultiSample) 관련 (0) | 2017.06.08 |
댓글 영역