WebGL编程指南 - 高级变换与动画基础

news2024/11/26 4:50:29
  • 学习使用一个矩阵变换库,该库封装了矩阵运算的数学细节。
  • 快速上手使用该矩阵库,对图形进行复合变换。
  • 在该矩阵库的帮助下,实现简单的动画效果。

矩阵变换库: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.0
  return (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.0
  return (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博客

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

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

相关文章

华为配置BFD状态与接口状态联动实验

组网图形 图1 配置BFD状态与接口状态联动组网图 BFD简介配置注意事项组网需求配置思路操作步骤配置文件 BFD简介 为了减小设备故障对业务的影响&#xff0c;提高网络的可靠性&#xff0c;网络设备需要能够尽快检测到与相邻设备间的通信故障&#xff0c;以便及时采取措施&…

机器学习中的图像处理与计算机视觉

引言 在现代计算机科学中&#xff0c;图像处理和计算机视觉已成为最活跃的研究领域之一&#xff0c;这得益于机器学习和深度学习的发展。本文将深入探讨图像处理与计算机视觉的基础概念、常见应用、关键技术、常用工具&#xff0c;以及在这些领域中的代码示例。通过本篇文章&a…

pytorch 交叉熵损失函数 BCELoss

BCE Loss 交叉熵损失函数计算公式&#xff1a; BCE Loss - 1/n*(y_actual * log(y_pred) (1 - y_actual) * log(1 - y_pred)) t[i]为标签值&#xff1a;0或者1 o[i]是经过sigmoid后的概率值 BCEWithLogitsLoss 这个损失将Sigmoid层和BCELoss合并在一个类中。 BCEWithLog…

OSPF特殊区域及其他特性

不用的链路这状态信息没必要一直保存&#xff0c;要不路由器承受不了。用OSPF 特殊区域解决 1. Stub区域和Totally Stub区域 R1作为ASBR引入多个外部网段&#xff0c;如果Area 2是普通区域&#xff0c;则R3将向该区域注入5类和4类LSA。 当把Area 2配置为Stub区域后&#xff1a…

Qt项目实战:汉中转拼音(大写字母,首字母)

目录 一.汉字转换 1.加载拼音数据 2.初始化简拼数组 3.汉字转换拼音 4.汉字转简拼 5.首字母 二.效果 1.汉字转拼音 2.汉字转简拼 3.首字母 三.代码 1.h 2.cpp 汉字转拼音的小程序不仅在教育和日常生活中发挥着重要作用&#xff0c;还促进了跨文化交流与信息传播。…

【ubuntu18.04】ubuntu18.04安装vmware-tools之后立即适应窗口为灰色,窗口不会自适应

问题描述 ubuntu18.04安装vmware-tools 点击查看&#xff0c;看到立即适应窗口为灰色 解决方案 关闭虚拟机 编辑此虚拟机 设置 注意 取消勾选拉伸模式 查看可以看到所有选项可选择 注意&#xff1a; 不要全屏的时候查看&#xff0c;全屏看到的立即适应窗口就会显示…

Web,RESTful API 在微服务中的作用是什么?

大家好&#xff0c;我是锋哥。今天分享关于【Web&#xff0c;RESTful API 在微服务中的作用是什么&#xff1f;】面试题&#xff1f;希望对大家有帮助&#xff1b; Web&#xff0c;RESTful API 在微服务中的作用是什么&#xff1f; 在微服务架构中&#xff0c;Web 和 RESTful …

秦时明月2搭建笔记

1.具体配置 数据库不修改&#xff0c;如果修改了&#xff0c;gm那边也要 2.数据库 3.上传配置文件出现问题 参考&#xff1a;对于测试时&#xff0c;错误信息 Caused by: com.mysql.jdbc.PacketTooBigException: Packet for query is too large-CSDN博客 4.启动tomcat&#x…

技术成神之路:设计模式(二十三)解释器模式

相关文章&#xff1a;技术成神之路&#xff1a;二十三种设计模式(导航页) 介绍 解释器模式&#xff08;Interpreter Pattern&#xff09;是一种行为设计模式&#xff0c;用于定义一种语言的文法表示&#xff0c;并提供一个解释器来处理这种文法。它用于处理具有特定语法或表达…

【资深码农】环境搭建篇

导航 MockingBird仿生环境搭建seleniumchrome爬虫环境搭建1.1 安装selenium1.2 安装chrome driver1.3 测试1.4 常见问题驱动不一致原因解决办法最新版本获取 自动下载驱动 NLTK环境搭建opencv环境搭建pytorch环境搭建pyspark环境搭建1 安装环境1.1 jdk下载安装1.2 Scala下载安装…

零售行业 Web EDI 解决方案——知行之云LIP系统操作详解

通过浏览器&#xff0c;用户可以轻松访问知行之云LIP系统&#xff0c;便捷管理订单、发货等操作。如何低成本、高效地实现与主流电商平台的EDI对接&#xff1f;用户对接不同的交易伙伴&#xff0c;可能会遇到不同的EDI需求&#xff0c;这时就需要做个性化定制。本文将以零售行业…

Redis+Lua限流的四种算法

1. 固定窗口&#xff08;Fixed Window&#xff09; 原理&#xff1a; 固定窗口算法将时间划分为固定的时间段&#xff08;窗口&#xff09;&#xff0c;比如 1 秒、1 分钟等。在每个时间段内&#xff0c;允许最多一定数量的请求。如果请求超出配额&#xff0c;则拒绝。 优点…

解锁流量密码:TikTok常见引流方式分享

在这个TikTok这个竞争激烈但又遍布商机的平台上&#xff0c;如何有效地引流&#xff0c;尤其是对于新手来说&#xff0c;是一个重要的课题。本文将详细介绍TikTok的几种常见引流方式&#xff0c;并为新手提供切实可行的引流策略&#xff0c;以帮助他们在平台上获得更高的曝光率…

Java 字节流:高效处理二进制数据

前言 字节流是 Java I/O 系统的一部分&#xff0c;专门用于处理字节数据。由于所有数据在计算机中最终都以字节形式存在&#xff0c;这意味着字节流可以操作任何类型的数据&#xff0c;包括文本、图片、视频等。 Java 提供了多种字节流类&#xff0c;这些类继承自 InputStrea…

Python 爬虫项目实战:爬取某云热歌榜歌曲

一、网络爬虫的定义 网络爬虫&#xff08;Web Crawler&#xff09;&#xff0c;也成为网页蜘蛛或者网页机器人&#xff0c;是一种按照既定规则自动浏览网络并提取信息的程序。爬虫的主要用途包括数据采集、网络索以及内容抓取等。 二、爬虫基本原理 1、种子URL&#xff1a;爬…

使用Python循环地画一个简单的爱心 - 烂漫教程

运行软件&#xff1a;pycharm 画一个爱心的代码&#xff1a; import turtle def print_love1():# 设置画笔颜色和粗细turtle.pencolor("red")turtle.pensize(6)# 绘制爱心turtle.fillcolor("pink")turtle.begin_fill()turtle.left(45)turtle.forward(100…

新鲜出炉面试题之【说说spring spring MVC spring boot的区别】

Spring MVC 和 Spring Boot 是 Spring 框架的一部分&#xff0c;但它们的目的和用途有所不同。下面详细阐述这两者之间的区别。 1. 概念 Spring MVC&#xff1a; Spring MVC 是一种基于请求-响应模式的 Web 框架&#xff0c;属于 Spring 框架的一部分。它提供了一种分离的方式…

【Jenkins】解决在Jenkins Agent节点容器内无法访问物理机的docker和docker compose的问题

解决在Jenkins Agent节点容器内无法访问物理机的docker和docker compose的问题 1. 确定物理机docker和docker compose已经安装2. 编写Jenkins Agent结点docker-compose.yaml配置文件3. 修改docker运行时文件权限4. 启动容器并验证 最近接触到一个发布产物是一个 docker镜像的项…

ORACLE 的SCHEDULER创建JOB

JOB_NAME&#xff1a;指定任务的名称&#xff0c;必选值&#xff0c;注意要确保指定的名称唯一。 JOB_TYPE&#xff1a;任务执行的操作类型&#xff0c;必选值&#xff0c;STORED_PROCEDURE&#xff1a;表示任务执行的是 ORACLE 存储过程。 JOB_ACTION&#xff1a;任务执行的操…

Pendle protocol

道阻且长&#xff0c;行而不辍&#xff0c;未来可期 我身边多是聪明的人&我一直认为我不是那个会学习的人&#xff0c;以往看官方文档&#xff0c;总有许多理解不透的地方&#xff0c;需要靠众多博主的白话分析才能理解&#xff0c;but,在学习Pendle protocol的时候&#x…