使用OpenGL ES 2.0进行模板绘制

4
我正在尝试找到一种方法来剪切背景纹理的某个区域,使得该背景不会在屏幕上渲染出某个自定义图案。例如: enter image description here
这个正方形可以是任何图案。 我正在使用帧缓冲对象和模板缓冲来实现这种效果。以下是代码:
fbo.begin();
//Disables ColorMask and DepthMask so that all the rendering is done on the Stencil Buffer
Gdx.gl20.glColorMask(false, false, false, false);
Gdx.gl20.glDepthMask(false);
Gdx.gl20.glEnable(GL20.GL_STENCIL_TEST);
Gdx.gl20.glStencilFunc(GL20.GL_ALWAYS, 1, 0xFFFFFFFF);
Gdx.gl20.glStencilOp(GL20.GL_REPLACE, GL20.GL_REPLACE, GL20.GL_REPLACE);

stage.getSpriteBatch().begin();
rHeart.draw(stage.getSpriteBatch(), 1); //Draws the required pattern on the stencil buffer

//Enables the ColorMask and DepthMask to resume normal rendering
Gdx.gl20.glColorMask(true, true, true, true);
Gdx.gl20.glDepthMask(true);

Gdx.gl20.glStencilFunc(GL20.GL_EQUAL, 1, 0xFFFFFFFF);
Gdx.gl20.glStencilOp(GL20.GL_KEEP, GL20.GL_KEEP, GL20.GL_KEEP);

background.draw(stage.getSpriteBatch(), 1); //Draws the background such that the background is not rendered on the required pattern, leaving that area black.

stage.getSpriteBatch().end();
Gdx.gl20.glDisable(GL20.GL_STENCIL_TEST);
fbo.end();

然而,这根本不起作用。我应该如何使用模板缓冲来实现这一点?我还遇到了一些困难,无法理解glStencilFunc和glStencilOp。如果有人能够阐明这两个问题,那将非常有帮助。
更新:我还尝试使用glColorMask生成类似的东西。以下是代码:
Gdx.gl20.glClearColor(0, 0, 0, 0);
stage.draw();
FrameBuffer.clearAllFrameBuffers(Gdx.app);
fbo1.begin();
Gdx.gl20.glClearColor(0, 0, 0, 0);
batch.begin();
rubber.draw(batch, 1);
Gdx.gl20.glColorMask(false, false, false, true);
coverHeart.draw(batch, 1);
Gdx.gl20.glColorMask(true, true, true, false);
batch.end();        
fbo1.end();

toDrawHeart = new Image(new TextureRegion(fbo1.getColorBufferTexture()));
batch.begin();
toDrawHeart.draw(batch, 1);
batch.end();

这段代码产生了这样的结果: enter image description here 而不是像这样:(忽略窗口大小和颜色) enter image description here 注意:我正在使用libgdx库。

@Tim 这意味着正在渲染的背景不包含任何已经被剪切出来的“图案”。这意味着我得到的是完全粉色的屏幕,而不是一个带有黑色框的粉色屏幕(如上所示)。 - Rafay
我不确定这个。那么作为替代方案,我应该尝试什么呢?你为什么认为SpriteBatch可能无法与模板缓冲区一起使用? - Rafay
这些绘制方法实际上并不会绘制任何东西,而是将顶点坐标、颜色和纹理坐标存储在一个数组中。在调用end()方法时,会调用renderMesh()方法。还有其他看起来很奇怪的renderMesh()调用。所以我认为即使代码改变了OpenGL ES状态,唯一重要的状态是在end()调用时处于活动状态的那个。 - Stefan Hanke
你的 rHeart.draw 调用是如何在模板缓冲区上绘制图案的? - rotoglup
@rotoglup 我正在为桌面应用程序做这个:`public class Main { public static void main(String[] args) { JoglApplicationConfiguration cfg = new JoglApplicationConfiguration(); cfg.title = "FBOTest"; cfg.useGL20 = true; cfg.width = 720; cfg.height = 480; cfg.stencil = 8; new JoglApplication(new FBOTestGame(), cfg); } }` 我认为这就是你所说的GL上下文。在这里查看:http://libgdx.l33tlabs.org/docs/api/com/badlogic/gdx/scenes/scene2d/ui/Image.html此外,这是Image类的API实现:https://gist.github.com/2943895 - Rafay
显示剩余6条评论
3个回答

