OpenGL ES渲染到纹理

34

我一直在寻找简单的OpenGL ES代码,用于在纹理中渲染场景(特别是对于iPhone而言)。 我想知道以下内容:

  1. 如何在OpenGL ES中将场景渲染到纹理中?
  2. 在OpenGL ES中创建一个可作为渲染目标的纹理时,必须使用哪些参数?
  3. 将此渲染纹理应用于其他基元时是否有任何影响?
2个回答

44

这是我的做法。

我定义了一个纹理变量(我使用苹果的Texture2D类,但你也可以使用OpenGL纹理ID),以及一个帧缓冲:

Texture2d * texture;
GLuint textureFrameBuffer;

然后,在某个时候,我创建纹理、帧缓冲并附加渲染缓冲区。这些只需要做一次:

texture = [[Texture2D alloc] initWithData:0 
                             pixelFormat:kTexture2DPixelFormat_RGB888
                             pixelsWide:32
                             pixelsHigh:32
                             contentSize:CGSizeMake(width, height)];

// create framebuffer
glGenFramebuffersOES(1, &textureFrameBuffer);
glBindFramebufferOES(GL_FRAMEBUFFER_OES, textureFrameBuffer);

// attach renderbuffer
glFramebufferTexture2DOES(GL_FRAMEBUFFER_OES, GL_COLOR_ATTACHMENT0_OES, GL_TEXTURE_2D, texture.name, 0);

// unbind frame buffer
glBindFramebufferOES(GL_FRAMEBUFFER_OES, 0);

每次我想要渲染到纹理时,我会执行以下操作:

glBindFramebufferOES(GL_FRAMEBUFFER_OES, textureFrameBuffer);

...
// GL commands
...

glBindFramebufferOES(GL_FRAMEBUFFER_OES, 0);

关于你的第三个问题,就是这样,你可以像使用其他纹理一样使用这个纹理。


请问您能否编辑一下您的代码,使得 initWithData:... 方法调用可以不需要滚动屏幕? - nornagon
3
只是一个小细节。如果您在 iPhone 上工作并希望“取消绑定”您的自定义帧缓冲区,您需要使用glBindFramebufferOES(GL_FRAMEBUFFER_OES,1)而不是0(或者更好的方法是存储先前版本并在之后恢复)。请注意保持原文意思,并以通俗易懂的语言表述。 - Lope
3
绑定到帧缓冲Id 0并不总是初始化为GL会话中主要帧缓冲的Id。 我的ES2会话肯定存储了另一个Id。我建议人们参考在创建GL会话时给出的那个Id。 - Kalen
没关系,看起来Cocos2D的Texture2D类会很好用。 - jjxtra
只是为了补充Kyle所说的,你可以使用以下代码获取当前帧缓冲名称: GLint currentFrameBuffer = 0; glGetIntegerv(GL_FRAMEBUFFER_BINDING_OES, &currentFrameBuffer); - Bikush
显示剩余3条评论

11

要将场景渲染到纹理上,您必须使用与纹理相关联的帧缓冲。这是我创建的一个简化方法:

void glGenTextureFromFramebuffer(GLuint *t, GLuint *f, GLsizei w, GLsizei h)
{
    glGenFramebuffers(1, f);
    glGenTextures(1, t);

    glBindFramebuffer(GL_FRAMEBUFFER, *f);

    glBindTexture(GL_TEXTURE_2D, *t);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, *t, 0);

    GLuint depthbuffer;
    glGenRenderbuffers(1, &depthbuffer);    
    glBindRenderbuffer(GL_RENDERBUFFER, depthbuffer);
    glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, w, h);
    glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depthbuffer);

    GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
    if(status != GL_FRAMEBUFFER_COMPLETE)
        NSLog(@"Framebuffer status: %x", (int)status);
}

您可以轻松创建帧缓冲和纹理:
GLuint _texture, _framebuffer;
GLsizei w,h;
float scale = [UIScreen mainScreen].scale;
w = self.view.bounds.size.width * scale;
h = self.view.bounds.size.height * scale;
glGenTextureFromFramebuffer(&_texture, &_framebuffer, w, h);

您可以在绘制方法中使用 _framebuffer 将场景渲染到 _texture 中:
glBindFramebuffer(GL_FRAMEBUFFER, _framebuffer);
//draw here the content you want in the texture
//_texture is now a texture with the drawn content

//bind the base framebuffer
glBindFramebuffer(GL_FRAMEBUFFER, 0);
//or if you use GLKit
[view bindDrawable];
//draw normaly

现在你可以随心所欲地处理纹理。如果你想进行一些后期处理(模糊、泛光、阴影等),你可以!

你好,你从哪里得到了“_renderToTextureBuffer”? - The Way
很酷,这正是我在想的。你有这个的工作示例吗?我会尝试把它加入我的代码中(再次),但我不太确定如何绘制纹理 //在此处绘制您想要的纹理内容 我可以用VBO和 glDrawArrays()来做吗? - The Way

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