如何在背景图像上绘制(如在 GLPaint 中)并使用临时绘图?

Posted

技术标签:

【中文标题】如何在背景图像上绘制(如在 GLPaint 中)并使用临时绘图?【英文标题】:How can I draw (as in GLPaint) onto a background image, and with temporary drawings? 【发布时间】:2010-11-12 19:59:53 【问题描述】:

我正在为 iPad 编写一个 GLPaint 风格的绘图应用程序,但是我遇到了一个绊脚石。具体来说,我目前正在尝试实现两件事:

1) 可以绘制的背景图片。

2) 绘制临时形状的能力,例如您可能会画一条线,但最终形状只有在手指抬起后才会提交。

对于背景图像,我理解的想法是将图像绘制成 VBO,并在每次画线之前绘制它。这很好,但现在我需要添加绘制临时形状的能力......将 kEAGLDrawablePropertyRetainedBacking 设置为 YES(如在 GLPaint 中),临时显然不是临时的!将保留的支持属性设置为 NO 非常适合临时对象,但现在我之前的手绘线不再保留。

这里最好的方法是什么?我是否应该使用多个 EAGLLayer?我发现的所有文档和教程似乎都表明大多数事情都应该通过单层实现。他们还说,保留的支持几乎总是应该设置为“否”。有没有办法在这样的配置中运行我的应用程序?我尝试将每个绘图点存储到一个不断扩展的顶点数组中,以便每帧重绘,但由于要绘制的精灵数量众多,这不起作用。

我非常感谢对此提供任何帮助,因为我在网上搜索并没有找到任何东西!

【问题讨论】:

【参考方案1】:

我已经找到了解决这个问题的方法。最好的方法似乎是使用自定义帧缓冲区对象和渲染到纹理。在问这个问题之前我没有听说过这个,但它看起来对于 OpenGLer 的工具包来说是一个非常有用的工具!

对于那些可能想要做类似事情的人来说,这个想法是您创建一个 FBO 并将纹理附加到它(而不是渲染缓冲区)。然后,您可以绑定此 FBO 并像其他任何内容一样对其进行绘制,唯一的区别是绘图是在屏幕外渲染的。那么显示纹理所需要做的就是绑定主 FBO 并将纹理绘制到它(使用四边形)。

因此,在我的实现中,我使用了两个不同的 FBO,每个都附加了一个纹理 - 一个用于“保留”图像(用于手绘),另一个用于“临时”图像(用于临时绘图)。每次渲染一帧时,我首先绘制一个背景纹理(在我的例子中,我只使用了 Texture2D 类),然后绘制保留的纹理,如果需要,最后绘制划痕纹理。绘制临时形状时,所有内容都会渲染到划痕纹理,并且在每一帧开始时都会被清除。完成后,将划痕纹理绘制到保留的纹理上。

这里有一些可能对某人有用的sn-ps代码:

1) 创建帧缓冲区(为了节省空间,我这里只展示了几个!):

// ---------- DEFAULT FRAMEBUFFER ---------- //
// Create framebuffer.
glGenFramebuffersOES(1, &viewFramebuffer);
glBindFramebufferOES(GL_FRAMEBUFFER_OES, viewFramebuffer);

// Create renderbuffer.
glGenRenderbuffersOES(1, &viewRenderbuffer);
glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewRenderbuffer);

// Get renderbuffer storage and attach to framebuffer.
[context renderbufferStorage:GL_RENDERBUFFER_OES fromDrawable:layer];
glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_COLOR_ATTACHMENT0_OES, GL_RENDERBUFFER_OES, viewRenderbuffer);
glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_WIDTH_OES, &backingWidth);
glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_HEIGHT_OES, &backingHeight);

// Check for completeness.
status = glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES);
if (status != GL_FRAMEBUFFER_COMPLETE_OES) 
    NSLog(@"Failed to make complete framebuffer object %x", status);
    return NO;


// Unbind framebuffer.
glBindFramebufferOES(GL_FRAMEBUFFER_OES, 0);


// ---------- RETAINED FRAMEBUFFER ---------- //
// Create framebuffer.
glGenFramebuffersOES(1, &retainedFramebuffer);
glBindFramebufferOES(GL_FRAMEBUFFER_OES, retainedFramebuffer);

// Create the texture.
glColor4f(0.0f, 0.0f, 0.0f, 0.0f);
glGenTextures(1, &retainedTexture);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, retainedTexture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1024, 1024, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glBindTexture(GL_TEXTURE_2D, 0);

// Attach the texture as a renderbuffer.
glFramebufferTexture2DOES(GL_FRAMEBUFFER_OES, GL_COLOR_ATTACHMENT0_OES, GL_TEXTURE_2D, retainedTexture, 0);

// Check for completeness.
status = glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES);
if (status != GL_FRAMEBUFFER_COMPLETE_OES) 
    NSLog(@"Failed to make complete framebuffer object %x", status);
    return NO;


// Unbind framebuffer.
glBindFramebufferOES(GL_FRAMEBUFFER_OES, 0);

2) 绘制到渲染到纹理 FBO:

// Ensure that we are drawing to the current context.
[EAGLContext setCurrentContext:context];
glBindFramebufferOES(GL_FRAMEBUFFER_OES, retainedFramebuffer);
glViewport(0, 0, 1024, 1024);

// DRAWING CODE HERE

3) 将各种纹理渲染到主 FBO,并呈现:

glBindFramebufferOES(GL_FRAMEBUFFER_OES, viewFramebuffer);
glViewport(0, 0, backingWidth, backingHeight);
glClearColor(1.0f, 1.0f, 1.0f, 1.0f);       // Clear to white.
glClear(GL_COLOR_BUFFER_BIT);


glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);

[self drawBackgroundTexture];
[self drawRetainedTexture];
[self drawScratchTexture];

glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);


glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewRenderbuffer);
[context presentRenderbuffer:GL_RENDERBUFFER_OES];

例如,使用[self drawRetainedTexture] 绘制保留纹理将使用以下代码:

// Bind the texture.
glBindTexture(GL_TEXTURE_2D, retainedTexture);

// Destination coords.
GLfloat retainedVertices[] = 
    0.0,          backingHeight,    0.0,
    backingWidth, backingHeight,    0.0,
    0.0,          0.0,              0.0,
    backingWidth, 0.0,              0.0
;

// Source coords.
GLfloat retainedTexCoords[] = 
    0.0, 1.0,
    1.0, 1.0,
    0.0, 0.0,
    1.0, 0.0
;

// Draw the texture.
glVertexPointer(3, GL_FLOAT, 0, retainedVertices);
glTexCoordPointer(2, GL_FLOAT, 0, retainedTexCoords);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);

// Unbind the texture.
glBindTexture(GL_TEXTURE_2D, 0);

很多代码,但我希望对某人有所帮助。确实让我难过一阵子!

【讨论】:

以上是关于如何在背景图像上绘制(如在 GLPaint 中)并使用临时绘图?的主要内容,如果未能解决你的问题,请参考以下文章

无法在画布中设置背景图像

在具有透明背景的绘制文本上添加图案覆盖

c ++在位图上绘制图像并保存

OpenGL:在背景图像上使用蒙版绘制颜色

在python中绘制图像背景[重复]

HTML5 Canvas - 如何在图像背景上画一条线?