WebGL - 初相识 - 着色器 - 变量

370 阅读6分钟

前言

书接上回,在上篇文章中我们一起探讨了 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 所以它在顶点着色器中需要借助 AttributeUniform 变量进行取值,然后再将值赋予给 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 语法,可能会更让人容易接受吧!

完整代码太长了就不贴文章里面了,查看完整的代码用例 -> 地址 !