北鸟南游的博客 北鸟南游的博客
首页
  • 前端文章

    • JavaScript
    • Nextjs
  • 界面

    • html
    • css
  • 计算机基础
  • 后端语言
  • linux
  • mysql
  • 工具类
  • 面试相关
  • 图形学入门
  • 入门算法
  • 极客专栏
  • 慕课专栏
  • 电影资源
  • 儿童动漫
  • 英文
关于我
归档
GitHub (opens new window)
首页
  • 前端文章

    • JavaScript
    • Nextjs
  • 界面

    • html
    • css
  • 计算机基础
  • 后端语言
  • linux
  • mysql
  • 工具类
  • 面试相关
  • 图形学入门
  • 入门算法
  • 极客专栏
  • 慕课专栏
  • 电影资源
  • 儿童动漫
  • 英文
关于我
归档
GitHub (opens new window)
  • 面试
  • 算法入门
  • 图形学入门
    • threeJs使用
    • webgl基础1-着色器、矩阵变换
      • 基础概念
        • 着色器
        • 左右手坐标系
        • 变量:
      • 创建第一个webgl程序
        • 完整的绘制代码:
        • 以上绘制流程抽象为一个initShader方法
      • 移动点位置、定义点位置
        • 使用vertexAttrib[n]f方法改变点位置
        • 使用buffer数据设置点的位置
      • drawArrays绘制多点图形
        • 示例代码:展示4边形
        • 绘制2个三角形
      • 使用数学公式进行操作
        • 平移
        • 旋转
        • 缩放
      • 使用矩阵进行操作
        • 平移
        • 旋转
        • 缩放
        • 通用矩阵变换公式
      • 矩阵的混合变换
        • 通过定义多个uniform变量,设置矩阵参数,进行运算
        • 将2个矩阵进行复合运算
    • webgl基础1
    • webgl基础2-颜色、纹理
    • webgl基础3-三维
    • webgl基础4-灯光
  • 极客专栏
  • 慕课专栏
  • vue3+vite封装element组件库
  • 基于云开发模式开发微信小程序
  • 珠峰培训资料
  • theme
  • graphics
北鸟南游
2024-04-27
目录

webgl基础1-着色器、矩阵变换

# 基础概念

# 着色器

着色器:使用webgl绘图,必须使用着色器。着色器程序以字符串的形式“嵌入”javascript文件中。类似c的编程语言,来实现视觉效果, 【着色器源码中的结尾分号“;” 是必须不能少】。webgl中共有2种着色器

  • 顶点着色器:用来描述顶点特性(位置、颜色)信息,
  • 片元着色器:进行逐片元处理过程如:光照的程序;

webgl程序中,图形的绘制主要就是依赖着色器代码实现。着色器可以灵活实现光照、灯光、阴影、贴图等渲染效果。

image.png

# 左右手坐标系

openGL中使用的是右手坐标系 (opens new window);

  • 右手坐标系,z轴正方向是从屏幕里向外
  • 左手坐标系,z轴正方向是从外向屏幕里

# 变量:

attribute、uniform、varying可以定义js和webgl【着色器】中传递数据的变量。

  • attribute:定义的是与顶点相关的数据,例如位置;只能用在顶点着色器中。
  • uniform:定义对应所有顶点都相同的数据(与顶点无关),例如:颜色,大小
  • varying: 定义必须是全局变量,目的是从顶点到片元着色器传输数据。必须同时在两种着色器中声明同名、同类型的varying变量。

定义变量的格式:<存储限定符> <类型> <变量名>;

⚠️:行结尾的分号不可以漏掉;

attribute vec4 a_Position;
1

vec4: 表示有4个浮点数定义的矢量

# 齐次坐标

在坐标定义中,vec4变量中的第4个值表示齐次坐标值。

齐次坐标(x,y,z,w)等价于三维坐标(x/w,y/w,z/w)。所以如果第4个分量值是1,就可以把vec4定义的矢量当作三维坐标使用。

w的值必须大于等于0,如果是趋近0,就表示点趋近无穷远。

