WebGL图形编程实战【3】:矩阵操控 × 从二维到三维的跨越

news2025/4/4 19:18:34

上一篇文章:WebGL图形编程实战【2】:动态着色 × 纹理贴图技术揭秘
仓库地址:github…、gitee…

矩阵操控

矩阵变换

回到前面关于平移缩放、旋转的例子当中,我们是通过改变传递进去的xy的值来改变的。

在进行基础变换的时候,涉及到多个变量且变化频率高,在实际的webgl应用开发过程中,其复杂程度更令人发指,故引入了数学工具—矩阵。(具有规律性的二维数组)计算机实际上是一个固执的老顽童,它最喜欢有规律性质的东西,所以这样一拍即合,计算机技术与数学理论达成情人关系(webgl内置了矩阵系统)。本堂课的内容就是将变换过程转换成矩阵进行表示。

矩阵动画推演

下面先展示了在数学方面上对xyz轴的数据进行相对应的变化,而后是在矩阵层面上变化的值

平移
x1 = x + Tx;
y1 = y + Ty;
z1 = z + Tz;

在这里插入图片描述

旋转
x1 = x * cos(angle) - y * sin(angle);
y1 = y * cos(angle) + x * sin(angle);

在这里插入图片描述

缩放
x1 = x * Sx;
y1 = y * Sy;
z1 = z * Sz;

在这里插入图片描述

矩阵运算案例

加(减)法

只有同型矩阵之间才可以进行加(减)法运算,将两个矩阵相同位置的元相加即可,m行n列的两个矩阵相加(减)后得到一个新的m行n列矩阵,例如

1,2,3,4  +  3,4,5,6  =  4,6,8,10
2,3,4,5  +  2,3,4,5  =  4,6,8,10
数乘

数乘即将矩阵乘以一个常量,矩阵中的每个元都与这个常量相乘,例如

1,2,3  *  3  =  3,6,9
乘法

两个矩阵的乘法仅当第一个矩阵的列数和另一个矩阵的行数相等时才能定义

1,2,3  *  3,4  =  1*3+2*2+3*3  1*4+2*3+3*4  = 16 22
2,3,4  *  2,3  =  2*3+3*2+4*3  2*4+3*3+4*4  = 24 33
          3,4  =  

glMatrix 常用API

glMatrix API 官网…

补充:在使用glMatrix-0.9.6.min.js和npm上的glMatrix.js,API是有一定区别的,下面是用最新的glMatrix

创建矩阵

返回类型是Float32Array,矩阵的元素个数是16,也就是一个4x4的矩阵。

const matrix = mat4.create();

投影矩阵

生成具有给定边界的透视投影矩阵。far传递null/undefined/no值将生成无限投影矩阵。

mat4.perspective(out, fovy, aspect, near, far);
mat4.perspective(matrix, 45, 4 / 3, 1, 100);
名称类型描述
outmat4mat4截头体矩阵将被写入
fovynumber垂直视场(弧度)
aspectnumber宽高比。通常视口宽度/高度
nearnumber截头体的近界
farnumber截头体的远边界,可以为null或Infinity

矩阵相乘

将两个mat 4相乘,参数一为目标矩阵,参数二三为要相乘的矩阵。

const matrix = mat4.create();
const matrix1 = mat4.create();
let target = [];
mat4.multiply(target, matrix, matrix1);

单位矩阵

将一个矩阵设置为单位矩阵。单位矩阵是一个4x4的矩阵,其元素值都为0,除了主对角线元素值都为1。

mat4.identity(matrix);

矩阵变化(平移、旋转、缩放)

平移缩放旋转都传递了两个矩阵参数,其中第一个参数是目标矩阵,第二个参数是变化矩阵(不做变换就和第一个传一样的值)。第三个参数是变化参数(平移的xyz值、缩放的xyz值、旋转的弧度和xyz值)。

mat4.translate(matrix, matrix, [10, 10, 10]);
mat4.scale(matrix, matrix, [1, 2, 1]);
mat4.rotate(matrix, matrix, 45, [0, 0, 1]);

WebGL+矩阵变化

整体逻辑如下mermaid图

数据组装
uniformMatrix4fv
变化值
赋值给shader
initWebGL
initShader
initBuffer
render
draw

修改着色器,添加一个中间矩阵,然后把中间矩阵传递给着色器。版本为0.9.6

