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

时间:2012-11-12 17:57:06

标签: opengl opengl-3 vertex-shader 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;
}

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

  

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

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

许多搜索产生了this page of tips regarding WebGL,其中包含以下内容:

  

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

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

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

1 个答案:

答案 0 :(得分: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();

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