c - Nested Stencil - OpenGL ES -
i have 2d node scene graph i'm trying 'nest' stencil clipping in.
i thinking when drawing stencil, increment pixel writes 1, , keep track of current 'layer' i'm on.
then when drawing, write pixel data color buffer if value of stencil @ pixel >= current layer #.
this code have now. doesn't quite work. messing up?
first call setupstencilformask(). draw stencil primitives. next, call setupstencilfordraw(). draw actual imagery when done layer, call disablestencil().
edit: updated solution. doesn't work individual items on same layer, otherwise fine. found great article on how pull off, although it's limited. http://cranialburnout.blogspot.com/2014/03/nesting-and-overlapping-translucent.html
// glclear(gl_stenicl_bit) @ start of each draw frame static int stencillayer = 0; void setupstencilformask(void) { if (stencillayer == 0) glenable(gl_stencil_test); glcolormask(gl_false, gl_false, gl_false, gl_false); glstencilfunc(gl_less, stencillayer, 0xff); glstencilop(gl_incr, gl_keep, gl_keep); glstencilmask(0xff); if (stencillayer == 0) glclear(gl_stencil_buffer_bit); stencillayer++; } void setupstencilfordraw() { glcolormask(gl_true, gl_true, gl_true, gl_true); glstencilfunc(gl_equal, stencillayer, 0xff); glstencilmask(0x00); } void disablestencil(void) { if (--stencillayer == 0) gldisable(gl_stencil_test); }
i have figured out way in libgdx. i'm not sure still need this, future reference here code:
/** * start cropping * * @param cropmask * mask plane */ public void startcropping(plane cropmask) { // check if there active masking group if (activecropmaskgroup == null) { // create new 1 activecropmaskgroup = new cropmaskinggroupdescriptor(cropmask); } else { // increase hierarchy level activecropmaskgroup.increasehierachy(cropmask); } } /** end cropping */ public void endcropping() throws illegalstateexception { // check if there active group mask if (activecropmaskgroup == null) { throw new illegalstateexception("call start cropping before this!"); } if (activecropmaskgroup.gethierachy() > 0) { activecropmaskgroup.decreasehierachy(); } else { // finish setup of crop data cropmaskgroups.add(activecropmaskgroup); activecropmaskgroup = null; } } /** crop registered planes cropping */ private void croprender(cropmaskinggroupdescriptor cropmaskgroupdescriptor) { // draw mask stencil buffer gdx.gl.glclear(gl20.gl_stencil_buffer_bit); // setup drawing stencil buffer gdx.gl20.glenable(gl20.gl_stencil_test); // number of registered hierarchy levels int hierarchylevels = cropmaskgroupdescriptor.getregisteredbatch().size(); // loop trough hierarchy (int hierarchylevel = 0; hierarchylevel < hierarchylevels; hierarchylevel++) { gdx.gl20.glstencilfunc(gl20.gl_always, 0x1, 0xffffffff); gdx.gl20.glstencilop(gl20.gl_incr, gl20.gl_incr, gl20.gl_incr); gdx.gl20.glcolormask(false, false, false, false); gdx.gl20.gldepthmask(false); // draw mask decal batch cropmaskbatch.add(((nativeplane) cropmaskgroupdescriptor.getcroppingmasks().get(hierarchylevel)).getplane()); cropmaskbatch.flush(); // fix stencil buffer, enable color buffer gdx.gl20.glcolormask(true, true, true, true); gdx.gl20.gldepthmask(true); gdx.gl20.glstencilop(gl20.gl_keep, gl20.gl_keep, gl20.gl_keep); // draw pattern has been drawn gdx.gl20.glstencilfunc(gl20.gl_lequal, hierarchylevel + 1, 0xffffffff); // loop trough registered masked layers , found 1 belongs // // current hierarchy level (int = 0; < cropmaskgroupdescriptor.getmaskedlayers().size(); i++) { if (cropmaskgroupdescriptor.getmaskedlayers().get(i).gethierarchyid() == hierarchylevel) { plane plane = cropmaskgroupdescriptor.getmaskedlayers().get(i).getmaskedplane(); cropmaskgroupdescriptor.getregisteredbatch().get(hierarchylevel).add(((nativeplane) plane).getplane()); } } cropmaskgroupdescriptor.getregisteredbatch().get(hierarchylevel).flush(); } gdx.gl20.gldisable(gl20.gl_stencil_test); }
and inner class inside renderer module.
/** * cropped layer descriptor * * @author veljko ilkic * */ private class cropmasklayerdescriptor {
/** layer needs masked */ private plane maskedplane; /** hierarchy level in belongs */ private int hierarchyid = 0; /** constructor 1 */ public cropmasklayerdescriptor(plane maskedplane, int hierarchyid) { this.maskedplane = maskedplane; this.hierarchyid = hierarchyid; } public plane getmaskedplane() { return maskedplane; } public int gethierarchyid() { return hierarchyid; } } /** * crop masking group descriptor class * * @author veljko ilkic * */ private class cropmaskinggroupdescriptor { /** crop mask */ private arraylist<plane> croppingmasks = new arraylist<plane>(); /** planes masked crop mask */ private arraylist<cropmasklayerdescriptor> maskedlayers = new arraylist<renderer.cropmasklayerdescriptor>(); /** batch drawing masked planes */ private arraylist<decalbatch> hierarchybatches = new arraylist<decalbatch>(); private int activehierarchylayerid = 0; /** constructor 1 */ public cropmaskinggroupdescriptor(plane toplevelcropmask) { // create batch top level hierarchy hierarchybatches.add(new decalbatch(new cameragroupstrategy(perspectivecamera))); // register top level crop mask croppingmasks.add(toplevelcropmask); } /** increase hierarchy level of group */ public void increasehierachy(plane hierarchycropmask) { activehierarchylayerid++; // create individual batch hierarchy level hierarchybatches.add(new decalbatch(new cameragroupstrategy(perspectivecamera))); // register crop mask current hierarchy level croppingmasks.add(hierarchycropmask); } /** decrease hierarchy group */ public void decreasehierachy() { activehierarchylayerid--; } /** current hierarchy level */ public int gethierachy() { return activehierarchylayerid; } /** register plane masking */ public void registerlayer(plane maskedplane) { hierarchybatches.get(activehierarchylayerid).add(((nativeplane) maskedplane).getplane()); maskedlayers.add(new cropmasklayerdescriptor(maskedplane, activehierarchylayerid)); } /** registered batched */ public arraylist<decalbatch> getregisteredbatch() { return hierarchybatches; } /** registered cropping masks */ public arraylist<plane> getcroppingmasks() { return croppingmasks; } /** layer should masked */ public arraylist<cropmasklayerdescriptor> getmaskedlayers() { return maskedlayers; } /** dispose */ public void dispose() { (int = 0; < hierarchybatches.size(); i++) { hierarchybatches.get(i).dispose(); hierarchybatches.set(i, null); } hierarchybatches.clear(); } }
hope helps.