我是OpenGL ES的新手。我正在尝试为iOS应用程序(尤其是游戏)的屏幕录制编写代码。
我使用的是"渲染到纹理"方法,在这个答案中用代码描述(https://stackoverflow.com/a/9704392/707773)捕捉屏幕并为cocos2d游戏编写视频。我做的一个修改是,当我调用CVOpenGLESTextureCacheCreate
时,我使用的是[EAGLContext currentContext]
而不是[[GPUImageOpenGLESContext sharedImageProcessingOpenGLESContext] context]
它确实录制了视频,但有两个问题
-
当它开始录制时,屏幕上的新绘图就会停止。我希望这个应用程序也能继续在屏幕上绘图。由于我是OpenGL ES的新手,我对帧缓冲区对象等没有深入的了解,所以我很难弄清楚如何同时在屏幕上绘制和捕捉屏幕。我会很欣赏这方面的代码示例。
-
录制的视频被倒置。我怎样才能把它指向正确的方向?
以前我也考虑过glReadPixels
方法,但这有性能缺陷。
更新:我还想到了几个想法。据我所知,
我可以简单地将纹理绘制回屏幕,但不知道如何绘制。
更新:主绘图
// ----- Display the keyframe -----
Texture* t = augmentationTexture[OBJECT_KEYFRAME_1 + playerIndex];
frameTextureID = [t textureID];
aspectRatio = (float)[t height] / (float)[t width];
texCoords = quadTexCoords;
// Get the current projection matrix
QCAR::Matrix44F projMatrix = vapp.projectionMatrix;
// If the current status is valid (not NOT_READY or ERROR), render the
// video quad with the texture we've just selected
if (NOT_READY != currentStatus) {
// Convert trackable pose to matrix for use with OpenGL
QCAR::Matrix44F modelViewMatrixVideo = QCAR::Tool::convertPose2GLMatrix(trackablePose);
QCAR::Matrix44F modelViewProjectionVideo;
// SampleApplicationUtils::translatePoseMatrix(0.0f, 0.0f, videoData[playerIndex].targetPositiveDimensions.data[0], &modelViewMatrixVideo.data[0]);
SampleApplicationUtils :: scalePoseMatrix(videoData[playerIndex].targetPositiveDimensions.data[0], videoData[playerIndex].targetPositiveDimensions.data[0] * aspectRatio, videoData[playerIndex].targetPositiveDimensions.data[0], &modelViewMatrixVideo.data[0]);
SampleApplicationUtils::multiplyMatrix(projMatrix.data, &modelViewMatrixVideo.data[0], &modelViewProjectionVideo.data[0]);
glUseProgram(shaderProgramID);
glVertexAttribPointer(vertexHandle, 3, GL_FLOAT, GL_FALSE, 0, quadVertices);
glVertexAttribPointer(normalHandle, 3, GL_FLOAT, GL_FALSE, 0, quadNormals);
glVertexAttribPointer(textureCoordHandle, 2, GL_FLOAT, GL_FALSE, 0, texCoords);
glEnableVertexAttribArray(vertexHandle);
glEnableVertexAttribArray(normalHandle);
glEnableVertexAttribArray(textureCoordHandle);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, frameTextureID);
glUniformMatrix4fv(mvpMatrixHandle, 1, GL_FALSE, (GLfloat*) &modelViewProjectionVideo.data[0]);
glUniform1i(texSampler2DHandle, 0 /*GL_TEXTURE0*/);
glDrawElements(GL_TRIANGLES, kNumQuadIndices, GL_UNSIGNED_SHORT, quadIndices);
glDisableVertexAttribArray(vertexHandle);
glDisableVertexAttribArray(normalHandle);
glDisableVertexAttribArray(textureCoordHandle);
glUseProgram(0);
}
将视频纹理缓冲区添加到帧
glBindTexture([videoWriter textureCacheTarget], [videoWriter textureCacheID]);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, [videoWriter textureCacheID], 0);
您似乎缺少一些关于这些东西如何工作的知识,并且不可能知道问题所在。让我为你分解一些事情,这样你就可以确定你的问题,希望你自己能解决它,但在其他方面,用代码中更具体的部分来问另一个问题,显示哪些有效,哪些无效。
因此,从帧缓冲区开始,这个对象是其他缓冲区的容器,在您的情况下,这些缓冲区可能是纹理和渲染缓冲区。通过绑定特定的帧缓冲区,您将告诉GPU绘制到此帧缓冲区的缓冲区。最常见的附件是颜色、深度和模具缓冲区。最常见的创建新帧缓冲区的过程如下所示:
- 生成新的帧缓冲区以获取ID并将其绑定
- 生成渲染缓冲区或纹理以获取ID并将其绑定
- 使用
glRenderbufferStorage
或glTexImage2D
设置缓冲区的数据和格式,如果要将其绑定到EAGLContext
对象上有renderbufferStorage:fromDrawable:
的视图,则在iOS中设置缓冲区。(最后一个可能由GLKView
等更高级别的对象完成) - 使用
glFramebufferRenderbuffer
或glFramebufferTexture2D
将渲染缓冲区附加到帧缓冲区
通过这种方式,可以创建任意数量的帧缓冲区。要选择绘制到哪个缓冲区,只需绑定正确的帧缓冲区。
通常,我们将帧缓冲区划分为主帧缓冲区和帧缓冲区对象(FBO)。主框架缓冲区是表示屏幕的缓冲区,ID为0,因此在大多数平台中,您可以简单地绑定一个0索引的缓冲区以继续绘制到主屏幕/视图。但是(这一点非常重要)在iOS上,从你的角度来看,没有主帧缓冲区这回事。你需要保存通过renderbufferStorage:fromDrawable:
生成的渲染缓冲区的ID,如果这不在你的代码中(你使用的是更高级别的工具),你需要通过调用向GPU询问缓冲区ID
GLint defaultFBO;
glGetIntegerv(GL_FRAMEBUFFER_BINDING_OES, &defaultFBO);
(在此处找到)但您需要确保在绑定主缓冲区时调用此函数,因此最好是在开始录制之前,这样您就知道它是正确的缓冲区。
则FBO是任何其他非主帧缓冲区。它也被称为屏幕外帧缓冲区。这些通常用于后处理或预处理。在您的情况下,您使用一个来将像素绘制到纹理数据中,CPU可以使用您提供的链接中描述的纹理缓存非常容易地访问这些数据。所以现在你至少有两个缓冲区,主缓冲区和屏幕外缓冲区。屏幕外缓冲区将具有纹理,然后可以重新绘制到主缓冲区。现在,绘制上的基本管道应该是这样的:
- 绑定FBO
- 绘制场景
- 处理FBO进行视频录制
- 绑定主缓冲区
- 绑定FBO纹理
- 将绑定的纹理绘制到主缓冲区
还有其他方法,如
- 从媒体(相机、视频…)获取数据
- 绑定FBO
- 将媒体数据绘制到FBO
- 处理FBO进行视频录制
- 绑定主缓冲区
- 将媒体数据绘制到主缓冲区
这将绘制两次场景,而不是重复使用FBO中纹理中的同一场景。如果你没有在绘图上做任何繁重的操作,这些基本上是一样的,但最好还是使用第一个。