矩阵坐标乘法会经常用到齐次坐标。 绘制操作函数
drawArrays:用来绘制各种图形;共3个参数,mode、first、count
●mode:指定绘制方式,有gl.POINTS, gl.LINES, gl.LINE_STRIP, gl.LINE_LOOP, gl.TRIANGLES, gl.TRIANGLE_STRIP, gl.TRIANGLE_FAN
●first: 指定从哪个顶点开始绘制,【整型数】
●count:指定绘制需要用多少个顶点【整型数】
错误:
INVALID_ENUM: 传入的mode参数不合法
INVALID_VALUE: 参数first或count为负数
类型化数组
javascript中Array定义的变量不能限定变量的类型,这对于需要处理大量数据的三维绘图程序来说性能低。
在webgl中通常会处理大量同类型的数据,为了优化性能,webgl为每种基本数据类型引入了特殊的类型化数组。
例如:Float32Array是32位浮点类型数组,通常用来存储顶点的坐标或颜色数据。

数组类型 占的字节 描述(c语言中的数据类型)
Int8Array 1 8位整型数(signed char)
UInt8Array 1 8位无符号整型数(unsigned char)
Int16Array 2 16位整型数(signed short)
UInt16Array 2 16位无符号整型数(unsigned short)
Int32Array 4 32位整型数(signed int)
UInt32Array 4 32位无符号整型数(unsigned int)
Float32Array 4 单精度32位浮点数(float)
Float64Array 8 双精度64位浮点数(double)

注意:与普通的javascript数组不同的是,类型化数组不支持push和pop方法
类型化数组的方法

方法、属性、常量 描述
get(index) 获取第 index 个元素
set(index, value) 设置第 index 个元素的值为value
set(array, offset) 从第 offset 个元素开始,将数组 array 中的值填充进去
length 数组的长度
BYTES_PER_ELEMENT 数组中每个元素所占的字节数

创建类型化数组

// 使用 new 关键字是创建类型化数组的唯一方式,
new Float32Array([1.0, 2.0, -1.0, 2.0]);

// 也可以指定数组元素个数创建一个空的类型化数组
new Float32Array(4);
1
2
3
4
5

# 创建第一个webgl程序

# 1.在html中添加canvas标签

<div>
    <canvas id="canvas" width="400" height="400"></canvas>
</div>
1
2
3

# 2.设置绘图上下文

// 获取canvas元素
const ctx = document.getElementById('canvas')

// 获取webgl绘图上下文
const gl = ctx.getContext('webgl')
1
2
3
4
5

# 3.设置顶点着色器和片元着色器 代码

// 顶点着色器代码
const VERTEX_SHADER = `
    void main(){
        gl_Position = vec4(0.0,0.0,0.0,1.0);
        gl_PointSize = 15.0;
    }
`;

// 片元着色器代码
const FRAGMENT_SHADER = `
    void main(){
        gl_FragColor = vec4(1.0,0.0,0.0,1.0);
    }
`;
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# 4.创建着色器对象

// 创建顶点着色器
const vertexShader = gl.createShader(gl.VERTEX_SHADER);

// 创建片元着色器
const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
1
2
3
4
5

# 5.将着色器代码添加到着色器对象中

// 将顶点着色器代码添加到顶点着色器中
gl.shaderSource(vertexShader, VERTEX_SHADER);

// 将片元着色器代码添加到片元着色器中
gl.shaderSource(fragmentShader, FRAGMENT_SHADER);
1
2
3
4
5

# 6.编译着色器对象

// 添加完成后,需要编译着色器对象
gl.compileShader(vertexShader);
gl.compileShader(fragmentShader);
1
2
3

# 7.创建程序对象,将着色器对象添加到程序对象中

const program = gl.createProgram();
// 将着色器添加到程序中
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
1
2
3
4

# 8.关联程序对象

// 关联program
gl.linkProgram(program);
1
2

# 9.使用程序对象

// 使用program
gl.useProgram(program);
1
2

# 10.设置背景色

// 设置背景色
gl.clearColor(0.2, 0.8, 0.0, 1.0);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER);
1
2
3

