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

waa*_*919 2 opengl

我的OpenGL窗口是这样绘制的:

glClearColor(0.3f, 0.4f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
Run Code Online (Sandbox Code Playgroud)

我想用一个纹理来填充窗口.

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

因为有glClearColor填充背景..

der*_*ass 6

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

1.无属性渲染

使用现代 GL,您可以完全渲染而无需顶点属性。您可以将全屏矩形的 4 个 2d 坐标直接作为常量数组放入顶点着色器中,并通过以下方式访问它们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);
}
Run Code Online (Sandbox Code Playgroud)

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

无论如何,您仍然需要一些 VAO 绑定,即使其中没​​有启用任何属性。所以这个方法需要你在初始化的时候做一次如下操作:

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

对于绘图,你必须:

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

您也许还可以简单地重新使用当前绑定的 VAO。由于着色器不访问任何属性,因此您的 VBO 提供什么数据以及当前启用哪些属性并不重要。

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

2.nvidia特定扩展

NVidia 为在屏幕上绘制纹理的任务提供了特定的扩展:NV_draw_texture。这引入了glDrawTextureNV()允许绘制纹理而无需在 GL 状态上进行任何设置更改的功能。引用扩展规范的概述部分:

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

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


Ret*_*adi 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);
Run Code Online (Sandbox Code Playgroud)

请注意上面的代码绑定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);
Run Code Online (Sandbox Code Playgroud)

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

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

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