获取shader变量地址及赋值
上一节创建了WebGL程序对象,创建好program对象后,对象中包含顶点着色器和片元着色器,着色器中含有变量,我们需要对其进行赋值后才能够进行绘制。
着色器代码如下:
const VSHADER_SOURCE = /* glsl */`
attribute vec4 a_Position;
void main() {
gl_Position = a_Position;
gl_PointSize = 10.0;
}
`;
const FSHADER_SOURCE = /* glsl */`
void main() {
gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
}
`;
其中,顶点着色代码VSHADER_SOURCE中定义了一个attribute
变量,a_Position
,在main函数中将其赋值给GLSL ES的内置变量gl_Position
,表示这个顶点所在的位置。
GLSL ES语言中,传入的变量有两种,attribute
和uniform
。其中,attribute
变量只能定义在顶点着色器中,传输的是与顶点有关的数据,而uniform
变量可以定义在顶点着色器和片元着色器中,传输的是与顶点无关或者说所有顶点共同使用的值。还有一类变量,varying
变量,是用于顶点着色器和片元着色器之间传输数据的。定义varying
变量时,需要在顶点着色器和片元着色器中各定义一个名称、类型都相同的变量,并指明为varying
变量。
只传入一个数据(不使用缓冲区)
给attribute变量赋值
以示例的shader代码为例,如果我们只打算在指定的位置绘制一个点,那么我们需要做的事就是:找到变量所在的地址,然后给它赋值即可。所以,给一个变量赋值步骤如下:
- 找到变量所在的地址
- 给变量赋值
示例代码
const a_Position = gl.getAttribLocation(shaderProgram, "a_Position");
if (a_Position < 0) {
console.log("变量a_Position地址查找失败!");
return;
}
gl.vertexAttrib3f(a_position, 0.0, 0.0, 0.0);
接口
GLint WebGLRenderingContext.getAttribLocation(program, name)
获取attribute变量的地址
-
program
: WebGLProgram,webgl程序 -
name
: string,变量名,与shader中的变量名一致
void WebGLRenderingContext.vertexAttrib3f(index, v0, v1, v2)
给顶点着色器中一个attribute变量赋值
index
: 变量的地址
v0,v1,v2
: 要赋值的值
vertexAttrib3f
还有很多同族函数,针对不同类型的变量
-
vertexAttrib1f(index, v0)
-
vertexAttrib2f(index, v0, v1)
-
vertexAttrib3f(index, v0, v1, v2)
-
vertexAttrib4f(index, v0, v1, v2, v3)
-
vertexAttrib1fv(index, value)
-
vertexAttrib2fv(index, value)
-
vertexAttrib3fv(index, value)
-
vertexAttrib4fv(index, value)
其中,以f
结尾的函数表示以浮点数的形式对变量进行赋值,1234表示分量的个数
以fv
结尾的函数表示使用一个向量进行赋值,其值为一个Float32Array
类型的值,1234表示这个结构化数组中的元素个数。
对变量进行赋值的时候,我们既可以通过浮点数分别对每个分量进行赋值,也可以使用结构化数组对整体赋值。
const a_foobar = gl.getAttribLocation(shaderProgram, "foobar");
// 对每个分量进行赋值
gl.vertexAttrib3f(a_foobar, 10.0, 5.0, 2.0);
// 使用结构化数组进行赋值
const floatArray = new Float32Array([10.0. 5.0, 2.0]);
gl.vertexAttrib3fv(a_foobar, floatArray);
对于矩阵,实际上是由多个列向量构成的,在计算其位置的时候,可以先获取到矩阵的起始位置,该位置+0,为第一列向量的位置,位置+1,为第二列向量的位置,以此类推,分别对每个列向量进行赋值,完成对整个矩阵的赋值。
/**
* 想赋值的3x3的矩阵为:
* 0 1 2
* 3 4 5
* 6 7 8
*/
const matrix3x3Location = gl.getAttribLocation(shaderProgram, "matrix3x3");
gl.vertexAttrib3f(matrix3x3Location, 0, 3, 6);
gl.vertexAttrib3f(matrix3x3Location + 1, 1, 4, 7);
gl.vertexAttrib3f(matrix3x3Location + 2, 2, 5, 8);
对uniform变量的赋值
uniform变量又称为一致变量,一般定义为与顶点无关的变量,即所有顶点保持一致的变量。
首先需要获取uniform变量的位置:
GLIint WebGLRenderingContext.getUniformLocation(program, name)
获取uniform变量的地址
-
program
: WebGLProgram, webgl程序对象 -
name
: string,变量名
之后,就可以对这些变量进行赋值
-
uniform1f(location, v0)
-
uniform2f(location, v0, v1)
-
uniform3f(location, v0, v1, v2)
-
uniform4f(location, v0, v1, v2, v3)
-
uniform1fv(location, value)
-
uniform2fv(location, value)
-
uniform3fv(location, value)
-
uniform4fv(location, value)
-
uniform1i(location, v0)
-
uniform1i(location, v0, v1)
-
uniform1i(location, v0, v1, v2)
-
uniform1i(location, v0, v1, v2, v3)
-
uniform1iv(location, value)
-
uniform2iv(location, value)
-
uniform3iv(location, value)
-
uniform4iv(location, value)
参数的含义与vertexAttrib[1234][fv]
相同,不同的是,uniform
变量可以设置整数类型的变量,即将其中的f
换成i
便代表整数类型,传入的数值类型也为整数。
对于uniform矩阵,有以下三个接口可以使用
-
uniformMatrix2fv(location, transpose, value)
-
uniformMatrix3fv(location, transpose, value)
-
uniformMatrix4fv(location, transpose, value)
其中,location
表示uniform变量的地址,transpose
为一个GLboolean
类型的值,表示是否转置,默认为false
,value
是一个Float32Array
类型的结构化数组,以列主序的方式指定矩阵的值
还有一个函数,用于获取指定位置的uniform变量的值
WebGLRenderingContext.getUniform(program, location)
获取uniform变量的值
-
program
: WebGLProgram,webgl程序对象 -
location
: GLint,变量位置,可以由getUniformLocation获取
该函数的返回值跟据变量类型的不同而改变,对应类型如下表:
Uniform类型 | 返回类型 |
---|---|
Uniform type | Returned type |
WebGL 1 only | |
boolean | GLBoolean |
int | GLint |
float | GLfloat |
vec2 | Float32Array (with 2 elements) |
ivec2 | Int32Array (with 2 elements) |
bvec2 | Array of GLBoolean (with 2 elements) |
vec3 | Float32Array (with 3 elements) |
ivec3 | Int32Array (with 3 elements) |
bvec3 | Array of GLBoolean (with 3 elements) |
vec4 | Float32Array (with 4 elements) |
ivec4 | Int32Array (with 4 elements) |
bvec4 | Array of GLBoolean (with 4 elements) |
mat2 | Float32Array (with 4 elements) |
mat3 | Float32Array (with 9 elements) |
mat4 | Float32Array (with 16 elements) |
sampler2D | GLint |
samplerCube | GLint |
Additionally available in WebGL 2 | |
uint | GLuint |
uvec2 | Uint32Array (with 2 elements) |
uvec3 | Uint32Array (with 3 elements) |
uvec4 | Uint32Array (with 4 elements) |
mat2x3 | Float32Array (with 6 elements) |
mat2x4 | Float32Array (with 8 elements) |
mat3x2 | Float32Array (with 6 elements) |
mat3x4 | Float32Array (with 12 elements) |
mat4x2 | Float32Array (with 8 elements) |
mat4x3 | Float32Array (with 12 elements) |
any sampler type | GLint |
赋值多个数据(使用缓冲区)
一般情况下,一个图形程序中有很多要绘制的对象,每个对象的位置、颜色等信息都不相同,但是用的是同一份shader源码,此时,如果使用上面的方法给shader中的变量赋值,那么最终渲染的结果会发现所有的东西都是一模一样的。此时,就需要使用缓冲区对每个渲染对象赋值不同的值。
缓冲区可以这么理解,就是开辟一个内存空间,把所有对象的位置信息、颜色信息等各种数据存储起来,当执行到这个对象对应的shader时,从内存中找到这个对象对应的数据,然后对变量进行赋值。这样,每次渲染的时候,每个对象都可以绘制成不同的样子。
使用缓冲区给变量赋值的过程分为如下几步:
- 创建缓冲区对象
- 将缓冲区对象绑定到目标
- 向缓冲区中写入数据
- 找到变量的位置
- 将缓冲区对象分配给变量
- 链接变量与缓冲区对象
示例代码
// 定义顶点数据
let vertices = new Float32Array([
0.0, 0.5, -0.5, -0.5, 0.5, -0.5
]);
// 创建顶点缓冲区
let vertexBuffer = gl.createBuffer();
if (!vertexBuffer) {
alert("缓冲区对象创建失败!");
}
// 绑定顶点缓冲区
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
// 绑定顶点数据,并定义绘制方式
gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
// 获取attribute变量地址
let aVertexPosition = gl.getAttribLocation(shaderProgram, "aVertexPosition");
if (aVertexPosition < 0) {
alert('查找变量aVertexPosition失败!');
}
// 将缓冲区对象分配给变量
gl.vertexAttribPointer(aVertexPosition, 2, gl.FLOAT, false, 0, 0);
// 链接变量与缓冲区对象
gl.enableVertexAttribArray(aVertexPosition);
接口
WebGLBuffer WebGLRenderingContext.createBuffer()
创建一个buffer对象
void WebGLRenderingContext.bindBuffer(target, buffer)
将一个Buffer对象绑定到目标
-
target
:GLenum,可以是以下几种:gl.ARRAY_BUFFER
,包含顶点属性的buffer,如顶点坐标、纹理坐标数据或顶点颜色数据gl.ELEMENT_ARRAY_BUFFER
:用于元素索引的buffer- 在webgl2,还可以使用以下值:
gl.COPY_READ_BUFFER
:从一个buffer对象复制到另一个buffer对象gl.COPY_WRITE_BUFFER
:从一个buffer对象复制到另一个buffer对象gl.TRANSFORM_FEEDBACK_BUFFER
:buffer for transform feedback operationsgl.UNIFORM_BUFFER
:用于存储统一块的buffergl.PIXEL_PACK_BUFFER
:用于像素传输操作的buffergl.PIXEL_UNPACK_BUFFER
:用于像素传输操作的buffer
-
buffer
:要绑定的buffer
webgl1中
void WebGLRenderingContext.bufferData(target, size, usage)
void WebGLRenderingContext.bufferData(target, ArrayBuffer? srcData, usage)
void WebGLRenderingContext.bufferData(target, ArrayBufferView srcData, usage)
webgl2中
void WebGLRenderingContext.bufferData(target, ArrayBufferView srcData, usage, srcOffset, length)
创建并初始化buffer对象的数据存储区
-
target
: GLenum,指定绑定目标,可以时以下值:gl.ARRAY_BUFFER
,包含顶点属性的buffer,如顶点坐标、纹理坐标数据或顶点颜色数据gl.ELEMENT_ARRAY_BUFFER
:用于元素索引的buffer- 在webgl2,还可以使用以下值:
gl.COPY_READ_BUFFER
:从一个buffer对象复制到另一个buffer对象gl.COPY_WRITE_BUFFER
:从一个buffer对象复制到另一个buffer对象gl.TRANSFORM_FEEDBACK_BUFFER
:buffer for transform feedback operationsgl.UNIFORM_BUFFER
:用于存储统一块的buffergl.PIXEL_PACK_BUFFER
:用于像素传输操作的buffergl.PIXEL_UNPACK_BUFFER
:用于像素传输操作的buffer
-
size
:GLsizeiptr:设定buffer对象的数据存储区大小 -
srcData(optional)
:一个ArrayBuffer、SharedArrayBuffer或者ArrayBufferView类型的数组对象,将被复制到buffer的数据存储区,如果为null,数据存储区会被创建,但是不会进行初始化和定义 -
usage
:GLenum,指定数据存储区的使用方法,可以是以下值:gl.STATIC_DRAW
:缓冲区的内容可能经常使用,而不会经常修改,内容被写入缓冲区,但不被读取gl.DYNAMIC_DRAW
:缓冲区的内容可能经常被使用,并且经常更改,内容被写入缓冲区,但不被读取gl.STREAM_DRAW
:缓冲区的内容可能不会经常使用,内容被写入缓冲区,但不被读取- 使用webgl2时,还可以使用以下值:
gl.STATIC_READ
:缓冲区的内容可能经常使用,而不会经常修改,内容从缓冲区读取,但不写入gl.DYNAMIC_READ
:缓冲区的内容可能经常被使用,并且经常更改,内容从缓冲区读取,但不写入gl.STREAM_READ
:缓冲区的内容可能不会经常使用,内容从缓冲区读取,但不写入gl.STATIC_COPY
:缓冲区的内容可能经常使用,而不会经常修改,内容既不从缓冲区读取,也不写入gl.DYNAMIC_COPY
:缓冲区的内容可能经常被使用,并且经常更改,内容既不从缓冲区读取,也不写入gl.STREAM_COPY
:缓冲区的内容可能不会经常使用,内容既不从缓冲区读取,也不写入
-
srcOffset
:GLuint,指定读取缓冲时的初始元素索引偏移量 -
length(optional)
:GLuint,默认为0
void WebGLRenderingContext.vertexAttribPointer(index, size, type, normalized, stride, offset)
告诉显卡从当前绑定的缓冲区中读取顶点数据。
-
index
:GLuint,指定要修改的顶点的索引 -
size
:GLint,指定每个顶点属性的组成数量,必须是1,2,3,4之一 -
type
:GLenum,指定数组中每个元素的数据类型,可以是以下值:gl.BYTE
:有符号8位整数,范围[-128, 127]gl.SHORT
:有符号16位整数,范围[-32768, 32767]gl.UNSIGNED_BYTE
:无符号8位整数,范围[0, 255]gl.UNSIGNED_SHORT
:无符号16位整数,范围[0, 65535]gl.FLOAT
:32位IEEE标准的浮点数gl.HALF_FLOAT(webgl2)
:16位IEEE标准的浮点数
-
normalized
:GLboolean,当转换为浮点数时是否应该将整数数值归一化到特定的范围- 对于BYTE和SHORT,将归一化到[-1, 1]
- 对于UNSIGNED_BYTE和UNSIGNED_SHORT,将归一化到[0, 1]
- 对于FLOAT和HALF_FLOAT,此参数无效
-
stride
:GLsizei,以字节为单位指定连续顶点属性开始之间的偏移量 -
offset
:GLintptr,指定顶点属性数组中第一部分的字节偏移量,必须是类型的字节长度的倍数
void WebGLRenderingContext.enableVertexAttribArray(index)
打开属性数组列表中指定索引处的通用点点属性数组
index
:GLuint,指向要激活的顶点属性
如果要关闭顶点属性数组,可以使用以下接口
void WebGLRenderingContext.disableVertexAttribArray(index)
关闭属性数组列表中指定索引处的通用点属性数组
index
:GLuint,指定要关闭的顶点属性