WebGL编程指南 - 高级变换与动画基础
- 学习使用一个矩阵变换库,该库封装了矩阵运算的数学细节。
- 快速上手使用该矩阵库,对图形进行复合变换。
- 在该矩阵库的帮助下,实现简单的动画效果。
矩阵变换库:cuon-matrix.js
OpenGL中的函数:
书中 cuon-matrix.js 函数库中是这样实现的:
函数库主要创建了一个Matrix4
类(构造函数),在该类原型函数下绑定了众多方法,许多函数之前都留有注释,我们主要看一下代码最前面的注释:
/** * This is a class treating 4x4 matrix.* This class contains the function that is equivalent to OpenGL matrix stack.* The matrix after conversion is calculated by multiplying a conversion matrix from the right.* The matrix is replaced by the calculated result.*/
根据注释可知,该函数库主要处理4×4的矩阵,对标OpenGL中矩阵处理函数。函数库提供了一个名为Matrix4
的对象(构造函数),我们可以通过new方法创建它的实例,对象内部挂载了许多关于矩阵计算的方法。
这样使用函数库的方法,写起来更加简单
- 矩阵的创建方式:
// RotatedTriangle_Matrix.js
...// 创建旋转矩阵let radian = (Math.PI * ANGLE) / 180.0 // 转换为弧度制let cosB = Math.cos(radian)let sinB = Math.sin(radian)// 注意WebGL中矩阵是列主序的let xformMatrix = new Float32Array([cosB, sinB, 0.0, 0.0,-sinB, cosB, 0.0, 0.0,0.0, 0.0, 1.0, 0.0,0.0, 0.0, 0.0, 1.0,])...
// RotatedTriangle_Matrix4.js
...// 创建旋转矩阵// 为旋转矩阵创建Matrix4对象let xformMatrix = new Matrix4()// 将xformMatrix设置为旋转矩阵xformMatrix.setRotate(ANGLE, 0, 0, 1)
...
- 传输矩阵数据
// RotatedTriangle_Matrix.js
...// 将旋转图形所需数据传输给顶点着色器let u_xformMatrix = gl.getUniformLocation(gl.program, 'u_xformMatrix')if (!u_xformMatrix) {console.log('Failed to get the storage location of u_xformMatrix')}gl.uniformMatrix4fv(u_xformMatrix, false, xformMatrix)
...
// RotatedTriangle_Matrix4.js
...let u_xformMatrix = gl.getUniformLocation(gl.program, 'u_xformMatrix')if (!u_xformMatrix) {console.log('Failed to get the storage location of u_xformMatrix')}gl.uniformMatrix4fv(u_xformMatrix, false, xformMatrix.elements)
...
Matrix4在不提供数据直接初始化的情况下,其挂载的element元素就是一个单位阵,之后大部分操作都是对element元素进行的。
从上表可见,Matrix4对象有两种方法:
- 一种方法的名称中含有前缀set,这一类方法会根据参数计算出变换矩阵,然后将变换矩阵写入自身;
- 另一种方法的名称中不含set前缀,这一类方法在计算出变换矩阵后,会将自身与计算出的变换矩阵相乘,最终结果写入Matrix4对象。
复合变换RotatedTranslatedTriangle.js
相关内容:采用Matirx4对象,运用矩阵乘法模拟符合变换。
相关函数:Matrix4.setRotate(), Matrix4.translate()注意:矩阵乘法的先后顺序与模型变换的先后顺序有关,不能随意变化(矩阵乘法不满足乘法交换律)。
注意是 左乘
一个模型可能经过了多次变换,将这些变换全部复合成一个等效的变换,就得到了模型变换(model transformation),或称建模变换(modeling transformation),相应的,模型变换的矩阵称为模型矩阵(model matrix)。
重写Rotatedtriangle_Matrix4 代码:
- 命名习惯,在顶点着色器中命名uniform变量(mat4)为u_ModelMatrix,取模型矩阵之意。
// 顶点着色器
var VSHADER_SOURCE ='attribute vec4 a_Position;\n' +'uniform mat4 u_ModelMatrix;\n' +'void main(){\n' +' gl_Position = u_ModelMatrix * a_Position;\n' +'}\n'
- 构建矩阵时,采用Matrix4对象,先设置对象为旋转矩阵,再和平移矩阵相乘。
// 创建Matrix4对象let modelMatrix = new Matrix4()// 旋转角度let ANGLE = 90.0// 平移距离let Tx = 0.5// 设置模型矩阵为旋转矩阵modelMatrix.setRotate(ANGLE, 0, 0, 1)// 将模型矩阵乘以平移矩阵modelMatrix.translate(Tx, 0, 0)// 将模型矩阵传输给顶点着色器let u_ModelMatrix = gl.getUniformLocation(gl.program, 'u_ModelMatrix')if (!u_ModelMatrix) {console.log('Failed to get the storage location of u_ModelMatrix')}gl.uniformMatrix4fv(u_ModelMatrix, false, modelMatrix.elements)
完整代码:
// RotatedTranslatedTriangle.js
// 顶点着色器
var VSHADER_SOURCE ='attribute vec4 a_Position;\n' +'uniform mat4 u_ModelMatrix;\n' +'void main(){\n' +' gl_Position = u_ModelMatrix * a_Position;\n' +'}\n'
// 片元着色器
var FSHADER_SOURCE ='void main(){\n' + ' gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);\n' + '}\n'// 主函数
function main() {// 获取canvas元素let canvas = document.getElementById('webgl')// 获取WebGL上下文let gl = getWebGLContext(canvas)if (!gl) {console.log('Failed to get the rendering context for WebGL')return}// 初始化着色器if (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) {console.log('Failed to initialize shaders')return}// 设置顶点位置let n = initVertexBuffers(gl)if (n < 0) {console.log('Failed to set the positions of the vertices')return}// 创建Matrix4对象let modelMatrix = new Matrix4()// 旋转角度let ANGLE = 90.0// 平移距离let Tx = 0.5// 设置模型矩阵为旋转矩阵modelMatrix.setRotate(ANGLE, 0, 0, 1)// 将模型矩阵乘以平移矩阵modelMatrix.translate(Tx, 0, 0)// 将模型矩阵传输给顶点着色器let u_ModelMatrix = gl.getUniformLocation(gl.program, 'u_ModelMatrix')if (!u_ModelMatrix) {console.log('Failed to get the storage location of u_ModelMatrix')}gl.uniformMatrix4fv(u_ModelMatrix, false, modelMatrix.elements)// 设置背景色gl.clearColor(0.0, 0.0, 0.0, 1.0)// 清空绘图区gl.clear(gl.COLOR_BUFFER_BIT)// 绘制三角形gl.drawArrays(gl.TRIANGLES, 0, n)
}function initVertexBuffers(gl) {// 设置类型化数组和顶点数let vertices = new Float32Array([0.0, 0.3, -0.3, -0.3, 0.3, -0.3])let n = 3// 创建缓冲区对象let vertexBuffer = gl.createBuffer()if (!vertexBuffer) {console.log('Failed to create the buffer object')return -1}// 绑定缓冲区gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer)// 缓冲区写入数据gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STREAM_DRAW)let 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}// 将缓冲区分配给attribute变量gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, 0, 0)// 开启attribute变量(连接)gl.enableVertexAttribArray(a_Position)return n
}
动画-RotatingTriangle.js
相关内容:通过JavaScript灵活设计WebGL系统,通过反复变换和重绘图形生成动画效果;setInterval()系列函数和requestAnimationgFrame()系列函数的异同。
相关函数:setInterval(), requestAnimationFrame(), cancelAnimationFrame()
机制一:在t0、t1、t2、t3等时刻反复调用同一个函数来绘制三角形。
机制二:每次绘制之前,清除上次绘制的内容,并使三角形旋转相应的角度。
基于此,该实例程序与前面的示例有以下三点区别:
- 实现反复调用绘制函数的机制(机制一)
- 定义绘制函数,在绘制函数中包括清空绘图区、向着色器传值、绘制三步
- 由于程序需要反复绘制,所以在一开始就指定了背景色。
// RotatedTranslatedTriangle.js
// 顶点着色器
var VSHADER_SOURCE ='attribute vec4 a_Position;\n' +'uniform mat4 u_ModelMatrix;\n' +'void main(){\n' +' gl_Position = u_ModelMatrix * a_Position;\n' +'}\n'
// 片元着色器
var FSHADER_SOURCE ='void main(){\n' + ' gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);\n' + '}\n'
// 旋转速度(度/秒)
var ANGLE_STEP = 45.0// 主函数
function main() {// 获取canvas元素let canvas = document.getElementById('webgl')// 获取WebGL上下文let gl = getWebGLContext(canvas)if (!gl) {console.log('Failed to get the rendering context for WebGL')return}// 初始化着色器if (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) {console.log('Failed to initialize shaders')return}// 设置顶点位置let n = initVertexBuffers(gl)if (n < 0) {console.log('Failed to set the positions of the vertices')return}// 设置背景色gl.clearColor(0.0, 0.0, 0.0, 1.0)// 获取u_ModelMatrix变量存储位置let u_ModelMatrix = gl.getUniformLocation(gl.program, 'u_ModelMatrix')if (!u_ModelMatrix) {console.log('Failed to get the storage location of u_ModelMatrix')}// 三角形当前旋转角度let currentAngle = 0.0// 模型矩阵,Matrix4对象let modelMatrix = new Matrix4()// 开始绘制三角形let tick = function () {currentAngle = animate(currentAngle) // 更新旋转角draw(gl, n, currentAngle, modelMatrix, u_ModelMatrix)requestAnimationFrame(tick) // 请求浏览器调用tick}tick()
}function initVertexBuffers(gl) {// 设置类型化数组和顶点数let vertices = new Float32Array([0.0, 0.3, -0.3, -0.3, 0.3, -0.3])let n = 3// 创建缓冲区对象let vertexBuffer = gl.createBuffer()if (!vertexBuffer) {console.log('Failed to create the buffer object')return -1}// 绑定缓冲区gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer)// 缓冲区写入数据gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STREAM_DRAW)let 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}// 将缓冲区分配给attribute变量gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, 0, 0)// 开启attribute变量(连接)gl.enableVertexAttribArray(a_Position)return n
}
function draw(gl, n, currentAngle, modelMatrix, u_ModelMatrix) {// 设置旋转矩阵modelMatrix.setRotate(currentAngle, 0, 0, 1)// 旋转前加个平移// modelMatrix.translate(0.5, 0, 0)// 将旋转矩阵传输给顶点着色器gl.uniformMatrix4fv(u_ModelMatrix, false, modelMatrix.elements)// 清空绘图区gl.clear(gl.COLOR_BUFFER_BIT)// 绘制三角形gl.drawArrays(gl.TRIANGLES, 0, n)
}// 记录上一次调用函数的时刻
var g_last = Date.now()
// 更新旋转角
function animate(angle) {// 计算距离上次调用经过多长时间let now = Date.now()let elapsed = now - g_last // 毫秒g_last = now// 根据上次调用的时间,更新当前旋转角度let newAngle = angle + (ANGLE_STEP * elapsed) / 1000.0return (newAngle %= 360)
}
重复调用函数的方法:
// 开始绘制三角形let tick = function () {currentAngle = animate(currentAngle) // 更新旋转角draw(gl, n, currentAngle, modelMatrix, u_ModelMatrix) // 绘制三角形requestAnimationFrame(tick) // 请求浏览器调用tick}tick()
该方法是一个诞生较早的方法,其出现时浏览器还没有支持多标签页,所以在现代浏览器中,不论标签页是否被激活,该方法都会反复调用func,如果标签页较多,就会增加浏览器的负荷。
这一方法只有当标签页处于激活状态时才会生效,基于此也无法指定重复调用的时间间隔。
更新旋转角度:
...
// 旋转速度(度/秒)
var ANGLE_STEP = 45.0
...
// 记录上一次调用函数的时刻
var g_last = Date.now()
// 更新旋转角
function animate(angle) {// 计算距离上次调用经过多长时间let now = Date.now()let elapsed = now - g_last // 毫秒g_last = now// 根据上次调用的时间,更新当前旋转角度let newAngle = angle + (ANGLE_STEP * elapsed) / 1000.0return (newAngle %= 360)
}
animate()是更新旋转角的主要部分,该函数配合当前旋转角currentAngle变量、全局变量ANGLE_STEP变量(旋转速度)和g_last(上一次调用函数的时刻)使用,基本思路是:根据本次调用与上次调用之间的时间间隔来决定这一帧的旋转角度比上一帧大出多少
绘制函数的设计:
/*** 绘制三角形* @param {WebGLRenderingContext} gl 上下文对象* @param {number} n 顶点数量 int* @param {number} currentAngle 当前旋转角度 float* @param {Martix4} modelMatrix 根据当前的旋转角度计算出的旋转矩阵,存储在Martix4对象中* @param {number} u_ModelMatrix 顶点着色器中同名的uniform变量的存储位置 uint*/
function draw(gl, n, currentAngle, modelMatrix, u_ModelMatrix) {// 设置旋转矩阵modelMatrix.setRotate(currentAngle, 0, 0, 1)// 将旋转矩阵传输给顶点着色器gl.uniformMatrix4fv(u_ModelMatrix, false, modelMatrix.elements)// 清空绘图区gl.clear(gl.COLOR_BUFFER_BIT)// 绘制三角形gl.drawArrays(gl.TRIANGLES, 0, n)
}
参考:【《WebGL编程指南》读书笔记-高级变换与动画基础】_webgl高级-CSDN博客
相关文章:

WebGL编程指南 - 高级变换与动画基础
学习使用一个矩阵变换库,该库封装了矩阵运算的数学细节。快速上手使用该矩阵库,对图形进行复合变换。在该矩阵库的帮助下,实现简单的动画效果。 矩阵变换库:cuon-matrix.js OpenGL中的函数: 书中 cuon-matrix.js 函数…...

银行客户贷款行为数据挖掘与分析
#1024程序员节 | 征文# 在新时代下,消费者的需求结构、内容与方式发生巨大改变,企业要想获取更多竞争优势,需要借助大数据技术持续创新。本文分析了传统商业银行面临的挑战,并基于knn、逻辑回归、人工神经网络三种算法࿰…...

制程质量管理方案设计
质量管理系统框架——QMS 涵盖产生产制造体系的全生命周期的质量管理过程 与SAP、WMS、MES、OA等业务系统进行集成,整合各业务系统中的质量信息 利用整合的全价值链质量信息,寻找质量改进点和质量创新点 系统功能模块管理 系统管理:用户管理…...

uniapp移动端优惠券! 附源码!!!!
本文为常见的移动端uniapp优惠券,共有6种优惠券样式(参考了常见的优惠券),文本内容仅为示例,您可在此基础上调整为你想要的文本 预览效果 通过模拟数据,实现点击使用优惠券让其变为灰色的效果(模…...

【分布式技术】中间件-zookeeper安装配置
文章目录 安装部署1. 安装ZooKeeper2. 配置ZooKeeper3. 启动ZooKeeper服务器4. 使用ZooKeeper命令行客户端5. 使用ZooKeeper的四个基本操作6. ZooKeeper集群模式7. 安全和权限8. 监控和日志 相关文献 安装部署 在Linux环境中操作ZooKeeper通常涉及以下几个方面: 1…...

高等数学 7.6高阶线性微分方程
文章目录 一、线性微分方程的解的结构*二、常数变易法 方程 d 2 y d x 2 P ( x ) d y d x Q ( x ) f ( x ) (1) \cfrac{\mathrm{d}^2 y}{\mathrm{d}x^2} P(x) \cfrac{\mathrm{d}y}{\mathrm{d}x} Q(x) f(x) \tag{1} dx2d2yP(x)dxdyQ(x)f(x)(1) 叫做二阶线性微分方程。…...

LSP的建立
MPLS需要为报文事先分配好标签,建立一条LSP,才能进行报文转发。LSP分为静态LSP和动态LSP两种。 静态LSP的建立 静态LSP是用户通过手工为各个转发等价类分配标签而建立的。由于静态LSP各节点上不能相互感知到整个LSP的情况,因此静态LSP是一个…...

huggingface的数据集下载(linux下clone)
1. 安装lfs sudo apt-get install git-lfs 或者 apt-get install git-lfs 2. git lfs install git lfs install 3. git clone dataset包 第2,3步骤的截图如下:...

Java使用dom4j生成kml(xml)文件遇到No such namespace prefix: xxx is in scope on:问题解决
介绍addAttribute和addNamepsace: addAttribute 方法 addAttribute 方法用于给XML元素添加属性。属性(Attributes)是元素的修饰符,提供了关于元素的额外信息,并且位于元素的开始标签中。属性通常用于指定元素的行为或样式&#…...

深入探讨Java中的LongAdder:使用技巧与避坑指南
文章目录 一、什么是LongAdder?二、LongAdder的简单使用示例代码: 三、LongAdder的工作原理四、LongAdder的常见使用场景五、使用LongAdder时的注意事项(避坑指南)1. 不要滥用LongAdder2. sum()方法与精度问题3. 避免过度使用rese…...

【本科毕业设计】基于单片机的智能家居防火防盗报警系统
基于单片机的智能家居防火防盗报警系统 相关资料链接下载摘要Abstract第1章 绪论1.1课题的背景1.2 研究的目的和意义 第2章 系统总体方案设计2.1 设计要求2.2 方案选择和论证2.2.1 单片机的选择2.2.2 显示方案的选择 第3章 系统硬件设计3.1 整体方案设计3.1.1 系统概述3.1.2 系…...

C语言 动态数据结构的C语言实现单向链表-2
建立一个单向链表 在单向链表中查找节点---查找尾节点 在单向链表中查找节点 --- 查找第 n 个节点 向单向链表中插入一个节点 向单向链表的尾部插入一个节点 向单向链表中某节点后插入一个节点 向单向链表中插入一个节点 删除单向链表中的某一节点 链表 vs 数组 动态数据结构...

Github 2024-10-23C开源项目日报 Top10
根据Github Trendings的统计,今日(2024-10-23统计)共有10个项目上榜。根据开发语言中项目的数量,汇总情况如下: 开发语言项目数量C项目10PLpgSQL项目1Redis - 内存数据库和数据结构服务器 创建周期:5411 天开发语言:C协议类型:BSD 3-Clause “New” or “Revised” Licen…...

ubuntu20.04 opencv4.0 /usr/local/lib/libgflags.a(gflags.cc.o): relocation报错解决
在一个只有ubuntu20.04的docker环境中配置opencv4.0.0, 什么库都没有,都要重新安装, 其他的问题在网上都找到了解决方案,唯独这个问题比较棘手: [ 86%] Linking CXX executable …/…/bin/opencv_annotation /usr/bin/ld: /usr/lo…...

android openGL ES详解——混合
一、混合概念 混合是一种常用的技巧,通常可以用来实现半透明。但其实它也是十分灵活的,你可以通过不同的设置得到不同的混合结果,产生一些有趣或者奇怪的图象。混合是什么呢?混合就是把两种颜色混在一起。具体一点,就…...

计网--物理层
目录 物理层的任务 1、常见概念 2、信道极限容量 3、传输介质 (1)导引型传输介质 (2)非导引型传输介质 4、信道复用技术 (1)频分 / 时分 复用 (2)波分复用WDM (…...

算法的学习笔记—数组中的逆序对(牛客JZ51)
😀前言 在算法和数据结构领域,"逆序对"是一个经典问题。它在数组中两个数字之间定义,若前面的数字大于后面的数字,则这两个数字组成一个逆序对。我们要做的就是,给定一个数组,找出数组中所有的逆…...

Golang | Leetcode Golang题解之第498题对角线遍历
题目: 题解: func findDiagonalOrder(mat [][]int) []int {m, n : len(mat), len(mat[0])ans : make([]int, 0, m*n)for i : 0; i < mn-1; i {if i%2 1 {x : max(i-n1, 0)y : min(i, n-1)for x < m && y > 0 {ans append(ans, mat[x…...

什么是全局污染?怎么避免全局污染?
全局污染(Global Pollution)是指在编程过程中,过度使用全局变量或对象导致命名冲突、代码可维护性下降及潜在错误增加的问题。在 JavaScript 等动态语言中,尤其需要关注全局污染的风险。 全局污染的影响 1. 命名冲突 3. 意外修改…...

C# 串口通信教程
串口通信(Serial Communication)是一种用于设备之间数据传输的常见方法,通常用于与外部硬件设备(如传感器、机器人、微控制器)进行通信。在 C# 中,System.IO.Ports 命名空间提供了与串口设备交互的功能&…...

PHP编程基础
PHP(Hypertext Preprocessor,超文本预处理器)是一种广泛使用的开源服务器端脚本语言,主要用于网页开发,同时也可以进行命令行脚本编写。以下是PHP编程的基础知识: 1. PHP文件结构 PHP文件通常以 .php 为扩…...

TwinCAT3下位机配置EAP通讯传递与接收变量
添加EAP设备 DEVICE中右键选择添加新项,添加EAP(EtherCAT Automation Protocal)选择Network Variables类型,如下图。 设置网络适配器来激活EAP,在Adapter中选择search,选择网络适配器后确定,…...

近似推断 - 期望最大化(EM)篇
前言 近似推断是统计学和机器学习中一个至关重要的领域,尤其在处理复杂模型和不完全数据时显得尤为重要。期望最大化( Expectation Maximization \text{Expectation Maximization} Expectation Maximization,简称 EM \text{EM} EM࿰…...

arp欺骗及其实验
ARP欺骗(ARP Spoofing)是一种网络攻击技术,攻击者通过伪造ARP(地址解析协议)消息,将其MAC地址与目标IP地址关联,从而实现对网络流量的截获、篡改或重定向。以下是ARP欺骗的详细信息:…...
HDU The Boss on Mars(容斥原理)
题目大意: ACM 有 n 名员工,现在是他们从老板那里拿薪水的时候了。所有员工都从 1 到 n 编号。原因不明,如果员工的工作编号是 k,他今年可以获得 k^4 Mars 美元。所以为 ACM 工作的员工非常富有。 因为员工人数太多,…...

nnUnet 大模型学习笔记(续):训练网络(3d_fullres)以及数据集标签的处理
目录 1. 数据集处理 1.1 实现脚本 1.2 json文件 2. 设置读取路径 2.1 设置路径 2.2 数据集转换 2.3 数据集预处理 2.4 训练(3d_fullres) 3. 训练结果展示 关于nnUnet 数据集的处理和环境搭建,参考上文:第四章:nnUnet大模…...

Java中的数据结构与集合源码
目录 一、数据结构 1.1 数据结构概念 1.2 研究对象 1.3 常见存储结构 1.3.1 数组 1.3.2 链表 1.单向链表 2.双向链表 1.3.3 二叉树 1.3.4 栈(FILO,先进后出) 1.3.5 队列(FIFO,先进先出) 二、集合…...

Java应用程序的测试覆盖率之设计与实现(三)-- jacoco cli 客户端
一、背景 上文已把覆盖率数据采集好了,并提供远程连接的tcp地址及端口。 jacoco cli文档jacoco cli jar包 jacococli.jar 我下载好了,放在github工程里。 本文主要是介绍如何使用jacoco cli 客户端读取并生成覆盖率报告。 二、使用 1、dump覆盖率统…...

Deepin V23 / 统信UOS 下安装与配置 tftp
几个月前,我将开发系统从 ubuntu 切换到 Deepin,当时写过一篇文章《使用国产操作系统作为开发系统》。几个月下来,没有感觉有什么不适应,Ubuntu 能做的事情,在 Deepin 上都能做。而且有 UOS 应用商店的加持,…...

java基础学习:定时任务常见实现方式
一、Timer解析 TaskQueue:小顶堆,存放timeTask。 TimerThread:任务执行线程 死循环不断检查是否有任务需要开始执行,有就执行它。始终是一个线程在执行。 单线程执行任务,任务有可能相互阻塞: schedul…...