在GLKit视图中实现持久性效果

时间:2014-12-05 13:02:58

标签: ios ipad opengl-es

我设置了一个GLKit视图来绘制一个实心形状,一条线和一组点,这些点都会改变每一帧。我的drawInRect方法的基础是:

- (void)glkView:(GLKView *)view drawInRect:(CGRect)rect
{
    glClear(...);
    glBufferData(...);
    glEnableVertexAttribArray(...);
    glVertexAttribPointer(...);

    // draw solid shape
    glDrawArrays(GL_TRIANGLE_STRIP, ...);

    // draw line
    glDrawArrays(GL_LINE_STRIP, ...);

    // draw points
    glDrawArrays(GL_POINTS, ...);
}

这很好用;每个阵列包含大约2000个点,但我的iPad似乎没有问题以60fps渲染它。

现在的问题是,我希望线条随着时间的推移逐渐消失,而不是随着下一帧消失,产生持久性或荧光效果。坚固的形状和点不能留下来,只有线条。

我尝试过蛮力方法(在Apple的示例项目aurioTouch中使用):存储最后100帧的数据并每帧绘制所有100行,但这太慢了。使用这种方法,我的iPad无法渲染超过10fps。

所以我的问题是:我可以使用某种类型的帧或渲染缓冲区来更有效地实现这一点,这些帧会累积前一帧的颜色吗?由于我使用的是GLKit,我以前不必直接处理这些事情,因此对它们不太了解。我已经读过积累缓冲区,它似乎做了我想要的,但我听说它们非常慢,无论如何我无法判断它们是否存在于OpenGL ES 3中,更不用说了如何使用它们。

我想象以下内容(在设置某种存储缓冲区后):

- (void)glkView:(GLKView *)view drawInRect:(CGRect)rect
{
    glClear(...);
    glBufferData(...);
    glEnableVertexAttribArray(...);
    glVertexAttribPointer(...);

    // draw solid shape
    glDrawArrays(GL_TRIANGLE_STRIP, ...);

    // draw contents of storage buffer

    // draw line
    glDrawArrays(GL_LINE_STRIP, ...);

    // multiply the alpha value of each pixel in the storage buffer by 0.9 to fade

    // draw line again, this time into the storage buffer

    // draw points
    glDrawArrays(GL_POINTS, ...);
}

这可能吗?我需要使用哪些命令(特别是组合存储缓冲区的内容并更改其alpha)?这可能实际上比蛮力方法更有效吗?

1 个答案:

答案 0 :(得分:1)

我最终通过渲染到纹理来实现所需的结果,例如here所述。基本的想法是设置一个自定义的帧缓冲区并附加一个纹理 - 然后我渲染我想要持久存储到这个帧缓冲区的行(不清除它),并将整个帧缓冲区作为纹理渲染到默认的帧缓冲区中(清除它)每一帧)。我没有清除自定义帧缓冲区,而是在整个屏幕上渲染一个略微不透明的四边形,使之前的内容每帧都淡出一点。

相关代码如下;设置帧缓冲区和持久性纹理是在init方法中完成的:

    // vertex data for fullscreen textured quad (x, y, texX, texY)
    GLfloat persistVertexData[16] = {-1.0, -1.0, 0.0, 0.0,
                                 -1.0, 1.0, 0.0, 1.0,
                                 1.0, -1.0, 1.0, 0.0,
                                 1.0, 1.0, 1.0, 1.0};

    // setup texture vertex buffer
    glGenBuffers(1, &persistVertexBuffer);
    glBindBuffer(GL_ARRAY_BUFFER, persistVertexBuffer);
    glBufferData(GL_ARRAY_BUFFER, sizeof(persistVertexData), persistVertexData, GL_STATIC_DRAW);

    // create texture for persistence data and bind
    glGenTextures(1, &persistTexture);
    glBindTexture(GL_TEXTURE_2D, persistTexture);

    // provide an empty image
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 2048, 1536, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);

    // set texture parameters
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

    // create frame buffer for persistence data
    glGenFramebuffers(1, &persistFrameBuffer);
    glBindFramebuffer(GL_FRAMEBUFFER, persistFrameBuffer);

    // attach render buffer
    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, persistTexture, 0);

    // check for errors
    NSAssert(glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE, @"Error: persistence framebuffer incomplete!");

    // initialize default frame buffer pointer
    defaultFrameBuffer = -1;

并在glkView:drawInRect:方法中:

// get default frame buffer id
if (defaultFrameBuffer == -1)
    glGetIntegerv(GL_FRAMEBUFFER_BINDING, &defaultFrameBuffer);

// clear screen
glClear(GL_COLOR_BUFFER_BIT);


// DRAW PERSISTENCE

// bind persistence framebuffer
glBindFramebuffer(GL_FRAMEBUFFER, persistFrameBuffer);

// render full screen quad to fade
glEnableVertexAttribArray(...);
glBindBuffer(GL_ARRAY_BUFFER, persistVertexBuffer);
glVertexAttribPointer(...);
glUniform4f(colorU, 0.0, 0.0, 0.0, 0.01);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);

// add most recent line
glBindBuffer(GL_ARRAY_BUFFER, dataVertexBuffer);
glVertexAttribPointer(...);
glUniform4f(colorU, color[0], color[1], color[2], 0.8*color[3]);
glDrawArrays(...);

// return to normal framebuffer
glBindFramebuffer(GL_FRAMEBUFFER, defaultFrameBuffer);

// switch to texture shader
glUseProgram(textureProgram);

// bind texture
glBindTexture(GL_TEXTURE_2D, persistTexture);
glUniform1i(textureTextureU, 0);

// set texture vertex attributes
glBindBuffer(GL_ARRAY_BUFFER, persistVertexBuffer);
glEnableVertexAttribArray(texturePositionA);
glEnableVertexAttribArray(textureTexCoordA);
glVertexAttribPointer(self.shaderBridge.texturePositionA, 2, GL_FLOAT, GL_FALSE, 4*sizeof(GLfloat), 0);
glVertexAttribPointer(self.shaderBridge.textureTexCoordA, 2, GL_FLOAT, GL_FALSE, 4*sizeof(GLfloat), 2*sizeof(GLfloat));

// draw fullscreen quad with texture
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);


// DRAW NORMAL FRAME

glUseProgram(normalProgram);
glEnableVertexAttribArray(...);
glVertexAttribPointer(...);

// draw solid shape
glDrawArrays(GL_TRIANGLE_STRIP, ...);

// draw line
glDrawArrays(GL_LINE_STRIP, ...);

// draw points
glDrawArrays(GL_POINTS, ...);

纹理着色器非常简单:顶点着色器只是将纹理坐标传递给片段着色器:

attribute vec4 aPosition;
attribute vec2 aTexCoord;

varying vec2 vTexCoord;

void main(void)
{
    gl_Position = aPosition;
    vTexCoord = aTexCoord;
}

并且片段着色器从纹理中读取片段颜色:

uniform highp sampler2D uTexture;
varying vec2 vTexCoord;

void main(void)
{
    gl_FragColor = texture2D(uTexture, vTexCoord);
}

虽然这样做有效,但它看起来效率不高,导致渲染器利用率上升到接近100%。当每帧绘制的线数超过100左右时,它似乎比蛮力方法更好。如果有人对如何改进此代码有任何建议,我将非常感激!