OpenGL ES는 텍스처를 사용하여 스텐실 효과를 만듭니다


10

환경

이것이 내가 일하고있는 환경입니다.

  • OpenGL ES 2.0
  • 아이폰 시뮬레이터 및 아이폰 4
  • NVIDIA GeForce GTX 680MX 2048MB를 사용하는 iMac 27 "

희망이 도움이됩니다.

문제

여러 소스 및 Stackoverflow를 포함한 여러 사이트에서 높고 낮은 검색을 수행했지만 작동하는 스텐실 효과는 얻지 못했습니다.

내가 가진 것은 이것입니다 :

스텐실 장면 설정

검은 색 'S' 는 다각형이 아니라 배경 이미지와 너비와 높이가 같은 사각형 쿼드에 그려진 텍스처입니다.

배경과 작은 노란색 사람이 검은 색 'S'텍스처 안에있는 경우에만 표시되어야하는 스텐실 효과를 시도하고 있습니다.

내 조각 셰이더에는 다음이 있습니다.

varying lowp vec4 destinationColor;

varying lowp vec2 TexCoordOut;
uniform sampler2D Texture;
uniform highp float TexScale;

void main()
{
    highp vec4 color = texture2D(Texture, TexCoordOut);

    if(color.a == 0.0)
    {
        discard;
    }

    gl_FragColor = color;
}

설정 깊이 스텐실 버퍼의 경우 다음과 같이 설정했습니다.

-(void)setupDepthStencilBuffer
{
    GLint width;
    GLint height;

    glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &width);
    glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &height);

    glGenBuffers(1, &depthStencilBuffer);
    glBindRenderbuffer(GL_RENDERBUFFER, depthStencilBuffer);
    glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8_OES, width, height);
    //glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_COMPONENT24_OES, width, height);

    NSLog(@"depthStencilBuffer = %d", depthStencilBuffer);
}

애플의 문서에 따르면 (구식이라고 생각합니다)

http://developer.apple.com/library/ios/#documentation/3DDrawing/Conceptual/OpenGLES_ProgrammingGuide/WorkingwithEAGLContexts/WorkingwithEAGLContexts.html#//apple_ref/doc/uid/TP40008793-CH103-SW1

GL_RGBA enum과 같은 일부는 Xcode로 입력하려고 할 때 존재하지 않습니다 (Apple이 그것을 제거하고 사용하지 않아야한다고 생각합니다).

또한 위 링크에서 소위 "깊이 / 스텐실"버퍼를 정의하는 Apple의 방법을 시도했지만 아래에서 동일한 오류가 발생했습니다.

위의 코드는 스텐실 버퍼를 만드는 방법입니다.

내 setupFrameBuffer () 메서드에서 다음과 같이 첨부합니다.

-(void)setupFrameBuffer
{
    GLuint frameBuffer;

    glGenBuffers(1, &frameBuffer);
    glBindFramebuffer(GL_FRAMEBUFFER, frameBuffer);
    glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, colorRenderBuffer);
    glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, depthStencilBuffer);

    GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);

    if(status != GL_FRAMEBUFFER_COMPLETE)
    {
        NSLog(@"failed to make complete framebuffer object %x", status);
    }
}

위와 같이 첨부 할 때 발생하는 오류는 다음과 같습니다.

완전한 프레임 버퍼 객체 8cd6를 만들지 못했습니다

내 렌더 방법에는 다음이 있습니다.

