OpenGL ES สร้างเอฟเฟกต์ stencil โดยใช้พื้นผิว


10

สิ่งแวดล้อม

นี่คือสภาพแวดล้อมที่ฉันกำลังทำงานอยู่:

  • OpenGL ES 2.0
  • iPhone Simulator และ iPhone 4
  • iMac 27 "โดยใช้ NVIDIA GeForce GTX 680MX 2048 MB

หวังว่าจะช่วย

ปัญหา

เคยค้นหาสูงและต่ำจากหลายแหล่งและหลายไซต์รวมถึง Stackoverflow แต่ยังไม่มีเอฟเฟกต์ stencil ที่ใช้งานได้

สิ่งที่ฉันมีคือ:

การตั้งค่าฉาก stencil

สิ่งที่สีดำไม่ใช่รูปหลายเหลี่ยมแต่พื้นผิวถูกวาดลงบนสี่เหลี่ยมรูปสี่เหลี่ยมที่มีความกว้างและความสูงเท่ากับภาพพื้นหลัง

ฉันกำลังพยายามทำเอฟเฟกต์ stencil ที่พื้นหลังและผู้ชายสีเหลืองตัวเล็ก ๆ ควรมองเห็นได้ถ้ามันอยู่ในพื้นผิวของสีดำ

ในส่วนของฉันฉันมีสิ่งนี้:

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;
}

สำหรับการตั้งค่า Depth Stencil Buffer ของฉันฉันได้ตั้งค่าดังนี้:

-(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);
}

ตามเอกสารของ Apple (ซึ่งฉันคิดว่าล้าสมัย):

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 ในการกำหนดบัฟเฟอร์ "depth / stencil" ที่เรียกว่าในลิงก์ด้านบน แต่ให้ข้อผิดพลาดเดียวกันด้านล่าง

รหัสที่ฉันมีด้านบนคือวิธีที่คุณจะสร้าง stencil buffer

ในเมธอด 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);
    }
}

ข้อผิดพลาดที่ฉันได้รับเมื่อฉันแนบมันเหมือนที่แสดงด้านบนคือ:

ไม่สามารถสร้างวัตถุ framebuffer ที่สมบูรณ์ได้ 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];
}

ผลลัพธ์ของสิ่งนี้คือหน้าจอสีชมพูซึ่งหมายความว่าการตั้งค่าของฉันผิด:

ป้อนคำอธิบายรูปภาพที่นี่

ใครบ้างที่สามารถเปล่งแสงโปรดบ้าง


ไม่มีความคิดวิธีการแก้ปัญหาที่แน่นอน แต่ทำไมไม่ใช้ "stencil texture" ในขณะที่วาดผู้ชายตัวเล็กสีเหลืองและทิ้งถ้าค่า texel ไม่ตรงกัน?
Jari Komppa

ลายฉลุไม่ได้มีไว้สำหรับปกปิดชายร่างเล็ก แต่ก็เป็นจุดประสงค์เพื่อใช้ในการตรวจจับภูมิประเทศที่ชายร่างเล็กยืนอยู่ (ไม่ใช่ภาพพื้นหลังที่คุณเห็นด้านบน) ลองนึกถึงเกม Worm คุณมีพื้นหลังจากนั้นคุณมีภูมิประเทศตรงกลางที่มีลายฉลุเพื่อลบภูมิประเทศและตรวจจับการชนกันของผู้เล่นบนหน้าจอ (ชายร่างเล็กสีเหลือง)
Zhang

คำตอบ:


7

การแก้ไขปัญหา

ใช่เลย!!!

ตอนนี้ฉันมีความสุขแล้ว! : D

ตกลงในที่สุดฉันก็จัดการเพื่อให้ลายฉลุทำงานกับ texture :)

(เรียนรู้หลายสิ่งหลายอย่างตลอดทางเช่นเราสามารถตรวจสอบ color.alpha และใช้การละทิ้งเป็นวิธีลบพิกเซลโปร่งใสและ glBlend (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) เคล็ดลับล้าสมัย)

ดังนั้นสิ่งแรกที่ฉันสังเกตเห็นว่าฉันทำผิดก็คือการสร้างชื่อบัฟเฟอร์บัฟเฟอร์

ในซอร์สโค้ดด้านบนในคำถามของฉันฉันพิมพ์:

glGenBuffers(1, &depthStencilBuffer);

นี่ควรจะเป็น:

glGenRenderbuffers(1, &depthStencilBuffer);

d'โอ้!

ที่สองจากทั้งหมดฉันแสดงความคิดเห็นสำคัญที่สอง 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

ฉันยังแก้ไขซอร์สโค้ดอื่น ๆ อีกจำนวนหนึ่ง แต่ทั้งสองอย่างนั้นเป็นการเปลี่ยนแปลงหลักที่ทำให้มันทำงานได้

การเปลี่ยนแปลงที่เป็นประโยชน์บางอย่างช่วยให้ฉันแก้ปัญหาด้วย:

  • ฉันพบว่าฉันไม่จำเป็นต้องแนบบัฟเฟอร์ความลึกเข้ากับ framebuffer ของฉันเพื่อให้มันทำงานได้
  • ฉันไม่ต้องล้าง GL_DEPTH_BUFFER_BIT
  • ฉันเปลี่ยนเมธอด setupFrameBuffer () เป็นสิ่งนี้ด้วยเงื่อนไข if-else เพิ่มเติมเพื่อระบุข้อผิดพลาดที่เป็นไปได้อย่างชัดเจน

เมธอด 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.