一起学 WebGL:复合矩阵

news2024/12/25 9:14:59

大家好,我是前端西瓜哥。之前讲了平移矩阵、旋转矩阵以及缩放矩阵,以及演示了在 WebGL 中的单独应用的效果。

这次我们看看同时进行多次矩阵变换的组合写法。

我们将会对一个三角形先平移,然后旋转。

矩阵乘法

简单过一下矩阵乘法的知识点。

具体讲解可看:

《计算机图形学:变换矩阵》

一个坐标(矢量),先进行矩阵变形 1,然后再做矩阵变换 2,它的写法是:

m2 * m1 * vec

第一个矩阵 A 的列数如果等于第二个矩阵 B 的行数,那这两个矩阵可以做乘法。

矩阵乘法不满足交换律,即 AxB 通常不等于 BxA。

矩阵乘法,一种理解方式是:A 的第 i 行的每个元素,依次乘以 B 的第 j 列的对应元素,然后加起来,作为新矩阵的第 [i][j] 个元素。

矩阵乘法的计算顺序为从左往右

以一个比较低维度的 2x2 矩阵为例:

$$
\begin{bmatrix}

a & b\

c & d

\end{bmatrix}

\begin{bmatrix}

e & g \

f & h

\end{bmatrix}

=

\begin{bmatrix}

a * e + b * f & a * g + b * h \

c * e + d * f & c * g + d * h

\end{bmatrix}
$$

矩阵乘法满足交换律。

  m2 * m1 * vec
= m2 * (m1 * vec)
= (m2 * m1) * vec

所以,一个顶点要做多次矩阵变换,我们可以先计算这些矩阵的结果,得到一个复合矩阵,然后再乘以顶点。

当多个顶点做的矩阵变换是一样的时候,我们就能减少计算量,一些线性计算就得到结果。

这种将一个模型丢到世界坐标系中进行各种变换,我们也称为 模型变换(model transform)。

顶点着色器中运算

顶点着色器代码:

const vertexShaderSrc = `
attribute vec4 a_Position;
uniform mat4 u_translateMatrix;
uniform mat4 u_rotateMatrix;

mat4 compoundMatrix = u_rotateMatrix * u_translateMatrix;
void main() {
 gl_Position = compoundMatrix * a_Position;
}
`;

声明了两个 uniform 类型的矩阵 u_translateMatrix 和 u_rotateMatrix,分别保存平移矩阵和旋转矩阵,然后将它们相乘。

compoundMatrix 因为不需要外部传入值,所以不需要加 uniform 声明。

因为是先平移再旋转,所以平移矩阵在右,旋转矩阵在左。

平移矩阵的构造:

/****** (1) 平移矩阵 ****/
const dx = 0.5; // 向右移动
const dy = 0; // 向下移动
// z 先不管,没用到透视矩阵,设置值也看不到效果
const translateMatrix = new Float32Array([
  1, 0, 0, 0,
  0, 1, 0, 0,
  0, 0, 1, 0,
  dx, dy, 0, 1
]);
// 传入顶点着色器
const u_translateMatrix = gl.getUniformLocation(
  gl.program,
  "u_translateMatrix"
);
gl.uniformMatrix4fv(u_translateMatrix, false, translateMatrix);

旋转矩阵的构造:

/****** (2) 旋转矩阵 ****/
const angle = 60;
const radian = (angle * Math.PI) / 180;
const cos = Math.cos(radian);
const sin = Math.sin(radian);
// prettier-ignore
const rotateMatrix = new Float32Array([
  cos, sin, 0, 0,
  -sin, cos, 0, 0,
  0, 0, 1, 0,
  0, 0, 0, 1
]);
const u_rotateMatrix = gl.getUniformLocation(gl.program, "u_rotateMatrix");
gl.uniformMatrix4fv(u_rotateMatrix, false, rotateMatrix);

demo 地址:

https://codesandbox.io/s/eg66e2?file=/index.js

渲染结果:

使用 JavaScript 运算

换成用 JavaScript 来做矩阵乘法,将计算好的矩阵传入到着色器中。

顶点着色器只要声明一个 uniform 类型的 u_compoundMatrix 就好。

uniform 是一个不变的类型,使用下一顶点时还会使用原来的值。如果顶点更换时,值会变,你应该使用的是 attribute 类型。

const vertexShaderSrc = `
attribute vec4 a_Position;
uniform mat4 u_compoundMatrix;
void main() {
 gl_Position = u_compoundMatrix * a_Position;
}
`;

创建完两个 Float32Array 的数组后,我们用自己实现的 JavaScript 将它们相乘。

/*************** 计算复合矩阵 *************/
// 两个 4x4 矩阵相乘的方法。m1 x m2
function multiply(m1, m2) {
  const size = 4;
  const m = new Float32Array(size * size);
  for (let i = 0; i < size; i++) {
    for (let j = 0; j < size; j++) {
      let sum = 0;
      for (let k = 0; k < size; k++) {
        sum += m2[i * size + k] * m1[k * size + j];
      }
      m[i * size + j] = sum;
    }
  }
  return m;
}