-(void)render:(CADisplayLink *)displayLink
{
    glClearColor(0.5f, 0.5f, 0.5f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);



    glClearStencil(0);
    glEnable(GL_STENCIL_TEST);

    // ----------------------------------------
    // Don't write to color or depth buffer
    // ----------------------------------------
    glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
    glDepthMask(GL_FALSE);

    glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);

    // ----------------------------------------
    // First set the alpha test so that
    // fragments greather than threshold
    // will pass thus will set nonzero
    // bits masked by 1 in stencil
    // ----------------------------------------
    glStencilFunc(GL_ALWAYS, 1, 1);

    // ----------------------------------------------------------------
    // Drawing our stencil
    // ----------------------------------------------------------------

    glBindBuffer(GL_ARRAY_BUFFER, bgVBO);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, bgIBO);

    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, stencilTexture);
    glUniform1i(textureUniform, 0);

    glVertexAttribPointer(positionSlot, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), 0);
    glVertexAttribPointer(colorSlot, 4, GL_FLOAT, GL_FALSE, sizeof(Vertex), (const void *)(sizeof(GLfloat) * 3));
    glVertexAttribPointer(texCoordSlot, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (const void *)(sizeof(GLfloat) * 7));

    glDrawElements(GL_TRIANGLE_FAN, sizeof(bgIndices)/sizeof(bgIndices[0]), GL_UNSIGNED_BYTE, 0);

    /*
    // -----------------------------------------
    // Second pass of the fragments less
    // or equal than the threshold will pass
    // thus will set zero bits masked by 1
    // in stencil
    // -----------------------------------------
    glStencilFunc(GL_ALWAYS, 0, 1);

    glBindBuffer(GL_ARRAY_BUFFER, bgVBO);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, bgIBO);

    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, stencilTexture);
    glUniform1i(textureUniform, 0);

    glVertexAttribPointer(positionSlot, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), 0);
    glVertexAttribPointer(colorSlot, 4, GL_FLOAT, GL_FALSE, sizeof(Vertex), (const void *)(sizeof(GLfloat) * 3));
    glVertexAttribPointer(texCoordSlot, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (const void *)(sizeof(GLfloat) * 7));

    glDrawElements(GL_TRIANGLE_STRIP, sizeof(bgIndices)/sizeof(bgIndices[0]), GL_UNSIGNED_BYTE, 0);
    */


    // ---------------------------------------------------
    // RE-ENABLING THE COLOR AND DEPTH MASK AGAIN
    // TO DRAW REST OF THE SCENE AFTER STENCIL
    // ---------------------------------------------------
    glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
    glDepthMask(GL_TRUE);




    Mat4 frustrumMatrix = [CameraMatrix createOrthographicMatrixUsingLeft:-(self.bounds.size.width / 2.0)
                                                                       Right:(self.bounds.size.width / 2.0)
                                                                      Bottom:-(self.bounds.size.height / 2.0)
                                                                         Top:(self.bounds.size.height / 2.0)
                                                                        Near:-1.0f
                                                                         Far:1.0f];

    glUniformMatrix4fv(projectionUniform, 1, 0, frustrumMatrix.matrix);

    glViewport(0, 0, self.bounds.size.width * self.contentScaleFactor, self.bounds.size.height * self.contentScaleFactor);

    // ----------------------------------------------------------------
    // Drawing our background first
    // ----------------------------------------------------------------

    glBindBuffer(GL_ARRAY_BUFFER, bgVBO);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, bgIBO);

    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, bgTexture);
    glUniform1i(textureUniform, 0);

    glVertexAttribPointer(positionSlot, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), 0);
    glVertexAttribPointer(colorSlot, 4, GL_FLOAT, GL_FALSE, sizeof(Vertex), (const void *)(sizeof(GLfloat) * 3));
    glVertexAttribPointer(texCoordSlot, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (const void *)(sizeof(GLfloat) * 7));

    glDrawElements(GL_TRIANGLE_FAN, sizeof(bgIndices)/sizeof(bgIndices[0]), GL_UNSIGNED_BYTE, 0);



    glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer);


    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, floorTexture);
    glUniform1i(textureUniform, 0);


    glVertexAttribPointer(positionSlot, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), 0);
    glVertexAttribPointer(colorSlot, 4, GL_FLOAT, GL_FALSE, sizeof(Vertex), (const void *)(sizeof(GLfloat) * 3));

    glVertexAttribPointer(texCoordSlot, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (const void *)(sizeof(GLfloat) * 7));

    glDrawElements(GL_TRIANGLE_FAN, sizeof(Indices)/sizeof(Indices[0]), GL_UNSIGNED_BYTE, 0);

    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    glEnable(GL_BLEND);

    [context presentRenderbuffer:GL_RENDERBUFFER];
}

