使用模板测试来剪辑在OpenGL-ES中绘制的项目

时间:2015-10-04 03:31:30

标签: android canvas opengl-es

我目前正在开发Android的OpenGL项目。现在,我正在尝试使用模板缓冲区为绘图时要限制的项目创建“剪辑”。我的问题是我希望能够创建一个剪辑形状,然后“删除”该剪裁形状的部分(很像在Android Canvas中使用Region.Op.UNION调用clipPath(),然后是另一个clipPath( )使用Region.Op.DIFFERENCE调用

基本上,我的意图是只使用值为1的模板缓冲位来绘制屏幕的某些部分。因此,据我所知,我应该能够使用不同的glStencilOp函数绘制到模板缓冲区,这将允许我在某些区域用1来绘制,然后可能在以后用这些区域的部分默认值0替换它们。

作为一个示例测试,我尝试使用类似的东西在屏幕上剪辑“甜甜圈”(注意:我使用LibGDX开源项目中的ShapeRenderer来绘制形状):

// Beginning LibGDX shapeRenderer.
shapeRenderer.begin(ShapeType.FILLED);

gl20.glClear(GL20.GL_STENCIL_BUFFER_BIT); // Clear stencil buffer.
gl20.glEnable(GL20.GL_STENCIL_TEST); // Enable stencil test.
gl20.glColorMask(false, false, false, false); // Disable color drawing.
gl20.glStencilMask(0xFF); // Set 0xFF as stencil mask.

// Have stencil test always fail, replace drawn stencil bits with 1.
gl20.glStencilFunc(GL20.GL_NEVER, 1, 0xFF); 
gl20.glStencilOp(GL20.GL_REPLACE, GL20.GL_KEEP,GL20.GL_KEEP);

/* Draw outer circle, libGDX call. 600px radius. */
shapeRenderer.circle(screenRect.width() / 2, screenRect.height() / 2, 600);

// Now, replace newly drawn stencil bits with 0.
gl20.glStencilOp(GL20.GL_ZERO, GL20.GL_KEEP, GL20.GL_KEEP);

/* Draw inner circle, libGDX call. 200px radius. */
shapeRenderer.circle(screenRect.width() / 2, screenRect.height() / 2, 200);

// Re-enable color drawing. 
gl20.glColorMask(true, true, true, true);
// Now, items drawn will be confined to areas with '1' stencil bit.
gl20.glStencilFunc(GL20.GL_EQUAL, 1, 0xFF);
gl20.glStencilOp(GL20.GL_KEEP, GL20.GL_KEEP, GL20.GL_KEEP);
gl20.glStencilMask(0x00);

/* Draw a filled, colored rectangle to the entire screen. */
shapeRenderer.setColor(Color.RED);
shapeRenderer.drawRect(screenRect);

shapeRenderer.end();

现在,据我所知,这会导致画面上出现彩色的“甜甜圈”;但是,如上面的代码所示,屏幕上没有任何内容。

但是,如果我只剪辑外圈(而不是试图摆脱内圈的“gl20.glStencilOp(GL20.GL_ZERO,GL20.GL_KEEP,GL20.GL_KEEP)”调用和随后的内圈画,一个圆圈,预定外圆的大小在屏幕上绘制。

我查看了我能找到的文档,到目前为止我读过的所有内容都表明这应该有效。这里有什么我想念的吗?非常感谢任何帮助。

3 个答案:

答案 0 :(得分:1)

我发现了问题;我在ShapeRenderer中的一个begin()和end()调用之间绘制了两个圆圈。因此,在end()调用时绘制了两个圆。这意味着形状甚至没有绘制到模板缓冲区,因为我在end()调用之前调用了这些函数:

// Now, items drawn will be confined to areas with '1' stencil bit.
gl20.glStencilFunc(GL20.GL_EQUAL, 1, 0xFF);
gl20.glStencilOp(GL20.GL_KEEP, GL20.GL_KEEP, GL20.GL_KEEP);
gl20.glStencilMask(0x00);

如果我确保在绘制每个形状之前使用begin()调用和end()调用,它将在预期时间绘制,因此确保它将预期值绘制到模板缓冲区。

答案 1 :(得分:0)

逻辑看起来不错 - 你在绘制几何图形?

猜测,根据您在不渲染内部圆圈时看到外部圆圈呈现的事实,看起来您在两种情况下都使用外部圆形几何体(因此您设置了所有模具值,然后清除所有这些值,因此当您来到矩形时,没有任何模板值为1。

答案 2 :(得分:0)

我在WebGL中实现了您的解决方案,它运行正常(试一试):

http://ezekiel.vancouver.wsu.edu/~cs442/stackoverflow/stencil.html

以下是我模仿您的代码的方式:

    gl.enable(gl.STENCIL_TEST);

    gl.colorMask(false, false, false, false);
    gl.stencilMask(0xFF);
    gl.stencilFunc(gl.NEVER, 1, 0xFF);
    gl.stencilOp(gl.REPLACE, gl.KEEP, gl.KEEP);
    fillCircle(1); // radius of 1 maps to canvas width

    gl.stencilOp(gl.ZERO, gl.KEEP, gl.KEEP);
    fillCircle(0.333);

    gl.colorMask(true, true, true, true);
    gl.stencilFunc(gl.EQUAL, 1, 0xFF);
    gl.stencilOp(gl.KEEP, gl.KEEP, gl.KEEP);
    gl.stencilMask(0x00);

    gl.uniform3fv(program.color, [1, 0, 0]);
    fillCircle(3);  // fill whole canvas width red

你会发现你得到了你想要的东西。