我可以使用OpenGL进行离屏渲染吗?

时间:2013-02-09 04:37:43

标签: opengl 3d rendering render off-screen

我想尝试制作一个简单的程序,它采用3D模型并将其渲染成图像。有什么方法可以使用OpenGL渲染图像并将其放入一个包含图像而不是显示图像的变量中?我不想看到我正在渲染的东西我只想保存它。有没有办法用OpenGL做到这一点?

2 个答案:

答案 0 :(得分:7)

我假设您知道如何使用OpenGL在屏幕上绘制内容,并且您编写了一个函数,例如 drawStuff 来执行此操作。

首先,您必须决定最终渲染的大小;我在这里选择一个方块,大小为512x512。您也可以使用不是2的幂的大小,但为了简单起见,我们暂时坚持这种格式。有时OpenGL对这个问题很挑剔。

const int width = 512;
const int height = 512;

然后你需要三个物体来创建一个屏幕外的绘图区域;这称为帧缓冲对象,如user1118321所述。

GLuint color;
GLuint depth;
GLuint fbo;

FBO存储颜色缓冲区和深度缓冲区;你的屏幕渲染区域也有这两个缓冲区,但是你不想使用它们,因为你不想绘制到屏幕上。要创建FBO,您需要在启动时执行类似以下的操作

glGenTextures(1, &color);
glBindTexture(GL_TEXTURE_2D, color);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_BGRA, GL_UNSIGNED_BYTE, NULL);
glBindTexture(GL_TEXTURE_2D, 0);

glGenRenderbuffers(1, &depth);
glBindRenderbuffer(GL_RENDERBUFFER, depth);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, width, height);
glBindRenderbuffer(GL_RENDERBUFFER, 0);

glGenFramebuffers(1, &fbo);
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, color, 0);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depth);
glBindFramebuffer(GL_FRAMEBUFFER, 0);

首先创建一个用于存储像素颜色的存储区域,而不是用于存储像素深度的存储区域(在计算机图形中用于移除隐藏曲面),最后将它们连接到FBO,FBO基本上包含对两者的引用。以第一个块为例,有6个调用:

  • glGenTextures 为纹理创建名称; OpenGL中的名称只是一个整数,因为字符串效率太低。
  • glBindTexture 将纹理绑定到目标,即 GL_TEXTURE_2D ;后续调用指定相同的目标将对该纹理进行操作。
  • 第3次,第4次和第5次调用特定于被操作的目标,您应该参考OpenGL文档以获取更多信息。
  • 最后一次调用 glBindTexture 从目标中取消绑定纹理。因为在某些时候你会将控制权交给你的 drawStuff 函数,而这个函数反过来会进行大量的OpenGL调用,你需要现在清理你的工作区,以避免干扰你创建的对象

要从屏幕渲染切换到屏幕外渲染,您可以在程序中的某处使用布尔变量:

if (offscreen)
    glBindFramebuffer(GL_FRAMEBUFFER, fbo);
else
    glBindFramebuffer(GL_FRAMEBUFFER, 0);

drawStuff();

if (offscreen)
    saveToFile();

因此,如果 offscreen true ,您实际上希望 drawStuff 干扰 fbo ,因为您需要它在其上渲染场景。

函数saveToFile负责加载渲染结果并将其转换为文件。这在很大程度上取决于您使用的操作系统和语言。例如,在带有C的Mac OS X上,它将类似于以下内容:

void saveImage()
{
    void *imageData = malloc(width * height * 4);

    glBindTexture(GL_TEXTURE_2D, color);
    glGetTexImage(GL_TEXTURE_2D, 0, GL_BGRA, GL_UNSIGNED_BYTE, imageData);

    CGContextRef contextRef = CGBitmapContextCreate(imageData, width, height, 8, 4 * width, CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB), kCGImageAlphaPremultipliedLast);
    CGImageRef imageRef = CGBitmapContextCreateImage(contextRef);
    CFURLRef urlRef = (CFURLRef)[NSURL fileURLWithPath:@"/Users/JohnDoe/Documents/Output.png"];
    CGImageDestinationRef destRef = CGImageDestinationCreateWithURL(urlRef, kUTTypePNG, 1, NULL);
    CGImageDestinationAddImage(destRef, imageRef, nil);        
    CFRelease(destRef);

    glBindTexture(GL_TEXTURE_2D, 0);

    free(imageData);
}

答案 1 :(得分:6)

是的,你可以这样做。你想要做的是创建一个由纹理支持的frame buffer object (FBO)。一旦你创建一个并绘制到它,你可以download the texture到主内存并保存它就像你任何位图一样。