OpenGL:如何实现“橡皮擦”工具?

Posted

技术标签:

【中文标题】OpenGL:如何实现“橡皮擦”工具?【英文标题】:OpenGL: How to implement an "eraser" tool? 【发布时间】:2008-11-15 03:11:30 【问题描述】:

我正在为 iPhone 开发一款涉及绘图/绘画机制的游戏,但我在尝试创建一个可以擦除已绘画内容的工具时遇到问题。

主要问题是绘制的背景不是纯色,而是静态图像或动画。我尝试在绘图中使用不同的混合选项和逻辑操作,但似乎没有任何效果。我是 OpenGL 的新手,所以我一定遗漏了一些东西。

有什么建议吗?

编辑:为了提供更多信息,我将纹理用于我的画笔并使用 glVertexPointer() 和 glDrawArrays() 来渲染它们。例如:

glBindTexture(GL_TEXTURE_2D, circleBrush);
glVertexPointer(3, GL_FLOAT, 0, verts);
glTexCoordPointer(2, GL_FLOAT, 0, coords);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);

编辑 2:不幸的是,模板缓冲区在 iPhone 上不可用。 : (

编辑 3:帧缓冲对象在 iPhone 上可用,这就是我所走的路。我还没有完全实现它,但到目前为止它看起来就像我想要的那样工作。谢谢大家!

【问题讨论】:

你能实现这个吗?我被困了好几天,似乎找不到解决办法。 【参考方案1】:

在您的场景上绘制一个全屏纹理四边形。当用户绘制笔触时,使用glTexSubImage2D 更新您的纹理。

glReadPixels/glDrawPixels 很慢。

使用 FrameBufferObjects 更好,但我怀疑 iPhone 上是否提供此扩展(再说一次,我不确定,所以不妨试试)。 FBO 允许您直接绘制到纹理中,就好像它是另一个渲染上下文一样。

【讨论】:

你有这些代码示例吗?将不胜感激【参考方案2】:

Stencil Buffer 肯定是解决此问题的最佳方法...您将节省时间、cpu 和潜在问题...,它们在 Iphone 上可用,您只需创建一个 OpenGLlES 2.0 表面(不是 gles 1.0或 1.1)。

        //Turn off writing to the Color Buffer and Depth Buffer
        //We want to draw to the Stencil Buffer only
        glColorMask(false, false, false, false);
        glDepthMask(false);

        //Enable the Stencil Buffer
        glEnable(GL_STENCIL_TEST);

        //Set 1 into the stencil buffer
        glStencilFunc(GL_ALWAYS, 1, 0xFFFFFFFF);
        glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);

        //CALL YOUR DRAWING METHOD HERE


        //Turn on Color Buffer and Depth Buffer
        glColorMask(true, true, true, true);
        glDepthMask(true);

        //Only write to the Stencil Buffer where 1 is set
        glStencilFunc(GL_EQUAL, 1, 0xFFFFFFFF);
        //Keep the content of the Stencil Buffer
        glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);

        //CALL OUR DRAWING METHOD AGAIN

查找此示例: http://www.opengl.org/resources/code/samples/glut_examples/examples/stenciltst.c

这是视觉效果: http://www.opengl.org/resources/code/samples/glut_examples/examples/stenciltst.jpg

我已经在安卓和 iPhone 上实现了相同类型的橡皮擦工具,它就像一个魅力!

祝你好运!

干杯!

【讨论】:

这是一个很好的技术,我非常接近实现它,但是,我不能删除以前的绘制内容,而是只能在模板缓冲区中的图元内绘制。有什么想法可以解决这个问题吗?【参考方案3】:

您没有提供太多信息,但我假设您将他们“绘制”的任何内容存储到缓冲区中,然后像这样在屏幕上绘制:

 glWindowPos2i(X, Y);
 glDrawPixels(drawing->Width, drawing->Height, drawing->Format, 
              GL_UNSIGNED_BYTE, drawing->ImageData);

使用绘图->ImageData 作为缓冲区。你可以做的是有一个单独的背景缓冲区并首先绘制它。然后擦除工具将简单地将绘图缓冲区变白(将所有值,包括 alpha,一直向上)。

要使此解决方案起作用,您必须打开混合并关闭深度测试

 glEnable(GL_BLEND);
 glDisable(GL_DEPTH_TEST);
 glWindowPos2i(X, Y);
 //background never changes
 glDrawPixels(background->Width, background->Height, background->Format, 
              GL_UNSIGNED_BYTE, background->ImageData);
 glWindowPos2i(X, Y);
 glDrawPixels(drawing->Width, drawing->Height, drawing->Format, 
              GL_UNSIGNED_BYTE, drawing->ImageData);
 glEnable(GL_DEPTH_TEST);
 glDisable(GL_BLEND);

这就是你要找的吗?

【讨论】:

【参考方案4】:

您可以使用模板缓冲区进行此操作。模板缓冲区是一个特殊的缓冲区,它保存每个像素的信息,类似于深度缓冲区。与深度缓冲区不同,您可以决定在绘制时如何更改模板缓冲区以及它如何影响是否绘制到颜色缓冲区的决定。为此,您可以在任何绘图操作之前设置特定状态。这是你要做的:

在用户擦除时,将“擦除”区域绘制到 模板缓冲区(使用 StencilOp,见下文)。您可以使用任何 GL 图纸 功能。

在每个渲染过程中,首先绘制 背景(如果你愿意,你可以 在这里使用负模板测试 只画“擦除”的部分 背景 - 可能会增加 性能)。

然后,为所有用户启用模板测试 其他绘图操作。 OpenGL 然后只会吸引到 “未擦除”区域。

设置模板缓冲区如何影响绘图(或不影响绘图)的函数: glStencilFunc()

设置模板缓冲区本身如何受绘图影响的函数: glStencilOp()

进一步的解释,也包括这些的论点: http://www.opengl.org/resources/code/samples/sig99/advanced99/notes/node117.html

【讨论】:

这本来是一个完美的解决方案,但不幸的是,Stencil Buffers 在 iPhone 上不可用。【参考方案5】:

使用 glBlendFunc(GL_ONE, GL_ZERO) 模式可以听清楚。

【讨论】:

以上是关于OpenGL:如何实现“橡皮擦”工具?的主要内容,如果未能解决你的问题,请参考以下文章

如何撤消 HTML5 Canvas 绘图应用程序的橡皮擦操作

OpenGL:如何实现相机后视图

opengl中如何实现图象的缩放

cdr橡皮擦怎么越擦越多

我的OpenGL学习进阶之旅强烈推荐一款强大的 Android OpenGL ES 调试工具 GAPID并展示实战操作演练一步一步看如何使用GAPID调试工具

如何仅将部分 OpenGL 代码实现到使用 SDL 编写的游戏中?