I am trying to use a sprite as clip-entity. This means I want that all it's child entities are clipped so that only the parts of the children that overlap the clip-entity (parent) are visible.
I'm trying to do that using the OpenGL stencil function. On my emulator I got it working, but on my Android phone it doesn't. What am I doing wrong?
GLES20.glEnable(GLES20.GL_STENCIL_TEST); GLES20.glClearStencil(0); GLES20.glStencilMask(1); GLES20.glClear(GLES20.GL_STENCIL_BUFFER_BIT); // pre draw GLES20.glStencilFunc(GLES20.GL_NEVER, 1, 1); GLES20.glStencilOp(GLES20.GL_REPLACE, GLES20.GL_KEEP, GLES20.GL_KEEP); // draw clip-entity (this) (sprite) this.draw(); // post draw GLES20.glStencilFunc(GLES20.GL_LESS, 2, 1); GLES20.glStencilOp(GLES20.GL_KEEP, GLES20.GL_KEEP, GLES20.GL_KEEP); // draw children of clip-entity (this) which should now be clipped this.drawChildren(); GLES20.glDisable(GLES20.GL_STENCIL_TEST);
<img alt="enter image description here" class="b-lazy" data-src="https://i.stack.imgur.com/1W0T8.png" data-original="https://i.stack.imgur.com/1W0T8.png" src="https://etrip.eimg.top/images/2019/05/07/timg.gif" />Answer1:
Using a stencil buffer for what you're describing sounds like a good approach.
You need to make sure that you request a framebuffer configuration with a stencil buffer. When using
GLSurfaceView on Android, this is done with one of the overloaded
setEGLConfigChooser() methods. The simplest form is the one that takes a bit depth for Red, Green, Blue, Alpha, Depth, and Stencil. If you also need a depth buffer, you can try combinations like these to get a config with stencil:
setEGLConfigChooser(8, 8, 8, 8, 24, 8); setEGLConfigChooser(8, 8, 8, 8, 16, 8); setEGLConfigChooser(8, 8, 8, 0, 24, 8); setEGLConfigChooser(8, 8, 8, 0, 16, 8); setEGLConfigChooser(5, 6, 5, 0, 24, 8); setEGLConfigChooser(5, 6, 5, 0, 16, 8);
To be even more thorough, you can also use the
setEGLConfigChooser() override that lets you register your own
EGLConfigChooser implementation. Using that, you can enumerate configuration, and implement your own logic to choose the one that best meets your need.
To verify that you indeed got a stencil buffer, you can use:
GLint stencilBits = 0; glGetIntegerv(GL_STENCIL_BITS, &stencilBits);
I thought that stencil support was standard, but the way I read the ES 2.0 spec, it's not required:<blockquote>
Further, an implementation or context may not provide depth or stencil buffers. However, an OpenGL ES implementation must support at least one conﬁg with a depth bit depth.</blockquote>
I believe at least relatively recent GPUs support stencil. If you have to get this working on a GPU that indeed does not provide stencil after you tried all of the above, things get more difficult. For the example you sketched, where you clip by a rectangle, that could of course simply be done by using a scissor rectangle. But I'm sure that you only used that for illustration.
Depending how much you use the depth buffer for your actual rendering, you could play some tricks with the depth buffer instead. For example, it could look like this:
glClearDepthf(0.0f); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glDisable(GL_DEPTH_TEST); // draw clip entity glEnable(GL_DEPTH_TEST); // draw children
The downside of this is that you need to disable depth testing for part of your drawing, which might be completely unacceptable if that drawing needs depth testing to render properly.