# 11.绘制点

// 绘制点
gl.drawArrays(gl.POINTS, 0, 1);
1
2

# 完整的绘制代码:

jcode (opens new window)

image.png

# 以上绘制流程抽象为一个initShader方法

/**
 * @description: 生成webgl着色器对象的程序
 * @param {*} gl gl的上下文对象;ctx.getContext('webgl')
 * @param {*} VERTEX_SHADER_SOURCE;顶点着色器源码
 * @param {*} FRAGMENT_SHADER_SOURCE;片元着色器源码
 * @return {*} 程序对象
 */
function initShader(gl, VERTEX_SHADER_SOURCE, FRAGMENT_SHADER_SOURCE) {
    const vertexShader = gl.createShader(gl.VERTEX_SHADER);
    const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);

    gl.shaderSource(vertexShader, VERTEX_SHADER_SOURCE) // 指定顶点着色器的源码
    gl.shaderSource(fragmentShader, FRAGMENT_SHADER_SOURCE) // 指定片元着色器的源码

    // 编译着色器
    gl.compileShader(vertexShader)
    gl.compileShader(fragmentShader)

    // 创建一个程序对象
    const program = gl.createProgram();

    gl.attachShader(program, vertexShader)
    gl.attachShader(program, fragmentShader)

    gl.linkProgram(program)

    gl.useProgram(program)

    return program;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

# 移动点位置、定义点位置

# 使用vertexAttrib[n]f方法改变点位置

动态设置attribute属性,改变点的位置;

attribute变量是 GLSL ES 变量,被用来从外部【javascript】向着色器内传输数据,只有顶点着色器能使用attribute变量。

在顶点着色器中使用attribute,先进行定义;如下第3行表示定义 attribute变量

// // 顶点着色器代码
const VERTEX_SHADER = `
    attribute vec4 aPosition;
    void main(){
        gl_Position = aPosition;
        gl_PointSize = 15.0;
    }
`;
1
2
3
4
5
6
7
8

# 1: 使用getAttribLocation获取位置变量

let aPos = gl.getAttribLocation(program, name);获取有 name 参数指定的 attribute 变量的存储地址。

参数: program :指定包含顶点着色器和片元着色器的着色器程序对象

name: 指定想要获取其存储地址的 attribute 变量的名称

返回值: 大于等于0 attribute 变量的存储地址

-1 指定的attribute变量不存在

错误: INVALID_OPERATION 程序对象未能成功连接

INVALID_VALUE name参数的长度大于 attribute 变量名的最大长度(默认256)

# 2: 使用vertexAttrib1f进行设置变量

gl.vertexAttrib1f是一系列同族函数中的一个,该系列函数的任务是从javascript向顶点着色器中attribute属性传递值。

gl.vertexAttrib1f(aPos, x);

  • vertexAttrib1f:设置参数的1个变量,后边跟一个变量和一个参数
  • vertexAttrib2f: 设置参数的2个变量,vertexAttrib2f(aPos, x, y)
  • vertexAttrib3f: 设置参数的3个变量, vertexAttrib3f(aPos, x, y, z)
  • vertexAttrib4f: 设置参数的4个变量, vertexAttrib4f(aPos, x, y, z, w)

可以是3f也可是3i;f表示浮点数,i表示整数

# 完整代码

// 获取canvas元素
const ctx = document.getElementById('canvas')

// 获取webgl绘图上下文
const gl = ctx.getContext('webgl')

// // 顶点着色器代码
const VERTEX_SHADER = `
    attribute vec4 aPosition;
    attribute float aPosSize;
    void main(){
        gl_Position = aPosition;
        gl_PointSize = 15.0;
    }
`;

// 片元着色器代码
const FRAGMENT_SHADER = `
    void main(){
        gl_FragColor = vec4(1.0,0.0,0.0,1.0);
    }
`;

let program = initShader(gl, VERTEX_SHADER, FRAGMENT_SHADER);
// getAttribLocation,获取gl的program对象的locatiion参数
let aPos = gl.getAttribLocation(program, 'aPosition');
let aSize = gl.getAttribLocation(program, 'aPosSize');

