WebGL - 着色器
着色器是在 GPU 上运行的程序。着色器是用 OpenGL ES 着色器语言(称为 ES SL)编写的。ES SL 有自己的变量、数据类型、限定符、内置输入和输出。
数据类型
下表列出了 OpenGL ES SL 提供的基本数据类型。
先生。 | 类型和描述 |
---|---|
1 | 空白 代表一个空值。 |
2 | 布尔值 接受真或假。 |
3 | 整数 这是有符号整数数据类型。 |
4 | 漂浮 这是浮点标量数据类型。 |
5 | 向量2、向量3、向量4 n 分量浮点向量 |
6 | bvec2、bvec3、bvec4 布尔向量 |
7 | ivec2、ivec3、ivec4 有符号整数向量 |
8 | 垫2、垫3、垫4 2x2、3x3、4x4 浮点矩阵 |
9 | 二维采样器 访问 2D 纹理 |
10 | 采样器立方体 访问立方体映射纹理 |
预选赛
OpenGL ES SL 中有三个主要限定符 -
先生。 | 限定符和描述 |
---|---|
1 | 属性 此限定符充当顶点着色器和 OpenGL ES 之间针对每个顶点数据的链接。该属性的值会随着顶点着色器的每次执行而变化。 |
2 | 制服 此限定符链接着色器程序和 WebGL 应用程序。与属性限定符不同,uniform 的值不会改变。制服是只读的;您可以将它们与任何基本数据类型一起使用来声明变量。 示例- 均匀vec4 lightPosition; |
3 | 变化的 此限定符在顶点着色器和片段着色器之间形成插值数据的链接。它可以与以下数据类型一起使用 - float、vec2、vec3、vec4、mat2、mat3、mat4 或数组。 示例- 改变vec3法线; |
顶点着色器
顶点着色器是一段程序代码,在每个顶点上都会被调用。它将几何图形(例如:三角形)从一处变换(移动)到另一处。它处理每个顶点的数据(per-vertex data),例如顶点坐标、法线、颜色和纹理坐标。
在顶点着色器的ES GL代码中,程序员必须定义属性来处理数据。这些属性指向用 JavaScript 编写的顶点缓冲区对象。可以使用顶点着色器和顶点变换来执行以下任务 -
- 顶点变换
- 法线变换和归一化
- 纹理坐标生成
- 纹理坐标变换
- 灯光
- 色料应用
预定义变量
OpenGL ES SL 为顶点着色器提供以下预定义变量 -
先生。 | 变量和描述 |
---|---|
1 | highp vec4 gl_Position; 保持顶点的位置。 |
2 | mediump float gl_PointSize; 保存变换后的点大小。该变量的单位是像素。 |
示例代码
看一下以下顶点着色器的示例代码。它处理三角形的顶点。
attribute vec2 coordinates; void main(void) { gl_Position = vec4(coordinates, 0.0, 1.0); };
如果你仔细观察上面的代码,我们已经声明了一个属性变量,名称为坐标。(此变量将使用getAttribLocation()方法与顶点缓冲区对象关联。属性坐标与着色器程序对象一起作为参数传递给此方法。)
在给定顶点着色器程序的第二步中,定义了gl_position变量。
gl_位置
gl_Position 是预定义变量,仅在顶点着色器程序中可用。它包含顶点位置。在上面的代码中,坐标属性以向量的形式传递。由于顶点着色器是逐顶点操作,因此为每个顶点计算 gl_position 值。
随后,gl_position 值被图元组装、裁剪、剔除和其他在顶点处理结束后对图元进行操作的固定功能操作使用。
我们可以为顶点着色器的所有可能操作编写顶点着色器程序,我们将在本教程中单独讨论。
片段着色器
网格由多个三角形组成,每个三角形的表面称为片段。片段着色器是在每个片段上的每个像素上运行的代码。这是为了计算和填充各个像素的颜色而编写的。可以使用片段着色器执行以下任务 -
- 插值运算
- 纹理访问
- 纹理应用
- 多雾路段
- 颜色总和
预定义变量
OpenGL ES SL 为片段着色器提供以下预定义变量 -
先生。 | 变量和描述 |
---|---|
1 | mediump vec4 gl_FragCoord ; 保存帧缓冲区内的片段位置。 |
2 | 布尔 gl_FrontFacing; 保存属于前置基元的片段。 |
3 | mediump vec2 gl_PointCoord; 保留点内的片段位置(仅限点光栅化)。 |
4 | mediump vec4 gl_FragColor; 保存着色器的输出片段颜色值 |
5 | mediump vec4 gl_FragData[n] 保存颜色附件n的片段颜色。 |
示例代码
以下片段着色器的示例代码展示了如何将颜色应用于三角形中的每个像素。
void main(void) { gl_FragColor = vec4(0, 0.8, 0, 1); }
在上面的代码中,颜色值存储在变量 中gl.FragColor
。片段着色器程序使用固定函数变量将输出传递到管道;FragColor 就是其中之一。该变量保存模型像素的颜色值。
存储和编译着色器程序
由于着色器是独立的程序,因此我们可以将它们编写为单独的脚本并在应用程序中使用。或者,您可以直接以字符串格式存储它们,如下所示。
var vertCode = 'attribute vec2 coordinates;' + 'void main(void) {' + ' gl_Position = vec4(coordinates, 0.0, 1.0);' + '}';
编译着色器
编译涉及以下三个步骤 -
- 创建着色器对象
- 将源代码附加到创建的着色器对象
- 编译程序
创建顶点着色器
为了创建空着色器,WebGL 提供了一个名为createShader()的方法。它创建并返回着色器对象。其语法如下 -
Object createShader (enum type)
正如语法中所观察到的,此方法接受预定义的枚举值作为参数。我们对此有两种选择 -
gl.VERTEX_SHADER用于创建顶点着色器
gl.FRAGMENT_SHADER用于创建片段着色器。
将源附加到着色器
您可以使用方法shaderSource()将源代码附加到创建的着色器对象。其语法如下 -
void shaderSource(Object shader, string source)
该方法接受两个参数 -
着色器- 您必须将创建的着色器对象作为一个参数传递。
源- 您必须以字符串格式传递着色器程序代码。
编译程序
要编译程序,您必须使用compileShader()方法。其语法如下 -
compileShader(Object shader)
该方法接受着色器程序对象作为参数。创建着色器程序对象后,将源代码附加到该对象并将该对象传递给此方法。
以下代码片段展示了如何创建和编译顶点着色器以及片段着色器来创建三角形。
// Vertex Shader var vertCode = 'attribute vec3 coordinates;' + 'void main(void) {' + ' gl_Position = vec4(coordinates, 1.0);' + '}'; var vertShader = gl.createShader(gl.VERTEX_SHADER); gl.shaderSource(vertShader, vertCode); gl.compileShader(vertShader); // Fragment Shader var fragCode = 'void main(void) {' + ' gl_FragColor = vec4(0, 0.8, 0, 1);' + '}'; var fragShader = gl.createShader(gl.FRAGMENT_SHADER); gl.shaderSource(fragShader, fragCode); gl.compileShader(fragShader);
联合计划
创建并编译两个着色器程序后,您需要创建一个包含两个着色器(顶点和片段)的组合程序。需要遵循以下步骤 -
- 创建程序对象
- 连接两个着色器
- 链接两个着色器
- 使用程序
创建程序对象
使用createProgram()方法创建程序对象。它将返回一个空的程序对象。这是它的语法 -
createProgram();
连接着色器
使用方法AttachShader()将着色器附加到创建的程序对象。其语法如下 -
attachShader(Object program, Object shader);
该方法接受两个参数 -
程序- 将创建的空程序对象作为一个参数传递。
Shader - 传递已编译的着色器程序之一(顶点着色器、片段着色器)
注意- 您需要使用此方法附加两个着色器。
链接着色器
使用linkProgram()方法链接着色器,方法是传递已附加着色器的程序对象。其语法如下 -
linkProgram(shaderProgram);
使用该程序
WebGL 提供了一个名为useProgram()的方法。您需要将链接的程序传递给它。其语法如下 -
useProgram(shaderProgram);
以下代码片段展示了如何创建、链接和使用组合着色器程序。
var shaderProgram = gl.createProgram(); gl.attachShader(shaderProgram, vertShader); gl.attachShader(shaderProgram, fragShader); gl.linkProgram(shaderProgram); gl.useProgram(shaderProgram);