为什么在禁用顶点属性数组零时OpenGL绘图失败?

flu*_*ggo 10 opengl vertex-shader opengl-3 vertex-attributes

我在使用ATI驱动程序的OpenGL 3.3内核运行我的顶点着色器时遇到了极大的麻烦:

#version 150

uniform mat4 graph_matrix, view_matrix, proj_matrix;
uniform bool align_origin;

attribute vec2 graph_position;
attribute vec2 screen_position;
attribute vec2 texcoord0;
attribute vec4 color;
varying vec2 texcoord0_px;
varying vec4 color_px;

void main() {
    // Pick the position or the annotation position
    vec2 pos = graph_position;

    // Transform the coordinates
    pos = vec2(graph_matrix * vec4(pos, 0.0, 1.0));

    if( align_origin )
        pos = floor(pos + vec2(0.5, 0.5)) + vec2(0.5, 0.5);

    gl_Position = proj_matrix * view_matrix * vec4(pos + screen_position, 0.0, 1.0);
    texcoord0_px = texcoord0;
    color_px = color;
}
Run Code Online (Sandbox Code Playgroud)

我使用glVertexAttrib4f来指定颜色属性,并关闭属性数组.根据3.3核心规范的第33页,这应该工作:

如果未启用与顶点着色器所需的通用属性对应的数组,则从当前通用属性状态获取相应的元素(请参阅第2.7节).

但是(大多数情况下,取决于配置文件和驱动程序)着色器要么根本不运行,要么在我访问禁用的颜色属性时使用黑色.用常量替换它使它运行.

很多搜索产生了关于WebGL的这一页提示,其中有以下内容:

始终启用顶点属性0数组.如果您使用顶点attrib 0数组禁用绘图,则会在桌面OpenGL上运行时强制浏览器执行复杂的仿真(例如在Mac OSX上).这是因为在桌面OpenGL中,如果顶点attrib 0未启用数组,则不会绘制任何内容.您可以使用bindAttribLocation()强制顶点属性使用位置0,并使用enableVertexAttribArray()使其启用数组.

果然,不仅将颜色属性指定为索引零,而且如果我将一个不同的,启用数组的属性强制绑定为零,则代码运行并生成正确的颜色.

我无法在任何地方找到任何其他提及此规则,当然也不会在ATI硬件上找到.有谁知道这条规则的来源?或者这是Mozilla人注意到并警告的实施中的错误?

Nic*_*las 27

tl; dr:这是一个驱动程序错误.核心OpenGL 3.3应该允许您不使用属性0,但兼容性配置文件不允许,并且某些驱动程序不能正确实现该开关.只需确保使用属性0以避免任何问题.

实际内容:

让我们来看一下OpenGL规范的历史教训.

在OpenGL最古老的时代,只有一种渲染方式:立即模式(即:)glBegin/glVertex/glColor/glEtc/glEnd.显示列表已存在,但它们始终定义为再次发送捕获的命令.因此,虽然实现实际上并没有进行所有这些函数调用,但实现仍然会像它们那样表现.

在OpenGL 1.1中,客户端顶点数组被添加到规范中.现在请记住:规范是一个指定行为的文档,而不是实现.因此,ARB简单地定义了客户端数组的工作方式与立即模式调用完全相同,使用对当前数组指针的适当访问.显然,实现实际上并不会这样做,但它们的行为就像它们那样.

基于缓冲区对象的顶点数组以相同的方式定义,但语言稍微复杂,因为从服务器存储而不是客户端存储中提取.

然后发生了一些事情:ARB_vertex_program(不是ARB_vertex_shader.我说的是汇编程序).

一旦有了着色器,就可以看到,您希望开始能够定义自己的属性,而不是使用内置属性.这一切都有道理.但是,有一个问题.

Immedate模式的工作方式如下:

glBegin(...);
glTexCoord(...);
glColor(...);
glVertex(...);
glTexCoord(...);
glColor(...);
glVertex(...);
glTexCoord(...);
glColor(...);
glVertex(...);
glEnd();
Run Code Online (Sandbox Code Playgroud)

每次调用时glVertex,都会导致所有当前属性状态用于单个顶点.所有其他立即模式函数只是将值设置到上下文中; 这个函数实际上顶点发送给OpenGL进行处理.这在即时模式下非常重要.并且因为每个顶点必须在固定功能区域中具有位置,所以使用该函数来决定何时应该处理顶点是有意义的.

一旦你不再使用OpenGL的固定函数顶点语义,你就会遇到立即模式的问题.也就是说,您如何确定何时实际发送顶点?

按照惯例,他们将其粘贴到属性0.因此,所有立即模式渲染必须使用属性0或glVertex来发送顶点.

但是,由于所有其他渲染都基于立即模式渲染的语言,因此所有其他渲染都具有与立即模式渲染相同的限制.立即模式需要属性0或glVertex,因此客户端阵列也需要等等.即使它对他们没有意义,他们也需要它,因为规范如何定义他们的行为.

然后OpenGL 3.0出现了.他们弃用了立即模式.弃用并不意味着被删除 ; 规范中仍然有这些函数,并且所有顶点数组渲染仍然是根据它们定义的.

OpenGL 3.1实际上撕掉了旧的东西.这带来了一些语言问题.毕竟,每个数组绘图命令总是根据立即模式定义.但是一旦立即模式不再存在......你如何定义它?

所以他们不得不为核心OpenGL 3.1+提出新的语言.在这样做时,他们删除了需要使用属性0的无意义限制.

但兼容性配置文件没有.

因此,OpenGL 3.2+的规则就是这样.如果您有核心OpenGL配置文件,则不必使用属性0.如果您具有兼容性OpenGL配置文件,则必须使用属性0(或glVertex).这就是规范所说的.

但这不是实现的实现.

一般来说,NVIDIA从不关心"必须使用属性0"规则,只是按照您的预期,即使在兼容性配置文件中也是如此.因此违反了规范的字母.AMD通常更有可能遵守规范.但是,他们忘记了正确实施核心行为.因此NVIDIA在兼容性方面过于宽松,而AMD对核心的限制过于严格.

要解决这些驱动程序错误,只需使用属性0即可.

顺便说一句,如果你想知道,NVIDIA赢了.在OpenGL 4.3中,兼容性配置文件使用与其数组渲染命令相同的措辞作为核心.因此,您不允许在核心和兼容性上使用属性0.