gl.vertexAttrib1f(aSize, 18);
let x = 0.0;
setInterval(() => {
    x = x + 0.05;
    if (x < 1.0) {
      // 设置顶点的attribute数据
        gl.vertexAttrib1f(aPos, x);
    } else {
        x = 0.0;
    }
    // 设置背景色
    gl.clearColor(0.2, 0.8, 0.0, 1.0);
    gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER);

    // 绘制点
    gl.drawArrays(gl.POINTS, 0, 1);
}, 200)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45

通过定时器移动点的位置,可以创建出动画效果。后边的矩阵变换也是通过点位置坐标的改变实现。

# 使用buffer数据设置点的位置

1.使用createBuffer创建buffer对象const buffer = gl.createBuffer();

2.绑定buffer数据gl.bindBuffer(gl.ARRAY_BUFFER, buffer);

3.赋值buffer数据gl.bufferData(gl.ARRAY_BUFFER, points, gl.STATIC_DRAW);

4.使用vertexAttribPointer方法,对aPosition变量进行赋值gl.vertexAttribPointer(aPosition, 2, gl.FLOAT, false, 0, 0);

5.开启着色器中顶点的属性gl.enableVertexAttribArray(aPosition)

image.png

# buffer数据

缓冲区对象是webgl系统中一块存储区,可以在缓冲区对象保存想要绘制的所有顶点的数据。

要使用缓冲区数据,首先创建一个缓冲区,然后向其中写入顶点数据,这样就能一次性的向顶点着色器中传入多个顶点的attribute变量的数据。

  • createBuffer:创建缓冲区对象
  • bindBuffer:绑定缓冲区,2个参数
    • target:可以是ARRAY_BUFFER或者ELEMENT_ARRAY_BUFFER
    • buffer:由createBuffer创建出来的buffer数据
  • bufferData:向缓冲区对象写入数据,3个参数
    • target:可以是ARRAY_BUFFER或者ELEMENT_ARRAY_BUFFER
    • data:写入缓冲区对象的数据,一般指定义的类型化数组
    • usage:三种变量类型gl.STATIC_DRAW、gl.STREAM_DRAW、gl.DYNAMIC_DRAW

# vertexAttribPointer函数

将缓冲区对象分配给attribute变量,包含6个参数

  • location:指定待分配 attribute 变量的存储位置
  • size: 指定缓冲区每个顶点的分量个数
  • type:用来指定数据格式
    • gl.UNSIGNED_BYTE: 无符号字节,Unit8Array
    • gl.SHORT: 短整型,Int16Array
    • gl.UNSIGNED_SHORT:无符号短整型,Uint16Array
    • gl.INT:整型,Int32Array
    • gl.UNSIGNED_INT:无符号整型,Uint32Array
    • gl.FLOAT:浮点型,Float32Array
  • normalized: 布尔类型,表明是否将非浮点型的数据归一化
  • stride: 指定相邻两个顶点间的字节数,默认为0
  • offset: 指定缓冲区对象中的偏移量,即 attribute变量从缓冲区中何处开始存储。

最后通过gl.enableVertexAttribArray(position)函数开启 attribute 变量;执行该函数后,才算将缓冲区对象和attribute变量之间建立了真正连接。

# 完整示例代码

function initShader(gl, VERTEX_SHADER_SOURCE, FRAGMENT_SHADER_SOURCE) {
  const vertexShader = gl.createShader(gl.VERTEX_SHADER);
  const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);

  gl.shaderSource(vertexShader, VERTEX_SHADER_SOURCE) // 指定顶点着色器的源码
  gl.shaderSource(fragmentShader, FRAGMENT_SHADER_SOURCE) // 指定片元着色器的源码

  // 编译着色器
  gl.compileShader(vertexShader)
  gl.compileShader(fragmentShader)

  // 创建一个程序对象
  const program = gl.createProgram();

  gl.attachShader(program, vertexShader)
  gl.attachShader(program, fragmentShader)

  gl.linkProgram(program)

  gl.useProgram(program)

  return program;
}

