OpenGL - 是否有更简单的方法用纹理填充窗口,而不是使用VBO等?

时间:2015-07-17 18:55:30

标签: opengl

我的OpenGL窗口如下所示:

glClearColor(0.3f, 0.4f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);

我想使用纹理来填充窗口。

是否有更简单的方法,而不是创建另一个VBO,EBO,除了我已用于我的三角形的那个?

因为glClearColor填充了背景..

4 个答案:

答案 0 :(得分:5)

使用glBlitFramebuffer()向窗口绘制纹理的最直接且最通用的最有效方法。

要使用此功能,您需要创建一个FBO,并将纹理texId附加到其上:

GLuint fboId = 0;
glGenFramebuffers(1, &fboId);
glBindFramebuffer(GL_READ_FRAMEBUFFER, fboId);
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
                       GL_TEXTURE_2D, texId, 0);

请注意,上面的代码绑定了GL_READ_FRAMEBUFFER,因为我们想将它用作blit的源。

然后,复制内容:

glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);  // if not already bound
glBlitFramebuffer(0, 0, width, height, 0, 0, width, height,
                  GL_COLOR_BUFFER_BIT, GL_NEAREST);

这适用于纹理和窗口具有相同大小的情况。否则,您可以在前8个参数中指定不同的大小,并且可能希望将GL_LINEAR用于最后一个参数。

使用glBlitFramebuffer()比绘制窗口大小的纹理四边形有一些优势:

  1. 它需要更少的API调用。
  2. 您不需要为复制操作编写着色器。
  3. 您不需要绑定不同的着色器程序,这可以减少开销。
  4. 与使用应用程序提供的着色器和绘制调用相比,驱动程序可能具有更优化的操作代码路径。
  5. 许多GPU都有用于blitting数据的专用单元,这可能比可编程着色器单元更有效。它们还可以与GPU的通用可编程部分并行运行,允许复制与渲染并行执行。如果适用,性能增益可能非常大。

答案 1 :(得分:4)

用一句话说:不。

在传统的OpenGL中,glDrawPixels就好了,但是这个功能从来没有得到很好的支持,并且在大多数实现中都很慢。你最好忘记我告诉你的事情。它也已从现代OpenGL中删除,并且从未存在于OpenGL-ES中。

答案 2 :(得分:3)

这个问题已经有了一些答案,但为了完整性,我想补充一些替代方案:

<强> 1。无属性渲染

使用现代GL,您可以完全渲染而不使用顶点属性。您可以将全屏rect的4个2d coordiantes直接作为const数组放入顶点着色器,并通过gl_VertexID访问它们:

// VERTEX SHADER
#version 150 core
out vec2 v_tex;

const vec2 pos[4]=vec2[4](vec2(-1.0, 1.0),
                          vec2(-1.0,-1.0),
                          vec2( 1.0, 1.0),
                          vec2( 1.0,-1.0));

void main()
{
    v_tex=0.5*pos[gl_VertexID] + vec2(0.5);
    gl_Position=vec4(pos[gl_VertexID], 0.0, 1.0)
}

// FRAGMENT SHADER
#version 150 core
in vec2 v_tex;
uniform sampler2D texSampler;
out vec4 color;
void main()"
{
    color=texture(texSampler, v_tex);
}

如果您的纹理与视口的分辨率完全匹配(因此您根本没有缩放纹理),则可以完全删除v_tex变化并使用FS中的color=texelFetch(texSampler, ivec2(gl_FragCoord.xy)),如@datenwolf在他的评论中提出。

在任何情况下,即使没有启用任何属性,您仍然需要一些VAO绑定。因此,此方法要求您在初始化期间执行以下一次

  • 创建并编译着色器并将它们链接到程序
  • 通过glGenVertexArrays()电话
  • 创建新的VAO名称

对于绘图,你必须:

  • 绑定您想要绘制的纹理
  • 使用程序
  • 绑定(仍为空)VAO
  • glDrawArrays(GL_TRIANGLE_STRIP, 0, 4)

您也可以简单地重复使用当前绑定的VAO。由于着色器不访问任何属性,因此VBO提供的数据以及当前启用的属性无关紧要。

此方法要求您切换着色器,这也不是很便宜,因此最好只需切换缓冲区bindigs并保留当前着色器。但您可能还需要切换着色器。

<强> 2。 nvidia-specificc扩展

NVidia为在屏幕上绘制纹理的任务提供了特定的扩展:NV_draw_texture。这引入了glDrawTextureNV()函数,该函数允许绘制纹理而不用设置更改GL状态的任何内容。引自扩展规范的概述部分:

  

虽然可以通过绘制a在未扩展的OpenGL中获得此功能       矩形并使用片段着色器进行纹理查找,       DrawTextureNV()可能会提高电源效率       支持此扩展的实现。另外,使用此功能       扩展使应用程序开发人员不必设置       专用着色器,变换矩阵,顶点属性和       各种其他状态,以呈现矩形

这种方法的缺点当然是它是特定于nvidia的,所以它在一般的GL应用中可能不太实际使用。

答案 3 :(得分:1)

您可以使用ortographic投影将纹理渲染到全屏四边形:

glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, texture);
glDisable(GL_LIGHTING);

// Set up ortographic projection
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadIdentity();
glOrtho(0, width, 0, height, -1, 1);

// Render a quad
glBegin(GL_QUADS);
glTexCoord2f(0,0); glVertex2f(0,0);
glTexCoord2f(0,1); glVertex2f(0,width);
glTexCoord2f(1,1); glVertex2f(height, width);
glTexCoord2f(1,0); glVertex2f(height,0);
glEnd();

// Reset Projection Matrix
glPopMatrix();
glDisable(GL_TEXTURE_2D);
glEnable(GL_LIGHTING);    

将其渲染到帧缓冲区而不是glClearColor。