const vertexString = `
  attribute vec4 a_position;
  uniform mat4 u_formMatrix;
  void main(){
    gl_Position = u_formMatrix * a_position;
    gl_PointSize = 40.0;
  }`;

在js当中通过glMatrix.js进行矩阵变换,然后用webGL的uniformMatrix4fv方法传递给着色器。

uniformMatrix4fv 为 uniform 变量指定矩阵值

  • 参数一:是指定待修改 uniform 变量的存储位置
  • 参数二:指定是否转置矩阵
  • 参数三:序列值
function animate() {
  const middleMat4 = mat4.create();
  mat4.identity(middleMat4);
  mat4.translate(middleMat4, [0, 0.5, 0]);
  mat4.rotate(middleMat4, 0.5 * Math.PI, [0, 0, 1]);
  mat4.scale(middleMat4, [0.5, 0.5, 0.5]);
  let uniformMatrix = webGL.getUniformLocation(program, 'u_formMatrix');
  webGL.uniformMatrix4fv(uniformMatrix, false, middleMat4);
}

案例:WebGL时钟效果

和上面webGL+矩阵变化的代码一样,在顶点着色器当中传入一个u_formMatrix用来计算,随后在initBuffer当中重新设置顶点坐标用来绘制三角带,如下

let triangleArray = [
  0, -0.1, 0, 1.0,
  0, 0.4, 0, 1.0,
  0.01, 0.4, 0, 1.0,
  0.01, -0.1, 0, 1.0
];
webGL.drawArrays(webGL.TRIANGLE_FAN, 0, 4);

之后就是矩阵变换的代码,用rotate选择的方法去改变矩阵,以秒钟为例,那就是一秒钟走2*Math.PI弧度除以60,这样一分钟60秒刚好一圈,那么代码就是这样实现的。

得到当前秒
计算弧度
初始化矩阵
单元化
旋转矩阵
传递矩阵
const second = new Date().getSeconds();
const rotate = 2 * Math.PI / 60 * second;
const middleMat4 = mat4.create();
mat4.identity(middleMat4);
mat4.rotate(middleMat4, -rotate, [0, 0, 1]);
let uniformMatrix = webGL.getUniformLocation(program, 'u_formMatrix');
webGL.uniformMatrix4fv(uniformMatrix, false, middleMat4);

随后分钟小时的代码就一样了,分钟和秒钟的计算是一样的,时针就是将60换成12即可。最后就是添加一个setInterval每隔一秒调用一次。注意:在这里绘制的时候,只需要在秒针绘制的时机先clear一遍,分针和时针的时候直接调用drawArray绘制即可,不用再次clear

在这里插入图片描述

完整代码地址:https://github.com/lizuoqun/visualThree/blob/main/webGL/animate/clockTriangle.html

三维世界

视点 & 视线

观察者的位置就是视点,从视点出发,观察者能看到的就是视线。

调整视口观察三维对象

三角形
分层级
设置颜色
lookAt
改变视口观察

前面的案例研究了xy轴的二维平面对象,而三维就是给z设置了对应的值,而改变观察的方向就能看到不同的结果。这里以三角形绘制为例,参考前面的代码实现,先绘制三个三角形,并且修改其z轴的值,在三个不同的平面上

let triangleArray = [
  0.0, 0.5, -0.4, 1.0,
  -0.5, -0.5, -0.4, 1.0,
  0.5, -0.5, -0.4, 1.0,

  0.5, 0.4, -0.2, 1.0,
  -0.5, 0.4, -0.2, 1.0,
  0.0, -0.6, -0.2, 1.0,

  0.0, 0.4, 0.0, 1,
  -0.4, -0.4, 0.0, 1,
  0.4, -0.4, 0.0, 1
];

给每一个层级的三角形设置一下不同的颜色,上面可以区分有三个层级,分别位于z的-0.4、-0.2、0.0三个位置上,修改这个数组对象,就代表着一个点对象有八个数值,分别是x、y、z、1、r、g、b、a,将颜色值和点绑定在一起

let triangleArray = [
  0.0, 0.5, -0.4, 1.0, 0.4, 1.0, 0.4, 1,
  -0.5, -0.5, -0.4, 1.0, 0.4, 1.0, 0.4, 1,
  0.5, -0.5, -0.4, 1.0, 0.4, 1.0, 0.4, 1,

  0.5, 0.4, -0.2, 1.0, 1.0, 0.4, 0.4, 1,
  -0.5, 0.4, -0.2, 1.0, 1.0, 0.4, 0.4, 1,
  0.0, -0.6, -0.2, 1.0, 1.0, 0.4, 0.4, 1,

  0.0, 0.4, 0.0, 1, 0.4, 0.4, 1.0, 1,
  -0.4, -0.4, 0.0, 1, 0.4, 0.4, 1.0, 1,
  0.4, -0.4, 0.0, 1, 0.4, 0.4, 1.0, 1
];

