1. 平移
顶点着色器
attribute vec4 pos;
// 新增变量
attribute float offset;
void main() {
// 为主使用pos分量加上offset改变
gl_Position = vec4(pos.x + offset, pos.y, pos.z, 1);
}
const offset = gl.getAttribLocation(program, 'offset')
let x = 0
setInterval(() => {
if(x > 1) x = -1
gl.vertexAttrib1f(offset, x)
x += 0.05
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 3)
}, 60)
效果
2. 缩放
和平移一样的原理,只不过是倍数移动坐标
顶点着色器
attribute vec4 pos;
attribute float scale;
void main() {
// 乘以坐标值
gl_Position = vec4(pos.x * scale, pos.y * scale, pos.z, 1);
}
const scale = gl.getAttribLocation(program, 'scale')
setInterval(() => {
if(x > 2) x = 1
gl.vertexAttrib1f(scale, x)
x += 0.1
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 3)
}, 60)
3.旋转
顶点着色器
attribute vec4 pos;
attribute float deg;
void main() {
// 利用公式绕z旋转
// 其实画个图用就明白了
gl_Position.x = pos.x * cos(deg) - pos.y * sin(deg);
gl_Position.y = pos.x * sin(deg) + pos.y * cos(deg);
gl_Position.z = pos.z;
gl_Position.w = pos.w;
}
let x = 0
const rotate = () => {
x += 0.05
gl.vertexAttrib1f(deg, x)
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 3)
// 使用了requestAnimationFrame,让动画流畅些
requestAnimationFrame(rotate)
}
rotate()
4.平移矩阵
上面的平移旋转无非就是一些线性变换,或者说解方程。所以天然和线性代数契合,如果把变换转化成矩阵计算,只剩下加、乘,计算机计算也会变得快速简单。
5.旋转矩阵
参考这个图:从p -> p'
上面的推导中,旋转矩阵是3X3
,而平移矩阵为4X4
,阶数不同不能做计算组合起来。所以加一维。
6. 缩放矩阵
7.矩阵变换使用
我们以旋转为例。
顶点着色器:
attribute vec4 pos;
// 矩阵只能是uniform
// 用attribute无法赋值
uniform mat4 routeMatrix;
void main(){
// 左乘旋转矩阵
gl_Position = routeMatrix * pos;
gl_PointSize = 30.0;
}
片段着色器:
precision mediump float;
void main(){
gl_FragColor = vec4(1, 0, 0, 1);
}
主程序
import vertexCode from './vertex.vert'
import fragmentCode from './fragment.frag'
////////////////////////// 初始化 ///////////////////////////////////
const canvas = document.getElementById('webgl') as HTMLCanvasElement
const gl = canvas.getContext('webgl')
const vertexShader = gl.createShader(gl.VERTEX_SHADER)
const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER)
gl.shaderSource(vertexShader, vertexCode)
gl.shaderSource(fragmentShader, fragmentCode)
gl.compileShader(vertexShader);
gl.compileShader(fragmentShader);
const program = gl.createProgram();
gl.attachShader(program, vertexShader)
gl.attachShader(program, fragmentShader)
gl.linkProgram(program);
gl.useProgram(program);
const pos = gl.getAttribLocation(program, 'pos')
const buffer = gl.createBuffer()
gl.bindBuffer(gl.ARRAY_BUFFER, buffer)
gl.bufferData(
gl.ARRAY_BUFFER,
new Float32Array([
-0.5, 0.5,
-0.5, -0.5,
0.5, 0.5,
0.5, -0.5,
]),
gl.STATIC_DRAW
)
gl.vertexAttribPointer(pos, 2, gl.FLOAT, false, 0, 0)
gl.enableVertexAttribArray(pos)
////////////////////////// 初始化end ///////////////////////////////////
/**************************** 旋转部分代码 ***********************/
const routeMatrix = gl.getUniformLocation(program, 'routeMatrix')
const angle = 45
const b = Math.PI * angle / 180
/**
* 原始旋转矩阵是
* cos -sin 0 0
* sin cos 0 0
* 0 0 1 0
* 0 0 0 1
*
* 由于webgl里面是按列主序存储所以应该是下面这样
*/
const routeMatrixValue = new Float32Array([
Math.cos(b), Math.sin(b), 0, 0,
-Math.sin(b), Math.cos(b), 0, 0,
0, 0, 1, 0,
0, 0, 0, 1
])
/**
* 矩阵赋值
* @param location 变量地址
* @param transpose 是否转置,在webgl里面没有转置方法所以只能false
* @param value 值
*/
gl.uniformMatrix4fv(routeMatrix, false, routeMatrixValue)
//////////////// 绘制 ////////////////////
gl.clearColor(0, 0, 0, 1)
gl.clear(gl.COLOR_BUFFER_BIT)
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4)
红色方块被旋转了45度。
8.组合使用
这里开始我们使用工具库来做矩阵操作,我使用的是gl-matrix
。
// 使用例子
import { mat4 } from 'gl-matrix'
// 创建一个旋转45度矩阵
const modelMatrix = mat4.create()
mat4.fromZRotation(modelMatrix, 45)
下面我们来创建一个模型矩阵
// 还是上面的代码
const routeMatrix = gl.getUniformLocation(program, 'routeMatrix')
// 创建4阶矩阵变量
const modelMatrix = mat4.create()
const transM = mat4.create()
const rotatM = mat4.create()
// 创建平移矩阵
mat4.fromTranslation(transM, [0.7, 0, 0])
// 创建旋转矩阵
mat4.fromZRotation(rotatM, 45)
// 叉乘矩阵形成模型矩阵
// 注意先平移和后平移效果是不一样的
mat4.multiply(modelMatrix, transM, rotatM)
gl.uniformMatrix4fv(routeMatrix, false, modelMatrix)
gl.clearColor(0, 0, 0, 1)
gl.clear(gl.COLOR_BUFFER_BIT)
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4)
代码效果
工具库里有直接生成旋转平移矩阵的函数:
fromRotationTranslation
const routateQuat = quat.create()
quat.rotateZ(routateQuat, routateQuat, 30)
// 注意第二个参数是一个四元数,什么是四元数有点复杂,有时间再研究
mat4.fromRotationTranslation(modelMatrix, routateQuat, [0.3, 0, 0])
gl.uniformMatrix4fv(routeMatrix, false, modelMatrix)