5

在向SpriteBatch绘制时,状态更改将被忽略,直到调用end()方法。如果您想在SpriteBatch中使用模板缓冲,则需要分开批处理绘图。还有一件事,我没有提到FBOs,但这不应该有任何影响。

@Override
public void create() {      
    camera = new OrthographicCamera(1, 1);
    batch = new SpriteBatch();

    texture = new Texture(Gdx.files.internal("data/badlogic.jpg"));
    texture.setFilter(TextureFilter.Linear, TextureFilter.Linear);

    TextureRegion region = new TextureRegion(texture, 0, 0, 256, 256);

    sprite = new Sprite(region);
    sprite.setSize(1f, 1f);
    sprite.setPosition(-0.5f, -0.5f);

    spriteUpsideDown = new Sprite(new TextureRegion(texture, 1f, 1f, 0f, 0f));
    spriteUpsideDown.setSize(1f, 1f);
    spriteUpsideDown.setPosition(-0.5f, -0.5f);

    pattern = new Sprite(region);
    pattern.setSize(0.5f, 0.5f);
    pattern.setPosition(-0.25f, -0.25f);

    << Set Input Processor >>
}

输入处理器允许通过键盘(在桌面上使用libgdx)设置两个布尔标志breakBatch1breakBatch2,用于中断SpriteBatch绘制。
@Override
public void render() {      
    Gdx.gl.glClearColor(1, 1, 1, 1);
    Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_STENCIL_BUFFER_BIT);
    batch.setProjectionMatrix(camera.combined);

    // setup drawing to stencil buffer
    Gdx.gl20.glEnable(GL20.GL_STENCIL_TEST);
    Gdx.gl20.glStencilFunc(GL20.GL_ALWAYS, 0x1, 0xffffffff);
    Gdx.gl20.glStencilOp(GL20.GL_REPLACE, GL20.GL_REPLACE, GL20.GL_REPLACE);
    Gdx.gl20.glColorMask(false, false, false, false);

    // draw base pattern
    batch.begin();
    pattern.draw(batch);

    if(breakBatch1) { batch.end(); batch.begin(); }

    // fix stencil buffer, enable color buffer
    Gdx.gl20.glColorMask(true, true, true, true);
    Gdx.gl20.glStencilOp(GL20.GL_KEEP, GL20.GL_KEEP, GL20.GL_KEEP);

    // draw where pattern has NOT been drawn
    Gdx.gl20.glStencilFunc(GL20.GL_NOTEQUAL, 0x1, 0xff);
    sprite.draw(batch);

    if(breakBatch2) { batch.end(); batch.begin(); }

    // draw where pattern HAS been drawn.
    Gdx.gl20.glStencilFunc(GL20.GL_EQUAL, 0x1, 0xff);
    spriteUpsideDown.draw(batch);
    batch.end();
}

这对我完全没有用。它创建了一个矩形遮罩,完全不考虑精灵的 Alpha 值。 - DKIT

2

Gdx.gl20.glStencilFunc(GL20.GL_REPLACE, GL20.GL_REPLACE, GL20.GL_REPLACE);

这不是正确的glStencilFunc参数。我认为你在这里想使用glStencilOp。

你需要在代码中使用glGetError,它会提示你这些类型的错误。


@Spoilt:你使用glGetError吗? - Tim
哦,那我就不确定了。虽然我对模板缓冲区没有经验,但我对它的工作原理有相当好的了解,而你所写的代码看起来很合理。 - Tim

1

我认为您的问题在于,您的初始GL_REPLACE模板操作被应用于rHeart.draw绘制的所有像素,而不考虑四边形上应用的任何纹理的形状。

因此,模板值被应用于四边形的每个像素,这导致了您的问题。

如果应用于四边形的纹理具有alpha通道,由于不支持GL_ALPHA_TEST,您可以设置您的着色器来丢弃完全透明的像素,防止它们被绘制到模板缓冲区中。


1
OpenGL ES 2.0 实现不允许 GL_ALPHA_TEST 和 glAlphaFunc。 - Rafay

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接