修改着色器代码,这里使用varying变量,先将颜色值传递给顶点着色器,再透传给片元着色器中,根据varying变量的值,设置颜色。

// 顶点着色器
const vertexString = `
  attribute vec4 a_position;
  attribute vec4 a_color;
  varying vec4 color;
    void main(){
      gl_Position =  a_position;
      color = a_color;
  }`;

// 片元着色器
const fragmentString = `
  precision mediump float;
  varying vec4 color;
  void main(){
    gl_FragColor = color;
  }`;

进行赋值,在js当中通过vertexAttribPointer将颜色值传递给着色器当中的a_color变量,同时因为数组的内容改了,之前是4个数据一个点现在是8个数据一个点,所以设置点的代码也要调整

// 设置点坐标
webGL.vertexAttribPointer(aPosition, 4, webGL.FLOAT, false, 8 * 4, 0);
// 调整不同层级三角形的颜色
let aColor = webGL.getAttribLocation(program, 'a_color');
webGL.enableVertexAttribArray(aColor);
webGL.vertexAttribPointer(aColor, 4, webGL.FLOAT, false, 8 * 4, 4 * 4);

改变视角:在顶点着色器当中添加u_formMatrix,使用lookAt方法设置视点,并传递给着色器中

let modelView = mat4.create();
mat4.identity(modelView);
modelView = mat4.lookAt(modelView, [0, -0.5, 0.2], [0, 0, 0], [0, 1, 0]);
let uniformMatrix = webGL.getUniformLocation(program, 'u_formMatrix');
webGL.uniformMatrix4fv(uniformMatrix, false, modelView);

lookAt(out, eye, center, up): 使用给定的眼睛位置、焦点和上方向轴生成注视矩阵

参数说明

名称类型描述
outmat4截头体矩阵将被写入
eyeReadonlyVec3视口位置
centerReadonlyVec3观看者正在观看的点
upReadonlyVec3指定上方向

叠加矩阵变化

可以再创建一个新的矩阵进行旋转90度,之后将视口矩阵和旋转矩阵乘积重新赋值也就完成了叠加矩阵变化。

let ModelMatrix = mat4.create();
mat4.identity(ModelMatrix);
mat4.rotate(ModelMatrix, ModelMatrix, Math.PI / 2, [0, 0, 1]);

let ViewMatrix = mat4.create();
mat4.identity(ViewMatrix);
ViewMatrix = mat4.lookAt(ViewMatrix, [0, 0, 0.3], [0, 0, 0], [0, 1, 0]);
let mvMatrix = mat4.create();
mat4.multiply(mvMatrix, ViewMatrix, ModelMatrix);
// 最后将这个进行赋值
let uniformMatrix = webGL.getUniformLocation(program, 'u_formMatrix');
webGL.uniformMatrix4fv(uniformMatrix, false, mvMatrix);

原本都是正向上的三角形就变成了横的了

在这里插入图片描述

可视范围(正射投影)

在上一个案例当中,当视点在极右或极左的位置时,三角形会缺少一部分。原因是没有指定可视范围,即实际观察得到的区域边界

两类常用的可视空间:

  • 长方体可视空间,也称盒状空间,由正射投影产生
  • 四棱锥/金字塔可视空间,由透视投影产生

可视空间由前后两个矩形表面确定,分别称近裁剪面(near)和远裁剪面(far)

改变视口可视域:ortho(out, left, right, bottom, top, near, far):生成具有给定边界的正交投影矩阵

名称类型描述
outmat4输出矩阵
leftnumber截头体的左边界
rightnumber右边界
bottomnumber底边界
topnumber上边界
nearnumber
farnumber

改变视口可视域,通过ortho方法设置可视域范围,这样他的坐标系取值就变成了canvas的坐标系,绘制图形的坐标值也要进行相对应的调整

let ProjMatrix = mat4.create();
mat4.identity(ProjMatrix);
mat4.ortho(ProjMatrix, -100, 100, -100, 100, near, far);    //修改可视域范围

