11

我在让我的顶点着色器在 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;
}

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

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

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

大量搜索产生了这个关于 WebGL 的提示页面,其中有以下内容:

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

果然,不仅颜色属性被分配给索引零,而且如果我将一个不同的、启用数组的属性强制绑定为零,代码就会运行并产生正确的颜色。

我在任何地方都找不到任何其他提及此规则的内容,当然也不是在 ATI 硬件上。有谁知道这条规则来自哪里?或者这是 Mozilla 人员注意到并警告的实现中的错误?

4

1 回答 1

30

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

实际内容:

让我们来上一堂关于 OpenGL 规范是如何形成的历史课。

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

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

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

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

看,一旦有了着色器,您就希望开始能够定义自己的属性,而不是使用内置属性。这一切都说得通。但是,有一个问题。

即时模式的工作原理如下:

glBegin(...);
glTexCoord(...);
glColor(...);
glVertex(...);
glTexCoord(...);
glColor(...);
glVertex(...);
glTexCoord(...);
glColor(...);
glVertex(...);
glEnd();

每次调用 时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。

BTW, if you're wondering, NVIDIA won. In OpenGL 4.3, the compatibility profile uses the same wording for its array rendering commands as core. Thus, you're allowed to not use attribute 0 on both core and compatibility.

于 2012-11-12T18:24:05.223 回答