前言
书接上回,在上篇文章中我们一起探讨了 WebGL 的着色器,并了解到了如何创建着色器以及将其绘制到画布当中。
那么~!就有一个问题了,当我们想改变顶点着色器的位置数据或者片元着色器中的颜色数据要怎么做?
总不能修改着色器源码(GLSL)后,再走一次着色器的创建和渲染流程吧?那太繁琐了!
那么有更优雅的形式来变更着色器中的数据吗?
答案是肯定的,这也是我们这次需要讨论的内容 Attribute
变量以及 Uniform
变量!
注释1:代码示例部分都会把 HTML 省略掉!同时也去掉了之前的注释以让代码更加简洁!
Attribute 变量
Attribute
变量(属性变量)只能在 顶点着色器
中定义,它的作用是接收 JavaScript
程序传递过来的与 顶点
有关的数据,比如 顶点颜色
、法线
、坐标
等,它们是顶点的属性。
注释2:目前可以不纠结属性变量的可用范围,先简单理解它可以动态修改顶点着色器的位置数据即可!
声明 Attribute 变量并赋值
要使用 Attribute
变量,就必须修改之前的着色器源码。
首先需要在 main
函数外面,预先定义 Attribute
变量!而且在 main
函数内部定义的话是会报错的!
Attribute
变量的定义语法为:
ini
变量类型 数据类型 变量名称
attribute vec4 aPosition;
具体示例如下所示:
csharp
const vertexShaderSource = `
// 这里就是定义 Attribute 变量,变量名为 aPosition,变量的数据类型为 vec4!
// 注意,Attribute 变量的声明是在 main 函数外面!!
// 这里虽然没有赋值,但是实际上是有默认值的,默认值是: vec4(0.0, 0.0, 0.0, 1.0) !!!
attribute vec4 aPosition;
// ----
void main () {
// 赋值
gl_Position = aPosition;
gl_PointSize = 100.0;
}
`
获取程序对象中 Attribute
变量的存储地址和渲染
这里主要是描述如何对已经渲染后的顶点着色器中的 Attribute
变量进行修改,这里我们需要用到 getAttribLocation 方法。
getAttribLocation
方法可以从指定的程序对象(WebGLProgram)中获取指定的 Attribute
变量(定义的属性变量的变量名)的 存储地址
!
arduino
// 使用 getAttribLocation 方法获取某个 程序对象 中的 存储地址
// 程序对象 变量名
const aPosition = ctx.getAttribLocation(program, 'aPosition')
获取了指定 Attribute
变量的存储地址后,还需要给其设置新的值,并进行渲染操作才能真正的让画布更新,想要对存储地址重新设置值就需要用到 vertexAttrib[1,2,3,4]f[v] 方法族,这里只需要修改 x 坐标的值,所以可以直接使用 vertexAttrib1f
方法来修改 x 坐标的数值。
vertexAttrib[1,2,3,4]f[v] 中的 1,2,3,4 可以理解成入参个数分别对应 vec4 的数据格式,v 表带浮点数组类型,vertexAttrib3fv(new Float32Array(local, [10.0, 5.0, 2.0]))
scss
// 修改 attribute 变量的值
ctx.vertexAttrib1f(aPosition, x)
// 重新绘制
ctx.drawArrays(ctx.POINTS, 0, 1)
Uniform 变量
Uniform
变量是用来修饰全局变量,它既可以在 顶点着色器
中定义,也可以在 片元着色器
中定义,用来接收与 顶点
无关的数据,例如:标量(如整数、浮点数)
、向量
、矩阵
、颜色
等。
声明 Uniform 变量并赋值
Uniform
变量的使用基本规则和 Attribute
变量基本一样!
需要注意的是,在使用 Uniform
变量进行声明的时候,需要声明变量的数据类型,你问我为什么?emmm 我母鸡呀~!先就这么记吧!当然如果你知道为什么的话麻烦告诉我一下,感谢!🤪
Uniform
变量的定义语法为:
sql
变量类型 数据类型 变量名称
uniform vec4 aPosition;
限定符 精度 数据类型
precision mediump float
具体示例如下所示:
csharp
const fragmentShaderSource = `
// 指定精度,顶点着色器默认是高精度,所以不用设置,但是片元着色器没有默认精度
// 低精度:lowp 中精度:mediump 高精度:highp
precision mediump float;
// 存储限定符 类型 变量名
uniform vec4 uColor;
void main () {
gl_FragColor = uColor;
}
`
获取程序对象中 Uniform
变量的存储地址和渲染
同样基本上和 Attribute
变量的规则基本一致!
这里我们需要用到 getUniformLocation 方法。
getUniformLocation
方法可以从指定的程序对象(WebGLProgram)中获取指定的 Uniform
变量(定义的属性变量的变量名)的 存储地址
!
arduino
// 使用方法和 getAttribLocation 一样
// 程序对象 变量名
const uColor = ctx.getUniformLocation(program, 'uColor')
获取了指定 Uniform
变量的存储地址后,也需要通过 uniform[1234][fi][v] 方法族来修改变量,也是对标 Attribute
变量的 vertexAttrib[1,2,3,4]f[v]
的方法族,用法也相似,就不多说了,点进去看文档吧。
scss
// 修改 uniform 变量的值
ctx.uniform4f(uColor, 1.0, 0.0, 0.0, 1.0)
// 重新绘制
ctx.drawArrays(ctx.POINTS, 0, 1)
总结
怎么说呢!对 GLSL 的语法了解还是太浅了,而且也没什么好的调试方法,出错了真就只能一行一行的看,相当困难!
当然先整些容易出效果的案例,后面对 WebGL 了解到一定程度后,再来系统的学习 GLSL 语法,可能会更让人容易接受吧!
完整代码太长了就不贴文章里面了,查看完整的代码用例 -> 地址 !