let uniformMatrix = webGL.getUniformLocation(program, 'u_formMatrix');
webGL.uniformMatrix4fv(uniformMatrix, false, ProjMatrix);

可视空间(透视投影)

在正射投影的可视空间中,不管三角形与视点的距离是远是近,它有多大,那么画出来就有多大。为了打破这条限制,使用透视投影可视空间,它将使场景具有深度性

设置透视投影:perspective(out, fovy, aspect, near, far):生成具有给定边界的透视投影矩阵

名称类型描述
outmat4输出矩阵
fovynumber垂直方向的视野角度(上截面与下截面的角度)
aspectnumber纵横比(宽高比)
nearnumber
farnumber

创建一个透视投影矩阵,并赋值给uniformMatrix,去修改传入的角度的时候可以观察到变化

let ProjMatrix = mat4.create();
mat4.identity(ProjMatrix);
//角度小,看到的物体大,角度大,看到的物体小。
mat4.perspective(ProjMatrix, 160 * Math.PI / 180, 1, 1, 100); //修改可视域范围

在这里插入图片描述

正射投影和透视投影的区别
  • 在透视投影下,产生的三维场景看上去更是有深度感,更加自然,因为我们平时观察真实世界用的也是透视投影。在大多数情况下,比如三维射击类游戏中,我们都应当采用透视投影。
  • 正射投影的好处是用户可以方便地比较场景中物体( 比如两个原子的模型)
    的大小,这是因为物体看上去的大小与其所在的位置没有关系。在建筑平面图等技术绘图的相关场合,应当使用这种投影。

在这里插入图片描述

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

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

相关文章

如何把数据从SQLite迁移到PostgreSQL

## 如何把数据从SQLite迁移到PostgreSQL 文章目录 1、DB-Engines 中的SQLite 和 PostgreSQL2、SQLite安装和测试2.1、编译安装SQLite2.2、数据测试 3、Postgresql安装和测试3.1、编译安装postgresql3.2、测试 4、pgloader安装5、数据迁移和验证5.1、准备参数文件5.2、数据迁移…

Qt使用QGraphicsView绘制线路图————附带详细实现代码

文章目录 0 效果1 核心1.1 简单示例1.1.1 解读 1.2 创建用户交互1.2.1 完整示例 1.3 创建图形元1.3.1 绘制直线1.3.2 绘制贝塞尔曲线1.3.3 绘制图片 1.4 移动的小车 2 使用自定义视图类参考 0 效果 视图中包含线路、道岔、信号灯、火车。 下图为站点信号灯: 下图…

【Linux】调试器——gdb使用

目录 一、预备知识 二、常用指令 三、调试技巧 (一)监视变量的变化指令 watch (二)更改指定变量的值 set var 正文 一、预备知识 程序的发布形式有两种,debug和release模式,Linux gcc/g出来的二进制…

【数据分享】2000—2024年我国乡镇的逐年归一化植被指数(NDVI)数据(年最大值/Shp/Excel格式)

之前我们分享过2000-2024年我国逐年的归一化植被指数(NDVI)栅格数据,该逐年数据是取的当年月归一化植被指数(NDVI)的年最大值!另外,我们基于此年度栅格数据按照行政区划取平均值,得到…

Shell 不神秘:拆解 Linux 命令行的逻辑与效率

初始shell shell的概述 什么是shell 本质 shell本质是脚本文件:完成批处理。 比如 有一个文件 中十个文件,这十个文件中每个文件又有是个子文件,由人来处理,很麻烦,但如果写一个脚本文件,让脚本来替我…

win 远程 ubuntu 服务器 安装图形界面

远程结果:无法使用docker环境使用此方法 注意要写IP和:数字 在 ubuntu 服务器上安装如下: # 安装 sudo apt-get install tightvncserver # 卸载 sudo apt purge tightvncserver sudo apt autoremove#安装缺失的字体包: sudo apt update s…

大模型高质量rag构建:A Cheat Sheet and Some Recipes For Building Advanced RAG

原文:A Cheat Sheet and Some Recipes For Building Advanced RAG — LlamaIndex - Build Knowledge Assistants over your Enterprise DataLlamaIndex is a simple, flexible framework for building knowledge assistants using LLMs connected to your enterpris…

【Qt】游戏场景和图元

一:图元 advance函数: 在 Qt 框架里,QGraphicsItem 是用于在 QGraphicsScene 中绘制图形项的基类。advance(int phase) 是 QGraphicsItem 类的一个虚函数,其主要用途是让图形项在场景的动画或更新过程里完成特定的逻辑操作。 p…