// 这里创建了俩 Float32Array 数组
// ...

// 两个矩阵相乘,得到复合变换矩阵
const xformMatrix = multiply(rotateMatrix, translateMatrix);
// 将计算出来的矩阵传给着色器
const u_compoundMatrix = gl.getUniformLocation(gl.program, "u_compoundMatrix");
gl.uniformMatrix4fv(u_compoundMatrix, false, xformMatrix);

在实现 multiply 方法过程中,有一个容易踩的坑,就是 WebGL 中的矩阵是 按列主序 的,即 m[0] 到 m[1] 对应的是第一列,而不是第一行,我们实现 JavaScript 矩阵乘法的时候要注意,否则就会从 AxB 变成了 BxA。

西瓜哥我就踩了这个坑,发现 JavaScript 实现和预期不符,居然是先旋转再移动,大眼瞪了半天才发现自己把左边矩阵的列当成行去运算了。

demo 地址:

https://codesandbox.io/s/hw8wfi?file=/index.js

渲染效果和在顶点着色器计算的一样:

如果我们将两个变换矩阵的顺序交换一下,就会得到一个先旋转再移动的效果。感兴趣的朋友可以去线上 demo 修改试试。

结尾

我是前端西瓜哥,欢迎关注我,学习更多前端知识。

顶点的变换可以用矩阵乘法来表示,乘以一个矩阵就是一个变形(比如平移、旋转、缩放)。如果应用了多个矩阵,我们可以利用矩阵乘法的结合律将这几个矩阵先预先计算好。

另外一个坑,就是矩阵是用一维数组表示的,且为比较别扭的按列主序,声明的数组在形式上和矩形真正的样子有一点不同,需要沿着从左上到右下的轴线进行一个翻转才行。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/442990.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Cassandra windos安装

首先从http://cassandra.apache.org/download/网站上找到cassandra&#xff0c;如下图所示&#xff1a; 2、点击3.11.3跳转到下载地址&#xff0c;如下图所示&#xff1a; …

DHCP服务器 DNS服务器 综合实验

配置DNS服务器 DNS服务器 主域服务器host1 192.168.80.101 从域服务器host2 192.168.80.102 DHCP服务器 host1 192.168.80.101 提供192.168.80.0/24网段 IP地址、默认网关、子网掩码、DNS服务器地址、DNS默认域名 客户端 Linux host3 19…

数据可视化神器!Matplotlib Python教程 | 从入门到精通绘制各种类型的图形和保存图形

大家好&#xff0c;我是爱吃熊掌的鱼&#xff0c;今天我要给大家带来一篇有趣开朗的Matplotlib Python教程。Matplotlib是Python中最流行的数据可视化库之一&#xff0c;它可以帮助我们将数据转化为易于理解的图表和图形。无论你是初学者还是专业人士&#xff0c;Matplotlib都是…

Eureka 注册中心——服务的注册与发现

文章目录 1.Eureka的结构和作用2.搭建eureka-server2.1.创建eureka-server服务2.2.引入eureka依赖2.3.编写启动类2.4.编写配置文件2.5.启动服务 3.服务注册3.1.引入依赖3.2 配置文件3.3启动多个user-service实例 4.服务发现4.1引入依赖4.2 配置文件4.3 服务拉取和负载均衡 最后…

flowable流程图绘制工具flowable-ui的安装和使用

一.简介 记录绘制flowable图的过程。 二.下载安装 1.下载 我这边是windows的&#xff0c;下载的版本是6.8.0&#xff0c;zip那个就可以&#xff0c;tar.gz是linux的 下载地址&#xff1a;https://github.com/flowable/flowable-engine/releases/tag/flowable-6.8.0 下载截…

PyCharm-2023安装教程

访问JetBrains的官方网站&#xff0c;下载PyCharm最新版本的安装程序。 双击下载的安装程序&#xff0c;在弹出的安装向导中点击“下一步”。 阅读许可协议&#xff0c;并同意协议条款。 选择安装路径。默认情况下&#xff0c;PyCharm会安装在C:\Program Files\JetBrain…

STL——Lambad表达式

&#x1f4d6;作者介绍&#xff1a;22级树莓人&#xff08;计算机专业&#xff09;&#xff0c;热爱编程&#xff1c;目前在c&#xff0b;&#xff0b;阶段>——目标Windows&#xff0c;MySQL&#xff0c;Qt&#xff0c;数据结构与算法&#xff0c;Linux&#xff0c;多线程&…

【C语言】输入输出、字符串操作、内存操作、文件操作函数

三对基本输入输出函数 1.gets()&#xff0c;puts() gets()从标准输入中获取一个字符串&#xff0c;到str&#xff08;自己创建的char型数组&#xff09;中&#xff0c;读到换行或输入末尾结束获取r&#xff1b; 成功返回str&#xff0c;失败返回空。 char *gets(char *str)…

