前言
书接上回,在上篇文章中我们一起探讨了 WebGL 的着色器,并了解到了如何创建着色器以及将其绘制到画布当中。
那么~!就有一个问题了,当我们想改变顶点着色器的位置数据或者片元着色器中的颜色数据要怎么做?
总不能修改着色器源码(GLSL)后,再走一次着色器的创建和渲染流程吧?那太繁琐了!
那么有更优雅的形式来变更着色器中的数据吗?
答案是肯定的,这也是我们这次需要讨论的内容 Attribute
变量以及 Uniform
变量!
注释1:代码示例部分都会把 HTML 省略掉!同时也去掉了之前的注释以让代码更加简洁!
Attribute 变量
Attribute
变量(属性变量)只能在 顶点着色器
中定义,它的作用是接收 JavaScript
程序传递过来的与 顶点
有关的数据,比如 顶点颜色
、法线
、坐标
等,它们是顶点的属性。
注释2:目前可以不纠结属性变量的可用范围,先简单理解它可以动态修改顶点着色器的位置数据即可!
声明 Attribute 变量并赋值
要使用 Attribute
变量,就必须修改之前的着色器源码。
首先需要在 main
函数外面,预先定义 Attribute
变量!而且在 main
函数内部定义的话是会报错的!
Attribute
变量的定义语法为:
变量类型 数据类型 变量名称
attribute vec4 aPosition;
具体示例如下所示:
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
变量(定义的属性变量的变量名)的 存储地址
!
// 使用 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(local, new Float32Array([10.0, 5.0, 2.0]))
// 修改 attribute 变量的值
ctx.vertexAttrib1f(aPosition, x)
// 重新绘制
ctx.drawArrays(ctx.POINTS, 0, 1)
Uniform 变量
Uniform
变量是用来修饰全局变量,它既可以在 顶点着色器
中定义,也可以在 片元着色器
中定义,用来接收与 顶点
无关的数据,例如:标量(如整数、浮点数)
、向量
、矩阵
、颜色
等。
声明 Uniform 变量并赋值
Uniform
变量的使用基本规则和 Attribute
变量基本一样!
需要注意的是在片元着色器
里面需要声明变量的数据类型精度
,因为片元着色器没有默认的数据类型精度
,但是顶点着色器有,当然养成好习惯就是都定义数据类型的精度。
Uniform
变量的定义语法为:
变量类型 数据类型 变量名称
uniform vec4 aPosition;
限定符 精度 数据类型
precision mediump float
具体示例如下所示:
const fragmentShaderSource = `
// 指定精度,顶点着色器默认是高精度,所以不用设置,但是片元着色器没有默认精度
// 低精度:lowp 中精度:mediump 高精度:highp
precision mediump float;
// 存储限定符 类型 变量名
uniform vec4 uColor;
void main () {
gl_FragColor = uColor;
}
`
获取程序对象中 Uniform
变量的存储地址和渲染
同样基本上和 Attribute
变量的规则基本一致!
这里我们需要用到 getUniformLocation 方法。
getUniformLocation
方法可以从指定的程序对象(WebGLProgram)中获取指定的 Uniform
变量(定义的属性变量的变量名)的 存储地址
!
// 使用方法和 getAttribLocation 一样
// 程序对象 变量名
const uColor = ctx.getUniformLocation(program, 'uColor')
获取了指定 Uniform
变量的存储地址后,也需要通过 uniform[1234][fi][v] 方法族来修改变量,也是对标 Attribute
变量的 vertexAttrib[1,2,3,4]f[v]
的方法族,用法也相似,就不多说了,点进去看文档吧。
// 修改 uniform 变量的值
ctx.uniform4f(uColor, 1.0, 0.0, 0.0, 1.0)
// 重新绘制
ctx.drawArrays(ctx.POINTS, 0, 1)
Varying 变量
Varying
变量是顶点着色器用来向片元着色器中传递数据
的,所以 Varying
变量一般需要在顶点着色器和片元着色器中同时定义
,Varying
变量在传递给片元着色器的时候会进行插值化
处理。
声明 Varying 变量
Varying
变量的定义需要同时
在顶点着色器和片元着色器中定义!
Varying
变量的定义语法为:
const vertex = `
precision mediump float;
attribute vec4 aPosition;
varying vec4 vColor; // 定义 varying 变量
void main() {
gl_Position = aPosition;
gl_PointSize = 10.0;
}
`
const fragment = `
precision mediump float;
varying vec4 vColor; // 定义(也可以叫接收) varying 变量
void main() {
gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
}
`
使用 Varying 变量
Varying
变量和 Attribute
or Uniform
变量不太一样,没有相关的 api
获取它的内存地址以及赋值 api
所以它在顶点着色器中需要借助 Attribute
和 Uniform
变量进行取值,然后再将值赋予给 Varying
变量。
Varying
变量的使用语法为:
const vertex = `
precision mediump float;
attribute vec4 aPosition;
varying vec4 vColor; // 定义 varying 变量
void main() {
vColor = aPosition; // 把 aPosition 的值赋给 vColor
gl_Position = aPosition;
gl_PointSize = 10.0;
}
`
const fragment = `
precision mediump float;
// 这里会自动接收顶点着色器传递下来的值
// 稍微提一嘴插值化:
// 打个比方我们用顶点着色器画了一条线段;
// 相当于我们有两个顶点坐标,那么两个顶点中间的'连线'信息是怎么来的呢?;
// 就是通过插值化计算出来的,它会计算两个顶点之间每一个点的坐标信息并一个一个的渲染。
varying vec4 vColor;
void main() {
gl_FragColor = vColor; // 使用 vColor 的值
}
`
Attribute
变量的内存地址获取和赋值就不在赘述,可以往上翻翻看。
总结
怎么说呢!对 GLSL 的语法了解还是太浅了,而且也没什么好的调试方法,出错了真就只能一行一行的看,相当困难!
当然先整些容易出效果的案例,后面对 WebGL 了解到一定程度后,再来系统的学习 GLSL 语法,可能会更让人容易接受吧!
完整代码太长了就不贴文章里面了,查看完整的代码用例 -> 地址 !