이것의 결과는 분명히 분홍색 화면으로 설정이 잘못되었음을 의미합니다.

여기에 이미지 설명을 입력하십시오

누구든지 약간의 빛을 비출 수 있습니까?


정확한 문제를 해결하는 방법을 모르지만 작은 노란색 사람을 그리는 동안 "스텐실 텍스처"를 사용하고 텍셀 값이 일치하지 않으면 버리지 않는 이유는 무엇입니까?
Jari Komppa

스텐실은 작은 사람을 가리기위한 것이 아니라 작은 사람이 서있는 지형을 감지하는 데 사용하기위한 것입니다 (위의 배경 이미지가 아님). Worm 게임을 생각하고 배경을 얻은 다음 중간에 지형을가집니다. 스텐실로 가려져 지형을 지우고 화면의 플레이어 (작은 노란색 사람)의 충돌을 감지합니다.
Zhang

답변:


7

해결책

그럼 당연하지!!!

나는 지금 행복한 녀석이야! :디

좋아, 마침내 Stencil이 텍스처로 작업하게했습니다 :)

(또한 여러 가지를 배웠습니다. 예를 들어 color.alpha를 확인하고 투명 픽셀을 제거하는 방법으로 폐기를 사용하면 glBlend (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) 트릭이 더 이상 사용되지 않습니다)

그래서 내가 잘못 알아 차린 첫 번째 것은 내 스텐실 버퍼의 이름을 잘못 생성하는 것입니다.

내 질문에 위의 소스 코드에서 다음을 입력했습니다.

glGenBuffers(1, &depthStencilBuffer);

이것은 정말로해야합니다 :

glGenRenderbuffers(1, &depthStencilBuffer);

디오!

두 번째로, 호출해야 할 중요한 두 번째 glStencilFunc ()를 주석 처리했습니다.

glStencilFunc(GL_ALWAYS, 1, 1);
glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);

// draw black 'S' textured quad here

. . .

// -----------------------------------
// I WAS MISSING THIS IMPORTANT LINE
// -----------------------------------
glStencilFunc(GL_ALWAYS, 0, 1);
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);

/* draw the rest of the scene here (the background and yellow guy) */

최종 결과 :

여기에 이미지 설명을 입력하십시오

텍스처 기능 으로이 멋진 스텐실을 시도하는 다른 모든 사람들에게 도움이되기를 바랍니다 .D

또한 다른 많은 소스 코드를 수정했지만 그 두 가지가 주요 변경 사항이었습니다.

몇 가지 유용한 변경 사항으로 문제를 디버깅하는 데 도움이되었습니다.

  • 프레임 버퍼에 깊이 버퍼를 연결하지 않아도 작동한다는 것을 알았습니다.
  • 또한 GL_DEPTH_BUFFER_BIT를 지울 필요가 없습니다
  • 가능한 오류가있는 경우를 대비하여 여분의 if-else 조건으로 setupFrameBuffer () 메서드를 이것으로 변경했습니다.

새로운 setupFrameBuffer () 메소드 :

-(void)setupFrameBuffer
{
    GLuint frameBuffer;

    glGenBuffers(1, &frameBuffer);
    glBindFramebuffer(GL_FRAMEBUFFER, frameBuffer);
    glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, colorRenderBuffer);
    //glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depthStencilBuffer);
    glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, depthStencilBuffer);

    GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);

    if(status == GL_FRAMEBUFFER_COMPLETE)
    {
        NSLog(@"framebuffer complete");
        //NSLog(@"failed to make complete framebuffer object %x", status);
    }
    else if(status == GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT)
    {
        NSLog(@"incomplete framebuffer attachments");
    }
    else if(status == GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT)
    {
        NSLog(@"incomplete missing framebuffer attachments");
    }
    else if(status == GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS)
    {
        NSLog(@"incomplete framebuffer attachments dimensions");
    }
    else if(status == GL_FRAMEBUFFER_UNSUPPORTED)
    {
        NSLog(@"combination of internal formats used by attachments in thef ramebuffer results in a nonrednerable target");
    }
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.