const ctx = document.getElementById('canvas')

const gl = ctx.getContext('webgl')

// 创建着色器源码
const VERTEX_SHADER_SOURCE = `
    attribute vec4 aPosition;

    void main() {
      gl_Position = aPosition;
    }
  `; // 顶点着色器

const FRAGMENT_SHADER_SOURCE = `
    void main() {
      gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
    }
  `; // 片元着色器

const program = initShader(gl, VERTEX_SHADER_SOURCE, FRAGMENT_SHADER_SOURCE)

const aPosition = gl.getAttribLocation(program, 'aPosition');
const points = new Float32Array([
  -0.8, -0.8,
  0.8, -0.8,
  0.0,  0.8,
])

// 使用vertexAttribPointer给aPosition赋值
const buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.bufferData(gl.ARRAY_BUFFER, points, gl.STATIC_DRAW);
gl.vertexAttribPointer(aPosition, 2, gl.FLOAT, false, 0, 0);

gl.enableVertexAttribArray(aPosition);

gl.drawArrays(gl.TRIANGLES, 0, 3);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61

创建出红色三角形 image.png

# drawArrays绘制多点图形

drawArrays方法包含3个参数:mode、first、count

  • 设定不同的绘制方式,包括 POINTS、LINES、LINE_STRIP、LINE_LOOP、TRIANGLES、TRIANGLE_STRIP、TRIANGLE_FAN
  • 设置从哪个点开始绘制
  • 指定绘制需要几个顶点

设置不同的mode绘制方式,会显示不同的绘制效果。如下图:

# 示例代码:展示4边形

jcode (opens new window)

# 绘制2个三角形

# TRIANGLES三角形

// 修改坐标点数据
const points = new Float32Array([
  -0.5, -0.5,
  0.5, -0.5,
  -0.5,  0.5,
  0.5,  0.5,
  0.8,  0.9,
  0.9,  0.3,
])

// 修改绘制方式
gl.drawArrays(gl.TRIANGLES, 0, 6);
1
2
3
4
5
6
7
8
9
10
11
12

image.png

# TRIANGLE_STRIP

// 修改坐标点数据
const points = new Float32Array([
  -0.5, -0.5,
  0.5, -0.5,
  -0.5,  0.5,
  0.5,  0.5,
  0.8,  0.9,
  0.9,  0.3,
])

// 修改绘制方式
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 6);
1
2
3
4
5
6
7
8
9
10
11
12

image.png

# TRIANGLE_FAN

// 修改坐标点数据
const points = new Float32Array([
  -0.5, -0.5,
  0.5, -0.5,
  -0.5,  0.5,
  0.5,  0.5,
  0.8,  0.9,
  0.9,  0.3,
])

// 修改绘制方式
gl.drawArrays(gl.TRIANGLE_FAN, 0, 6);
1
2
3
4
5
6
7
8
9
10
11
12

image.png

# 使用数学公式进行操作

# 平移

通过修改着色器顶点数据,将原始数据和变量数据相加

jcode (opens new window)

# 旋转

绕着Z轴旋转,对x和y的坐标分别进行赋值。

gl_Position.x = aPosition.x * cos(deg) - aPosition.y * sin(deg);

gl_Position.y = aPosition.x * sin(deg) + aPosition.y * cos(deg);

jcode (opens new window)

# 缩放

将原始坐标值和变量进行相乘。 jcode (opens new window)

# 使用矩阵进行操作

# 矩阵的定义

  • 矩阵是横竖排列的数据表格
  • 作用是把一个点转移到另外一个位置
  • 只有在矩阵的列数和矢量【有多个分量组成的对象,如点坐标、颜色值等】的行数相等时,才可以将两者相乘

矩阵的乘法不符合交换率,AB和BA的结果并不相等;

上面的矩阵有3行3列,因为相乘的矢量具有3个分量,也被称为三维矢量;

x' = ax + by + cz;
y' = dx + ey + fz;
z' = gx + hy + iz;
1
2
3

# 矩阵分为行主序和列主序

image.png