开源的CMS建站系统可以随便用吗?有什么需要注意的?

开源CMS建站系统虽然具有许多优点,但并非完全“随便用”。无论选哪个CMS系统,大家在使用的时候,可以尽可能地多注意以下几点: 1、版权问题 了解开源许可证:不同的开源CMS系统采用不同的开源许可证,如GPL、…

初始ARM

ARM最基础的组成单元。 最小系统:能系统能够正常工作的最少器件构成的系统 。 一、CPU基础定义 1. 核心定位 计算机三大核心部件: CPU(运算与控制)内部存储器(数据存储)输入/输出设备(数据交互…

DataPlatter:利用最少成本数据提升机器人操控的泛化能力

25年3月来自中科院计算所的论文“DataPlatter: Boosting Robotic Manipulation Generalization with Minimal Costly Data”。 视觉-语言-动作 (VLA) 模型在具身人工智能中的应用日益广泛,这加剧对多样化操作演示的需求。然而,数据收集的高成本往往导致…

诠视科技MR眼镜如何安装apk应用

诠视科技MR眼镜如何安装apk应用 1、使用adb工具安装1.1 adb工具下载1.2 解压adb文件1.3 使用adb安装apk1.4 常用adb命令 2、拷贝到文件夹安装 1、使用adb工具安装 1.1 adb工具下载 点击下面的链接开始下载adb工具,下载结束以后解压文件。 下载链接: https://down…

搭建前端环境和后端环境

搭建前端环境 ①、安装vscode,并安装相应的插件工具 ②、安装node.js,可以选择当前版本,或者其他版本 ③、创建工作区 创建一个空文件夹,然后通过vscode工具打开,保存为后缀名为.code-workspace ④、从gitee…

Polhemus FastScan 单摄像头3D激光扫描器

FastSCAN Cobra是Polhemus公司研制的手持激光扫描仪。与以前的产品比较,它节省了30%的费用,体积也减小了一半 ,但仍然保留了所有功能,使用和携带都更加方便。作为超小的手持激光扫描仪,FastSCAN Cobra对扫描三维物体具…

召唤数学精灵

1.召唤数学精灵 - 蓝桥云课 问题描述 数学家们发现了两种用于召唤强大的数学精灵的仪式,这两种仪式分别被称为累加法仪式 A(n) 和累乘法仪式 B(n)。 累加法仪式 A(n) 是将从1到 n 的所有数字进行累加求和,即: A(n)12⋯n 累乘法仪式 B(n) …

2025图像处理和深度学习国际学术会议(IPDL 2025)

重要信息 官网:www.IPDL.xyz 时间:2025年4月11-13日 地点:中国-成都 简介 随着深度学习和图像处理技术的迅速发展,相关技术的应用逐渐渗透到各个行业,如医疗影像分析、自动驾驶、安防监控和智能制造等。这些应用的…

使用uni-app框架 写电商商城前端h5静态网站模板项目-手机端-前端项目练习

以前用vue2 分享过一个电商商城前端静态网站项目-电脑端,需要的小伙伴还是很多的,最近又花了几天更新了一个 手机端的 电商商城h5项目,今天也分享一下实现方案。 对于以前写的 电商商城前端静态网站模板-电脑端,有兴趣的小伙伴 可…

远心镜头原理

文章目录 原理特点分类应用领域 参考:B站优致谱视觉 原理 远心镜头的工作原理基于其特殊的光学设计,旨在解决普通镜头存在的视差问题。它通过将镜头的光轴与成像面垂直,并使主光线平行于光轴,从而确保在一定的物距范围内&#xf…

centos7修复漏洞CVE-2023-38408

漏洞描述: CVE-2023-38408 是 OpenSSH 组件中的一个远程代码执行(RCE)漏洞,影响 OpenSSH 代理(ssh-agent)的安全性。该漏洞被发现于 2023 年 7 月,并被标记为 高危(CVSS 评分 7.3&a…

uniapp微信小程序封装navbar组件

一、 最终效果 二、实现了功能 1、nav左侧返回icon支持自定义点击返回事件(默认返回上一步) 2、nav左侧支持既显示返回又显示返回首页icon 3、nav左侧只显示返回icon 4、nav左侧只显示返回首页icon 5、nav左侧自定义left插槽 6、nav中间支持title命名 7…