在WebGL中,属性(attribute)、统一变量(uniform)和易变变量(varying variable)有什么区别?

101
有没有什么比喻可以用来比较这些不同类型或它们的工作方式?
另外,“uniforming a matrix”是什么意思?
4个回答

109

以下内容直接从http://www.lighthouse3d.com/tutorials/glsl-tutorial/data-types-and-variables/复制而来。实际网站有更详细的信息,值得一看。

变量修饰符

修饰符可以赋予变量特殊含义。以下修饰符可用:

  • const – 声明为编译时常量。
  • attribute – 全局变量,可能会在每个顶点中发生变化,由OpenGL应用程序传递到顶点着色器中。此修饰符只能在顶点着色器中使用。对于着色器而言,这是一个只读变量。请参见Attribute部分。
  • uniform – 全局变量,可能会在每个基元中发生变化[...],由OpenGL应用程序传递到着色器中。此修饰符可在顶点着色器和片段着色器中使用。对于着色器而言,这是一个只读变量。请参见Uniform部分。
  • varying – 用于在顶点着色器和片段着色器之间插值数据。在顶点着色器中可写,在片段着色器中只读。请参见Varying部分。

对于类比,const和uniform就像C/C++中的全局变量,一个是常量,另一个可以被设置。Attribute变量伴随着顶点而变化,例如颜色或纹理坐标。Varying变量可以被顶点着色器修改,但不能被片段着色器修改,因此它们实际上是通过管道传递信息。


1
仅就属性方面进行扩展:属性不一定需要是数组属性(如果值对于每个顶点可能不同,则需要数组属性)。它也可以是常量顶点属性,在这种情况下,该值在所有顶点之间共享。实际上,数组属性需要通过gl.enableVertexAttribArray主动启用。 - Robert Monfera
很高兴在这里有这段文本,因为网站已经无法访问了。 - Ziyuan
1
只是想“那个人”,但我在网站上看不到任何表明在此处复制是合法的内容。 - gman

106
  • uniform每个图元(primitive) 的参数(在整个绘制调用期间保持不变);
  • attribute每个顶点 的参数(通常是位置、法向量、颜色、UV 等);
  • varying每个片段(或像素) 的参数:它们从一个像素变化到另一个像素。

了解 varying 的工作原理对于编写自己的着色器非常重要。
假设您在 顶点着色器 中为三角形的每个顶点定义了一个变化参数 v。当将此变化参数发送到 片段着色器 时,它的值会根据要绘制的像素的位置自动插值。

在以下图像中,红色像素接收到了变化参数 v 的插值值。这就是为什么我们称它们为“varying”的原因。

varying parameter being bilinearly interpolated

为简单起见,上面给出的示例使用了 双线性插值,假设所有绘制的像素与相机的距离相同。为了实现准确的 3D 渲染,图形设备使用 透视校正插值,它考虑了像素的深度。


4
答案的精神是正确的,但要记住对于“varying”来说,默认使用的插值方式称为透视校正插值,而不仅仅是双线性插值。当然,可以使用插值限定符 noperspective 来更改这个设置,以获取简单的双线性插值而不是透视校正插值(默认限定符为smooth)。请参见 此示例 - legends2k
1
谢谢,我会添加一条关于这个的注释。 - neeh
1
这应该是被接受的答案。谢谢! - Chen Ni

16
在WebGL中,属性(attribute)、一致变量(uniform)和易变变量(varying variable)有何区别?
在OpenGL中,“程序”是“着色器”(较小的程序)的集合,这些着色器在管道中彼此连接。
// "program" contains a shader pipeline:
//   vertex shader -> other shaders -> fragment shader
//
const program = initShaders(gl, "vertex-shader", "fragment-shader");
gl.useProgram(program);

顶点着色器处理顶点,几何着色器处理几何体,镶嵌着色器处理细分,像素着色器处理片元,计算着色器处理其他批处理任务,这些都是渲染三维模型所需的。

OpenGL(WebGL)着色器使用GLSL编写(一种在GPU上编译的基于文本的着色器语言)。

// Note: As of 2017, WebGL only supports Vertex and Fragment shaders

<!-- Vertex Shader -->
<script id="shader-vs" type="x-shader/x-vertex">

  // <-- Receive from WebGL application
  uniform vec3 vertexVariableA;

  // attribute is supported in Vertex Shader only
  attribute vec3 vertexVariableB;

  // --> Pass to Fragment Shader
  varying vec3 variableC;

</script>

<!-- Fragment Shader -->
<script id="shader-fs" type="x-shader/x-fragment">

  // <-- Receive from WebGL application
  uniform vec3 fragmentVariableA;

  // <-- Receive from Vertex Shader
  varying vec3 variableC;

</script>

记住这些概念:

着色器可以将数据传递给管道中的下一个着色器(outinout),它们还可以接受来自WebGL应用程序或先前着色器的数据(in)。

  • 顶点着色器和片段着色器(任何着色器都可以)可以使用uniform变量从WebGL应用程序接收数据。

// Pass data from WebGL application to shader
const uniformHandle = gl.glGetUniformLocation(program, "vertexVariableA");
gl.glUniformMatrix4fv(uniformHandle, 1, false, [0.1, 0.2, 0.3], 0);
  • 顶点着色器还可以使用attribute变量接收来自WebGL应用程序的数据,根据需要启用或禁用该变量。

  • // Pass data from WebGL application to Vertex Shader
    const attributeHandle = gl.glGetAttribLocation(mProgram, "vertexVariableB");
    gl.glEnableVertexAttribArray(attributeHandle);
    gl.glVertexAttribPointer(attributeHandle, 3, gl.FLOAT, false, 0, 0);
    
  • 顶点着色器可以使用 varying 变量将数据传递到片段着色器。请参见上面的GLSL代码(varying vec3 variableC;)。


  • 2

    Uniforms是将数据从CPU应用程序传递到GPU着色器的另一种方式,但与顶点属性相比,uniforms略有不同。首先,uniforms是全局的。全局意味着uniform变量对于每个着色器程序对象是唯一的,并且可以从着色器程序的任何阶段访问。其次,无论您将uniform值设置为什么,uniforms都会保持其值,直到重置或更新它们为止。

    我喜欢https://learnopengl.com/Getting-started/Shaders中的描述,因为单词“per-primitive”并不直观。


    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接