// 行主序
new Float32Array([
    1.0, 2.0, 3.0, 4.0,
    5.0, 6.0, 7.0, 8.0,
    9.0, 10.0, 11.0, 12.0,
    13.0,14.0, 15.0, 16.0,
])

// 列主序
new Float32Array([
    1.0, 5.0, 9.0,  13.0,
    2.0, 6.0, 10.0, 14.0,
    3.0, 7.0, 11.0, 15.0,
    4.0, 8.0, 12.0, 16.0,
])
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# 平移

image.png

如上图,把A点坐标移动到 A' 点的位置,需要进行以下坐标移动

x' = x + x1;
y' = y + y1;
z' = z + z1;
1
2
3

image.png

w的值为1

x' = ax + by + cz + d;
y' = ex + fy + gz + h;
z' = ix + jy + kz + l;
w' = mx + ny + oz + p;
1
2
3
4

将公式1和公式2进行 等式 替换

ax + by + cz + d = x + x1; // 只有当a=1且 b=c=0,d=x1的时候,等式才成立
ex + fy + gz + h = y + y1; // 只有当f=1且 e=g=0,h=y1的时候,等式才成立
ix + jy + kz + l = z + z1; // 只有当k=1且 i=j=0,l=z1的时候,等式才成立
mx + ny + oz + p = 1;      // 只有当p=1且 m=n=o=0 的时候,等式才成立
1
2
3
4

image.png

// 平移矩阵
function getTranslateMatrix(x = 0,y = 0,z = 0) {
  return new Float32Array([
    1.0, 0.0, 0.0, 0.0,
    0.0, 1.0, 0.0, 0.0,
    0.0, 0.0, 1.0, 0.0,
    x  , y  , z  , 1,
  ])
}
1
2
3
4
5
6
7
8
9

# 完整的平移矩阵示例代码

jcode (opens new window)

# 使用方法uniformMatrix4fv,给矩阵赋值

gl.uniformMatrix4fv(location, transpose, array)

该方法包含3个参数:

  • location:uniform变量存储的位置
  • transpose:在webgl中必须设置为false
  • array:待传输的类型化数组,4x4矩阵按照列主序存储在其中

函数名最后一个字母为v,表示可以向着色器传递多个数据值;

# 旋转

image.png 上图中将实线三角形转动到虚线的位置,也就是把A点坐标移动到A'点的位置;

点A坐标的位置公式

x = cos(α) * R;
y = sin(α) * R;
z = 0;
1
2
3

A'点的坐标公式

x'
  = R * cos(α + β) 
  = R * (cos(α)*cos(β) - sin(α)*sin(β) )
  = R * cos(α) *cos(β) - R * sin(α) * sin(β)
  = x * cos(β) - y * sin(β);
y' 
  = R * sin(α + β) 
  = R * (sin(α)*cos(β) + cos(α)*sin(β))
  = R * sin(α) * cos(β) + R * cos(α)*sin(β)
  = y*cos(β) + x*sin(β);
z'
  = z
1
2
3
4
5
6
7
8
9
10
11
12

将以上坐标公式带入到矩阵中

image.png

x' = ax + by+ cz + d;
y' = ex + fy + gz + h;
z' = ix + jy + kz + l;
w' = mx + ny + oz + p;
1
2
3
4

对比旋转坐标公式和矩阵坐标公式,进行等式替换

ax + by+ cz + d = x * cos(β) - y * sin(β); // 只有a=cos(β)且b=-sin(β)、c=d=0时,等式成立
ex + fy + gz + h = y*cos(β) + x*sin(β);  // 只有e=sin(β)且f=cos(β)、g=h=0时,等式成立
ix + jy + kz + l = z;  // 只有k=1且i=j=l=0时,等式成立
mx + ny + oz + p = 1;  // 只有p=1且m=n=o=0时,等式成立
1
2
3
4

从而可以推导出如下的旋转矩阵公式;

image.png