ARM汇编第一次上机(顺序、分支、单重循环)【嵌入式系统】

ARM汇编第一次上机&#xff08;顺序、分支、单重循环&#xff09;【嵌入式系统】 前言推荐说明ARM汇编第一次上机&#xff08;顺序、分支、单重循环&#xff09;内容1 sum1流程图代码编写结果分析 2 sum2流程图代码编写结果分析 3 numbers流程图代码编写结果分析 最后 前言 20…

Python统计学:配对样本t检验~均值差的检验

配对样本 t检验 用于两个不同条件下&#xff0c;对同一对象进行测量所得的两个样本均值的区别。 比如吃药前和吃药后体温是否有变化&#xff0c;大鱼钩和小鱼钩钓的鱼的体长是否有差异等。 配对样本是什么&#xff1f; 配对样本需要满足两个基本条件&#xff1a; 1. 样本间存…

Java版本工程管理系统软件源码 自主研发,工程行业适用

Java版工程项目管理系统 Spring CloudSpring BootMybatisVueElementUI前后端分离 功能清单如下&#xff1a; 首页 工作台&#xff1a;待办工作、消息通知、预警信息&#xff0c;点击可进入相应的列表 项目进度图表&#xff1a;选择&#xff08;总体或单个&#xff09;项目显示…

【Java 数据结构】PriorityQueue(堆)的使用及源码分析

&#x1f389;&#x1f389;&#x1f389;点进来你就是我的人了 博主主页&#xff1a;&#x1f648;&#x1f648;&#x1f648;戳一戳,欢迎大佬指点!人生格言&#xff1a;当你的才华撑不起你的野心的时候,你就应该静下心来学习! 欢迎志同道合的朋友一起加油喔&#x1f9be;&am…

ES配置与使用

一、单机版安装 地址&#xff1a;www.elastic.co 下载tar格式&#xff0c;或者复制链接&#xff0c;wget url下载 启动&#xff1a; ./bin/elasticsearch 二、插件 解决页面问题&#xff0c;GitHub下载&#xff1a;elasticsearch-head 需要node环境。node -v检查node版本…

TryHackMe-Jack(boot2root)

Jack 破坏运行Wordpress的Web服务器&#xff0c;获得低特权用户&#xff0c;并使用Python模块将您的权限升级到root。 端口扫描 循例nmap web枚举 robots.txt wpscan枚举user wpscan直接爆 得到wendy的密码 直接登后台 根据题目提示&#xff0c;利用user role editor帮助我们…

使用chatgpt写一个划词翻译的浏览器插件

最近在网上流行的chatgpt&#xff0c;实在是太火了&#xff0c;好多人在使用&#xff0c;而且它的用处实在是太丰富了&#xff0c;能帮助我们解决很多问题&#xff0c;就比如说&#xff1a;编程&#xff0c;生成图片&#xff0c;翻译等。太丰富了。我看网上很多人都在说&#x…

江苏三年制专转本本法学类考纲配套课程及复习资料

法学类专业课内容比较多&#xff0c;需要背的也很多&#xff0c;今天我来给大家看一下我们应该如何学习法学类&#xff1f;01&#xff1a;考试科目考试科目&#xff1a;大学语文专业课基础理论考试课程A &#xff1a;法理学约 15%课程B&#xff1a;宪法学 约 15%课程C &#xf…

第九讲 常用数据结构之列表-2

列表的方法 列表类型的变量拥有很多方法可以帮助我们操作一个列表&#xff0c;假设我们有名为foos的列表&#xff0c;列表有名为bar的方法&#xff0c;那么使用列表方法的语法是&#xff1a;foos.bar()&#xff0c;这是一种通过对象引用调用对象方法的语法。后面我们讲面向对象…

接口自动化【三】(接口依赖之上传图片;Template 模块用法)

前言 一、后台上传图片接口 二、用postman来做这种有依赖的接口 三、使用 Template 模块进行字符串的替换 四、使用 Template 模块进行字符串的替换&#xff08;针对于Excel表格中的数据&#xff09; 总结 前言 本章中&#xff0c;对接口的依赖做了阐述&#xff0c;后台上…

怎么用Elai智能数字人来直播带货?

随着直播带货的火爆&#xff0c;越来越多的商家和品牌想要借助这一新兴的营销方式来提升销量和品牌影响力。但是&#xff0c;直播带货也面临着一些挑战&#xff0c;比如真人主播的成本高、稳定性差、风险大等。那么&#xff0c;有没有一种更好的解决方案呢&#xff1f;Elai智能…

leetcode重点题目分类别记录(三)动态规划深入

文章目录 动态规划背包问题01背包抽象出求解目标尝试进程子问题拆分基本情况根据拆分过程定义dp数组与转移方程遍历顺序与状态压缩模板归纳题目应用变种提升组合问题多维01背包有特殊限制的01背包 完全背包尝试进行子问题拆分转移方程题目应用变种提升-求组合/排列数 打家劫舍变…