OpenGL ES membuat efek stensil menggunakan tekstur

10

Lingkungan Hidup

Ini adalah lingkungan tempat saya bekerja:

  • OpenGL ES 2.0
  • iPhone Simulator & iPhone 4
  • iMac 27 "menggunakan NVIDIA GeForce GTX 680MX 2048 MB

Semoga itu bisa membantu.

Masalah

Telah mencari tinggi dan rendah dari berbagai sumber dan beberapa situs termasuk Stackoverflow tetapi belum ada efek stensil yang berfungsi.

Apa yang saya miliki adalah ini:

pengaturan adegan stensil

Benda hitam 'S' bukan poligon tetapi tekstur yang digambar di atas segi empat yang memiliki lebar dan tinggi yang sama dengan gambar latar belakang.

Saya mencoba untuk melakukan efek stensil di mana latar belakang dan orang kuning kecil seharusnya hanya terlihat jika itu dalam tekstur 'S' hitam itu.

Dalam shader fragmen saya, saya punya ini:

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

Untuk penyetelan Depth Stencil Buffer setup saya, saya telah mengaturnya seperti ini:

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

Menurut dokumentasi Apple (yang menurut saya sudah ketinggalan zaman):

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

Perhatikan beberapa hal seperti GL_RGBA enum tidak ada ketika saya mencoba mengetiknya di Xcode (saya pikir Apple harus menghapusnya dan membuatnya usang).

Saya juga telah mencoba cara Apple untuk mendefinisikan apa yang disebut "kedalaman / stensil" buffer di tautan di atas tetapi itu memberikan kesalahan yang sama di bawah ini.

Kode yang saya miliki di atas adalah bagaimana Anda akan membuat buffer stensil.

Dalam metode setupFrameBuffer () saya, saya lampirkan seperti ini:

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

Kesalahan yang saya dapatkan ketika saya lampirkan seperti yang ditunjukkan di atas adalah:

gagal membuat objek framebuffer lengkap 8cd6

Untuk metode render saya, saya punya ini:

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

Hasil dari ini jelas merupakan layar merah muda yang berarti pengaturan saya salah:

masukkan deskripsi gambar di sini

Adakah yang bisa menjelaskan?

Zhang
sumber
Tidak tahu bagaimana menyelesaikan masalah yang sebenarnya, tetapi mengapa tidak hanya menggunakan "tekstur stensil" saat menggambar pria kuning kecil, dan membuang jika nilai texel tidak cocok?
Jari Komppa
Stensil ini bukan untuk menutupi si kecil, itu agak untuk digunakan dalam mendeteksi medan tempat si kecil berdiri (bukan gambar latar belakang yang Anda lihat di atas). Pikirkan permainan Worm, Anda memiliki latar belakang, lalu Anda memiliki medan di tengah, yang ditutupi oleh stensil untuk menghapus medan dan mendeteksi tabrakan untuk para pemain di layar (lelaki kuning kecil).
Zhang

Jawaban:

7

Solusinya

Tentu saja!!!

Saya senang sekali sekarang! : D

OK, saya akhirnya berhasil membuat Stencil bekerja dengan tekstur :)

(juga mempelajari sejumlah hal di sepanjang jalan, misalnya kita dapat memeriksa color.alpha dan menggunakan discard sebagai cara untuk menghapus piksel transparan dan glBlend (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) trik menjadi usang)

Jadi hal pertama yang saya perhatikan salah adalah membuat nama untuk buffer stensil saya.

Dalam kode sumber di atas dalam pertanyaan saya, saya telah mengetik:

glGenBuffers(1, &depthStencilBuffer);

Ini harus benar-benar:

glGenRenderbuffers(1, &depthStencilBuffer);

d'oh!

Kedua, saya berkomentar tentang glStencilFunc () kedua yang penting yang perlu dipanggil:

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) */

Hasil akhir:

masukkan deskripsi gambar di sini

Harapan yang membantu semua orang yang mencoba stensil keren ini dengan fitur tekstur: D

Saya juga memodifikasi sejumlah kode sumber lain tetapi kedua adalah perubahan utama yang membuatnya berfungsi.

Beberapa perubahan bermanfaat membantu saya men-debug masalah juga:

  • Saya telah menemukan bahwa saya tidak perlu melampirkan buffer kedalaman ke framebuffer saya untuk membuatnya berfungsi
  • Saya juga tidak perlu menghapus GL_DEPTH_BUFFER_BIT
  • Saya mengubah metode setupFrameBuffer () menjadi ini dengan kondisi if-else tambahan untuk dengan jelas menunjukkan kepada saya kemungkinan kesalahan.

Metode setupFrameBuffer () baru:

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