// 绕z轴旋转的旋转矩阵
function getRotateMatrix(deg) {
  return new Float32Array([
    Math.cos(deg)  ,Math.sin(deg) ,0.0,0.0,
    -Math.sin(deg)  ,Math.cos(deg) ,0.0,0.0,
    0.0,            0.0,            1.0,0.0,
    0.0,            0.0,            0.0, 1,
  ])
}
1
2
3
4
5
6
7
8
9

# 使用矩阵完成旋转的完整代码

jcode (opens new window)

# 缩放

image.png 将坐标A缩放到坐标A'

x' = Tx * x;
y' = Ty * y;
z' = Tz * z;
1
2
3

image.png

x' = ax + by + cz + d;
y' = ex + fy + gz + h;
z' = ix + jy + kz + l;
w' = mx + ny + oz + p;
1
2
3
4

推导出缩放矩阵坐标如下

  x, 0.0, 0.0, 0.0,
0.0, y  , 0.0, 0.0,
0.0, 0.0, z  , 0.0,
0.0, 0.0, 0.0, 1  ,
1
2
3
4
// 缩放矩阵
function getScaleMatrix(x = 1,y = 1,z = 1) {
  return new Float32Array([
    x  , 0.0, 0.0, 0.0,
    0.0, y  , 0.0, 0.0,
    0.0, 0.0, z  , 0.0,
    0.0, 0.0, 0.0, 1  ,
  ])
}
1
2
3
4
5
6
7
8
9

# 完整的缩放矩阵示例代码

jcode (opens new window)

# 通用矩阵变换公式

4x4矩阵不仅可以表示平移,也可以用来表示旋转和缩放;

不管是平移、缩放、旋转都可以使用 矩阵和矢量的运算完成变换;

变换公式:<新坐标> = <变换矩阵> * <旧坐标>

比如顶点着色器中定义的 gl_Position = mat * aPosition;

const VERTEX_SHADER_SOURCE = `
  attribute vec4 aPosition;
  uniform mat4 mat;
  void main() {
    gl_Position = mat * aPosition;
    gl_PointSize = 10.0;
  }
`; 
1
2
3
4
5
6
7
8

# 矩阵的混合变换

图形变换包含平移、缩放、旋转中的至少2个;

# 通过定义多个uniform变量,设置矩阵参数,进行运算

在顶点着色器中,定义 translateMatrix、scaleMatrix、rotationMatrix三个矩阵变量 jcode (opens new window)

# 将2个矩阵进行复合运算

将第一个矩阵A和第二个矩阵B相乘;

1:第一步用A第1列 乘以 矩阵B第一行,计算出第一行第一列的结果

image.png

2:用A第一列 乘以 矩阵B的第2行,计算出第一行的第2列结果

image.png 3:矩阵A 第一列乘以 矩阵B的第3列,计算出第一行的第3列结果

image.png 以此类推,可以求出新的矩阵

image.png

# 根据以上结果,推导出矩阵的复合公式

// 矩阵复合函数
function mixMatrix(A, B) {
  const result = new Float32Array(16);

  for (let i = 0; i < 4; i++) {
    result[i] = A[i] * B[0] + A[i + 4] * B[1] + A[i + 8] * B[2] + A[i + 12] * B[3]
    result[i + 4] = A[i] * B[4] + A[i + 4] * B[5] + A[i + 8] * B[6] + A[i + 12] * B[7]
    result[i + 8] = A[i] * B[8] + A[i + 4] * B[9] + A[i + 8] * B[10] + A[i + 12] * B[11]
    result[i + 12] = A[i] * B[12] + A[i + 4] * B[13] + A[i + 8] * B[14] + A[i + 12] * B[15]
  }

  return result;
}
1
2
3
4
5
6
7
8
9
10
11
12
13

# 使用混合矩阵公式,进行移动、缩放、旋转变换的完整代码

jcode (opens new window)

更多详情 (opens new window)

编辑 (opens new window)
上次更新: 2025/04/19, 14:22:11
threeJs使用
webgl基础1

← threeJs使用 webgl基础1→

最近更新
01
色戒2007
04-19
02
真实real
04-19
03
Home
更多文章>
Theme by Vdoing | Copyright © 2018-2025 北鸟南游
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式