其他章节请看:
动画就是不停地将某个东西变换(transform)。例如将三角形不停地旋转就是一个动画
和 CSS transform 类似,变换有三种形式:平移、缩放和旋转。
简单的变换用普通表达式容易实现,如果事情复杂,比如旋转后平移,这时就可以使用变换矩阵。
比如要平移一个三角形,只需要将三个顶点移动相同的距离即可(这是一个逐顶点操作)

用二维向量表示,就像这样:[x1, y1] + [tx1, ty2] = [x2, y2]
比如要实现如下效果:

前面我们已经讲过三角形了,这里不再冗余,改动的核心代码如下:
const VSHADER_SOURCE = `
attribute vec4 a_Position;
+uniform vec4 u_Translation;
void main() {
- gl_Position = a_Position;
+ gl_Position = a_Position + u_Translation;
gl_PointSize = 10.0;
}
`
function main() {
gl.clearColor(0, 0, 0, 1);
gl.clear(gl.COLOR_BUFFER_BIT);
+ const u_Translation = gl.getUniformLocation(gl.program, 'u_Translation');
+ if (!u_Translation) {
+ return;
+ }
+ gl.uniform4f(u_Translation, 0.5, 0.5, 0, 0.0);
+
a_Position 和 u_Translation 都是 vec4 类型,使用 + 号,两个矢量(也称向量)对应的分量会被同时相加(矢量相加是着色器语言的特性之一)。就像这样:

以一个点为例,比如要将 A 点缩放到 B 点,乘以一个系数就好,系数小于1表示缩小,系数大于1表示放大:

用二维向量表示,就像这样:k[x1, y1] = [x2, y2]
比如要将 p 点旋转 β,推导出来的公式如下:

变换矩阵(非常适合操作计算机图形)是数学线性代数中的一个概念。请看下图:

将点从 S 旋转到 T,新坐标(m, n)可以用普通表达式表示,同样可以用变换矩阵来表示(旧点 * 变换矩阵 = 新点)
变换矩阵和向量相乘有一个规则,并会得到一个新的向量。
Tip:webgl 中的一个点,在坐标系中就相当于一个向量
在 webgl 中变换矩阵和向量相乘的规则如下:

注:牢记公式:变换矩阵 * 向量 会生成一个新的向量;顺序不同结果也不同,例如:向量 * 变换矩阵
将旋转的普通表达式转为变换矩阵:

为什么要用四维矩阵?
因为三维矩阵矩阵不够用,比如将 (0,0,0) 移动到 (1, 0, 0),用三维矩阵是表示不出来的,而四维却可以。请看下图:

将平移的普通表达式转为变换矩阵:

将缩放的普通表达式转为变换矩阵:

为了更好的理解矩阵。我们先不使用矩阵库,直接通过 js 来使用矩阵实现变换。
js 中没有矩阵数据类型,这里用数组表示。
比如要表示如下一个平移矩阵:
1, 0, 0, Tx
0, 1, 0, Ty
0, 0, 1, Tz
0, 0, 0, 1
数组就是这样:
const matrix = [
1, 0, 0, Tx,
0, 1, 0, Ty,
0, 0, 1, Tz,
0, 0, 0, 1,
]
而要表示如上这个变换矩阵,在 webgl 中就得将数组颠倒:行变成列。
所以最后就得这么写:
const matrix = [
1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
Tx, Ty, Tz, 1,
]
Tip: 对于缩放,颠倒后和颠倒前是相同的。
需求:将三角形向右上角偏移。
效果:

前面我们已经学会画三角形,笔者在此基础上改动如下代码:
const VSHADER_SOURCE = `
+// mat4 是一种4维矩阵
+uniform mat4 u_xformMatrix;
void main() {
- gl_Position = a_Position ;
+ // 注:必须是 "变换矩阵 * 向量",不可是 "向量 * 变换矩阵"
+ gl_Position = u_xformMatrix * a_Position ;
gl_PointSize = 10.0;
}
`
function main() {
initVertexBuffers(gl, vertices)
+ 变换(gl)
gl.drawArrays(gl.TRIANGLES, 0, vertices.vertexNumber);
}
+function 变换(gl){
+ const u_xformMatrix = gl.getUniformLocation(gl.program, 'u_xformMatrix');
+ if (!u_xformMatrix) {
+ console.log('Failed to get the storage location of u_xformMatrix');
+ return;
+ }
+ // 四维矩阵
+ const [Tx, Ty, Tz] = [0.5, 0.5, 0];
+ // webgl 中矩阵的行和列是要颠倒的,比如要传一个 A 矩阵,给 webgl 的A矩阵就得颠倒,也就是将 A 的第一行变为第一列,第二行变成第二列
+ const matrix = new Float32Array([
+ 1, 0, 0, 0,
+ 0, 1, 0, 0,
+ 0, 0, 1, 0,
+ Tx, Ty, Tz, 1,
+ ])
+ // 将矩阵分配给 u_xformMatrix
+ gl.uniformMatrix4fv(u_xformMatrix, false, matrix);
+}
代码解析:
逐顶点的操作,每个顶点都相同,所以不用 attribute 而用 uniformmat4 表示4*4的矩阵变换矩阵 * 向量(旧点)注:如果改变变换矩阵 * 向量的顺序,平移效果就不对了:

自己手写矩阵数组非常麻烦。
openGL 提供了一系列有用的函数帮助我们创建变换矩阵。例如通过 glTranslate 传入在 x、y、z 上平移的距离,就可以创建一个平移矩阵。
既然 webgl 中未提供创建变换矩阵的函数,我们就使用库来做这部分工作。
笔者使用一个较流行的矩阵库 gl-matrix —— 用于高性能WebGL应用程序的Javascript矩阵和矢量(又称为向量)库。
下载后,在 dist 目录下看到 esm 文件夹和两个 js 文件:
toji-gl-matrix-4480752/dist (master)
$ ll
drwxr-xr-x 1 Administrator 197121 0 Mar 6 15:26 esm/
-rw-r--r-- 1 Administrator 197121 52466 Jan 10 05:24 gl-matrix-min.js
-rw-r--r-- 1 Administrator 197121 206643 Jan 10 05:24 gl-matrix.js
其实也就是提供两种使用的方法:
<script type="module" src="main.mjs"></script> 这种方式使用<script src="animation.js"></script>笔者选用第二种:在 html 中引入:<script src="./animation.js"></script>
这时在控制台就有一个 glMatrix 全局变量:
glMatrix
{glMatrix: {…}, mat2: {…}, mat2d: {…}, mat3: {…}, mat4: {…}, …}
glMatrix: {EPSILON: 0.000001, ANGLE_ORDER: "zyx", RANDOM: ƒ, setMatrixArrayType: ƒ, …}
mat2: {create: ƒ, clone: ƒ, copy: ƒ, identity: ƒ, fromValues: ƒ, …}
mat2d: {create: ƒ, clone: ƒ, copy: ƒ, identity: ƒ, fromValues: ƒ, …}
mat3: {create: ƒ, fromMat4: ƒ, clone: ƒ, copy: ƒ, fromValues: ƒ, …}
mat4: {create: ƒ, clone: ƒ, copy: ƒ, fromValues: ƒ, set: ƒ, …}
quat: {create: ƒ, identity: ƒ, setAxisAngle: ƒ, getAxisAngle: ƒ, getAngle: ƒ, …}
quat2: {create: ƒ, clone: ƒ, fromValues: ƒ, fromRotationTranslationValues: ƒ, fromRotationTranslation: ƒ, …}
vec2: {create: ƒ, clone: ƒ, fromValues: ƒ, copy: ƒ, set: ƒ, …}
vec3: {create: ƒ, clone: ƒ, length: ƒ, fromValues: ƒ, copy: ƒ, …}
vec4: {create: ƒ, clone: ƒ, fromValues: ƒ, copy: ƒ, set: ƒ, …}
官方文档也是从这几个模块来介绍的:mat2, mat2d, mat3, mat4, quat, quat2, vec2, vec3, vec4。
mat[234]就是2维3维4维矩阵vec[234]就是2维3维4维向量首先取得 mat4 模块,然后调用 create() 就会创建一个四维矩阵:
// 四维矩阵模块
const { mat4 } = glMatrix
// 创建一个4维单位矩阵
const matrix = mat4.create()
/*
Float32Array(16) [
1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1]
*/
console.log(matrix)
Tip: create() 创建的是一个单位矩阵,如同数的乘法中的1
fromTranslation - 平移矩阵
语法如下:
(static) fromTranslation(out, v) → {mat4}
Creates a matrix from a vector translation This is equivalent to (but much faster than): mat4.identity(dest); mat4.translate(dest, dest, vec);
Parameters:
Name Type Description
out mat4 mat4 receiving operation result
v ReadonlyVec3 Translation vector
Returns:
out
请看示例:
mat4.fromTranslation(matrix, [0.5, 0.5, 0])
/*
Float32Array(16) [
1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
0.5, 0.5, 0, 1
]
*/
console.log(matrix)
matrix 是一个单位矩阵,通过该方法,即可得到一个向 x 和 y 各平移 0.5 的变换矩阵。
与之对应不修改原矩阵的方法是:translate(out, a, v)。语法如下:
(static) translate(out, a, v) → {mat4}
Translate a mat4 by the given vector
Parameters:
Name Type Description
out mat4 the receiving matrix
a ReadonlyMat4 the matrix to translate
v ReadonlyVec3 vector to translate by
Returns:
out
请看示例:
const matrix2 = mat4.create()
mat4.translate(matrix2, matrix, [0.5, 0.5, 0])
// Float32Array(16) [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]
/*
Float32Array(16) [
1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1
]
*/
console.log(matrix)
/*
Float32Array(16) [
1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
0.5, 0.5, 0, 1
]
*/
console.log(matrix2)
matrix 没有改变,最终变换矩阵输出到 matrix2。
fromRotation - 旋转矩阵
创建一个旋转矩阵。请看示例:
// fromRotation(out, rad, axis) - out 是要修改的矩阵、rad 旋转角度、axis 围绕哪个轴旋转 [x, y, z]
const angle = 90
// 角度转弧度
const rad = angle * Math.PI / 180;
const axis = [0, 0, 1];
// 等于 fromXRotation、fromYRotation、fromZRotation
mat4.fromRotation(matrix, rad, axis)
/*
Float32Array(16) [
6.123234262925839e-17, 1, 0, 0,
-1, 6.123234262925839e-17, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1
]
*/
console.log(matrix)
与之对应不修改原矩阵的方法是:rotate(out, a, rad, axis)。用法与平移中的类似。
旋转矩阵需要使用弧度,通过 toRadian() 可以将角度转为弧度。用法如下:
glMatrix.glMatrix.toRadian(180) => 3.141592653589793
fromScaling - 缩放矩阵
创建一个缩放矩阵。请看示例:
mat4.fromScaling(matrix, [2, 2, 0])
/*
Float32Array(16) [
2, 0, 0, 0,
0, 2, 0, 0,
0, 0, 0, 0,
0, 0, 0, 1
]
*/
console.log(matrix)
与之对应不修改原矩阵的方法是:scale(out, a, v)。用法与平移中的类似。
现在使用这个库来实现平移,只需要将手动矩阵替换如下即可:
- const matrix = new Float32Array([
- 1, 0, 0, 0,
- 0, 1, 0, 0,
- 0, 0, 1, 0,
- Tx, Ty, Tz, 1,
- ])
+
+ const { mat4 } = glMatrix
+ const matrix = mat4.create()
+ mat4.fromTranslation(matrix, [Tx, Ty, 0])
旋转、缩放也类似,不再展开。
变换矩阵可以组合,比如希望将三角形旋转和平移,这里需要注意:顺序不同导致结果不同。请看下图

核心代码:
const VSHADER_SOURCE = `
attribute vec4 a_Position;
// 移动矩阵
uniform mat4 u_tformMatrix;
// 旋转矩阵
uniform mat4 u_rformMatrix;
void main() {
// 先旋转后移动
// gl_Position = u_tformMatrix * u_rformMatrix * a_Position;
// 先移动后旋转
gl_Position = u_rformMatrix * u_tformMatrix * a_Position;
gl_PointSize = 10.0;
}
`
const u_rformMatrix = gl.getUniformLocation(gl.program, 'u_rformMatrix');
const u_tformMatrix = gl.getUniformLocation(gl.program, 'u_tformMatrix');
const { mat4 } = glMatrix
const tMatrix = mat4.create()
const rMatrix = mat4.create()
mat4.fromTranslation(tMatrix, [0.5, 0, 0])
// 设置移动矩阵
gl.uniformMatrix4fv(u_tformMatrix, false, tMatrix);
const rad = glMatrix.glMatrix.toRadian(90)
const axis = [0, 0, 1];
mat4.fromRotation(rMatrix, rad, axis)
// 设置旋转矩阵
gl.uniformMatrix4fv(u_rformMatrix, false, rMatrix);
组合变换矩阵的顺序和 css 类似,从右往左。比如:
u_rformMatrix * u_tformMatrix * a_Position 先移动后旋转u_tformMatrix * u_rformMatrix * a_Position 先旋转后移动Tip: 这里的组合变换矩阵其实就是计算机图形学中模型变换(M)。还有视图变换(V)、投影变换(P),统称为 MVP。
需求:绘制一个旋转动画
效果如下:

思路:
完整代码如下:
const VSHADER_SOURCE = `
attribute vec4 a_Position;
// 所有顶点移动位置都相同,所以不用 Attribute 而用 uniform
// mat4 是一种4维矩阵
uniform mat4 u_xformMatrix;
void main() {
// 注:必须是 "变换矩阵 * 向量",不可是 "向量 * 变换矩阵"
gl_Position = u_xformMatrix * a_Position ;
gl_PointSize = 10.0;
}
`
const FSHADER_SOURCE = `
void main() {
gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
}
`
function main() {
const canvas = document.getElementById('webgl');
const gl = canvas.getContext("webgl");
if (!gl) {
console.log('Failed to get the rendering context for WebGL');
return;
}
if (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) {
console.log('Failed to intialize shaders.');
return;
}
const vertices = {
data: new Float32Array([
0.0, 0.5,
-0.5, -0.5,
0.5, -0.5
]),
vertexNumber: 3,
count: 2,
}
initVertexBuffers(gl, vertices)
tick(gl, vertices)
}
function initVertexBuffers(gl, { data, count }) {
// 1. 创建缓冲区对象
const vertexBuffer = gl.createBuffer();
if (!vertexBuffer) {
console.log('创建缓冲区对象失败');
return -1;
}
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
gl.bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW);
const a_Position = gl.getAttribLocation(gl.program, 'a_Position');
if (a_Position < 0) {
console.log('Failed to get the storage location of a_Position');
return -1;
}
gl.vertexAttribPointer(a_Position, count, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(a_Position);
}
function 变换(gl, vertices) {
const u_xformMatrix = gl.getUniformLocation(gl.program, 'u_xformMatrix');
if (!u_xformMatrix) {
console.log('Failed to get the storage location of u_xformMatrix');
return;
}
const { mat4 } = glMatrix
const matrix = mat4.create()
const rad = glMatrix.glMatrix.toRadian(angle)
const axis = [0, 0, 1];
mat4.fromRotation(matrix, rad, axis)
gl.uniformMatrix4fv(u_xformMatrix, false, matrix);
gl.clearColor(0, 0, 0, 1);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.drawArrays(gl.TRIANGLES, 0, vertices.vertexNumber);
}
let angle = 0
// 每次改变的角度
const seed = 1
function tick(gl, vertices){
变换(gl, vertices)
// 改变角度
angle += seed;
// 动画绘制
requestAnimationFrame(() => tick(gl, vertices))
}
其他章节请看:
关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。
这里是Ruby新手。完成一些练习后碰壁了。练习:计算一系列成绩的字母等级创建一个方法get_grade来接受测试分数数组。数组中的每个分数应介于0和100之间,其中100是最大分数。计算平均分并将字母等级作为字符串返回,即“A”、“B”、“C”、“D”、“E”或“F”。我一直返回错误:avg.rb:1:syntaxerror,unexpectedtLBRACK,expecting')'defget_grade([100,90,80])^avg.rb:1:syntaxerror,unexpected')',expecting$end这是我目前所拥有的。我想坚持使用下面的方法或.join,
点向量坐标矩阵的几何意义介绍旋转矩阵的几何含义之前,先介绍一下点向量坐标矩阵的几何含义点:在一维空间下就是一个标量,如同一条直线上,以任意某一个位置为0点,以一定的尺度间隔为1,2,3...,相反方向为-1,-2,-3...;如此就形成了一维坐标系,这时候任何一个点都可以用一个数值表示,如点p1=5,即即从原点出发沿着x轴正方向移动5个尺度;点p2=-3,负方向移动3个尺度; 在一维坐标系上过原点做垂直于一维坐标系的直线,则形成了二维坐标系,此时描述一个点需要两个数值来表示点p3=(3,2),即从原点出发沿着x轴正方向移动3个尺度,在此基础上沿着y轴正方向移动两个尺度的位置就是点p3。
在应用开发中,有时候我们需要获取系统的设备信息,用于数据上报和行为分析。那在鸿蒙系统中,我们应该怎么去获取设备的系统信息呢,比如说获取手机的系统版本号、手机的制造商、手机型号等数据。1、获取方式这里分为两种情况,一种是设备信息的获取,一种是系统信息的获取。1.1、获取设备信息获取设备信息,鸿蒙的SDK包为我们提供了DeviceInfo类,通过该类的一些静态方法,可以获取设备信息,DeviceInfo类的包路径为:ohos.system.DeviceInfo.具体的方法如下:ModifierandTypeMethodDescriptionstatic StringgetAbiList()Obt
Unity自动旋转动画1.开门需要门把手先动,门再动2.关门需要门先动,门把手再动3.中途播放过程中不可以再次进行操作觉得太复杂?查看我的文章开关门简易进阶版效果:如果这个门可以直接打开的话,就不需要放置"门把手"如果门把手还有钥匙需要旋转,那就可以把钥匙放在门把手的"门把手",理论上是可以无限套娃的可调整参数有:角度,反向,轴向,速度运行时点击Test进行测试自己写的代码比较垃圾,命名与结构比较拉,高手轻点喷,新手有类似的需求可以拿去做参考上代码usingSystem.Collections;usingSystem.Collections.Generic;usingUnityEngine;u
基础版云数据库RDS的产品系列包括基础版、高可用版、集群版、三节点企业版,本文介绍基础版实例的相关信息。RDS基础版实例也称为单机版实例,只有单个数据库节点,计算与存储分离,性价比超高。说明RDS基础版实例只有一个数据库节点,没有备节点作为热备份,因此当该节点意外宕机或者执行重启实例、变更配置、版本升级等任务时,会出现较长时间的不可用。如果业务对数据库的可用性要求较高,不建议使用基础版实例,可选择其他系列(如高可用版),部分基础版实例也支持升级为高可用版。基础版与高可用版的对比拓扑图如下所示。优势 性能由于不提供备节点,主节点不会因为实时的数据库复制而产生额外的性能开销,因此基础版的性能相对于
我使用irb。下面是我写的代码。“斧头”..“bc”我期待"ax""ay""az""ba"bb""bc"但结果只是“斧头”..“bc”我该如何纠正?谢谢。 最佳答案 >puts("ax".."bc").to_aaxayazbabbbc 关于ruby-从结束值创建一系列字符串,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/7617092/
所有题目均有五种语言实现。C实现目录、C++实现目录、Python实现目录、Java实现目录、JavaScript实现目录题目n行m列的矩阵,每个位置上有一个元素你可以上下左右行走,代价是前后两个位置元素值差的绝对值.另外,你最多可以使用一次传送阵(只能从一个数跳到另外一个相同的数)求从走上角走到右下角最少需要多少时间。输入描述:第一行两个整数n,m,分别代表矩阵的行和列。后面n行,每行m个整数,分别代表矩阵中的元素。输出描述:一个整数,表示最少需要多少时间。
动画/*INITIALIZEANANIMATION 初始化一个动画*-----------------------*/lv_anim_ta;lv_anim_init(&a);/*MANDATORYSETTINGS 必选设置*------------------*//*Setthe"animator"function 设置“动画”功能*/lv_anim_set_exec_cb(&a,(lv_anim_exec_xcb_t)lv_obj_set_x);/*Setthe"animator"function*/lv_anim_set_var(&a,obj);/*Lengthoftheanim
使用RubyonRails,我使用给定的增量(例如每30分钟)用时间填充“选择”。目前我正在YAML文件中写出所有的可能性,但我觉得有一种更巧妙的方法。我想我想提供一个开始时间、一个结束时间、一个增量,并且目前只提供一个名为“关闭”的选项(想想“business_hours”)。所以,我的选择可能会显示:'Closed'5:00am5:30am6:00am...[allthewayto]...11:30pm谁能想出更好的方法,或者只是将它们全部“拼写”出来的最佳方法? 最佳答案 此答案基于@emh的答案。defcreate_hour