0

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

enter image description here

genpfault
  • 51,148
  • 11
  • 85
  • 139
sjkm
  • 3,887
  • 2
  • 25
  • 43
  • Have you checked that stencil buffer exists when running on the device? – harism Jul 02 '14 at 20:57
  • Yes, check you `setEGLConfigChooser()` call, and make sure that you requested a stencil buffer. Some devices will automatically give you a stencil buffer when you request a depth buffer, because depth/stencil are often combined in the same buffer. But that's not the case on all devices. – Reto Koradi Jul 02 '14 at 21:15
  • Thank you both very much for your quick answers! I really appreciate your help. I already call view.setEGLConfigChooser(8, 8, 8, 8, 16, 0); in onSetContentView but sadly without success. Any other ideas? Btw: should I basically avoid using OpenGL stencil or is this a stable way to achieve what I want? – sjkm Jul 02 '14 at 21:22
  • The last argument of that `setEGLConfigChooser()` overload is `stencilSize`. Use 8 for the value of the last argument. – Reto Koradi Jul 02 '14 at 21:39
  • Thanks for getting back to me! I now call this.mRenderSurfaceView.setEGLConfigChooser(8, 8, 8, 8, 16, 8); but it still doesn't work? Is the use of stencils a good idea anyway? - Do they work without problems on most of the devices? Appreciate your help. Thank you – sjkm Jul 02 '14 at 21:52

1 Answers1

2

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:

Further, an implementation or context may not provide depth or stencil buffers. However, an OpenGL ES implementation must support at least one config with a depth bit depth.

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.

Reto Koradi
  • 53,228
  • 8
  • 93
  • 133
  • Hi Reto. Thank you so much for your detailed answer! Really interesting. I also thought that stencil was supported on all devices with ES 2.0, good to know that I was wrong. And yes you're right I cannot use scissors since I also want to rotate the "clip-rectangle". However seems I have to find another workaround to avoid misbehaviour on some devices that do not support stencil. Thank you for your time and effort! Have a nice day – sjkm Jul 03 '14 at 10:19