WebGL系列教程九(动画)

目录

  • 1 前言
  • 2 绘制立方体并进行纹理映射
  • 3 动画思路
  • 4 开始绘制
    • 4.1 在顶点着色器中声明旋转矩阵
    • 4.2 获取旋转矩阵变量并进行赋值
    • 4.3 计算角度
    • 4.4 每一帧都去绘制
    • 4.5 效果
    • 4.6 完整代码
  • 5 总结

1 前言

  上一篇我们讲了WebGL中的基础语法,现在我们已经讲过了三维物体的绘制,着色及纹理映射,现在我们可以讲一些稍微高级点的操作了,这一节我们来讲动画,我们考虑怎么让一个立方体动起来。

2 绘制立方体并进行纹理映射

  这一节的代码是在WebGL系列教程六(纹理映射与立方体贴图)的基础上修改而来的,因此绘制立方体和纹理映射的代码我们直接拿过来。

<script id="vertex-shader" type="x-shader/x-vertex">//顶点位置attribute vec4 a_Position;//纹理坐标attribute vec2 a_TexCoord;//传递纹理坐标varying vec2 v_TexCoord;void main(){gl_Position = a_Position;//直接将纹理坐标赋值给传递变量v_TexCoord = a_TexCoord;}
</script>
<script id="fragment-shader" type="x-shader/x-fragment">precision highp float;//采样器,固定写法uniform sampler2D u_Sampler;//接收顶点着色器传过来的值varying vec2 v_TexCoord;void main(){//到某个纹理坐标去采样,也是固定写法gl_FragColor = texture2D(u_Sampler,v_TexCoord);}
</script>
// Create a cube
//    v6----- v7
//   /|      /|
//  v1------v0|
//  | |     | |
//  | |v5---|-|v4
//  |/      |/
//  v2------v3
const verticesColors = new Float32Array([// 前面-1.0, -1.0,  1.0,   0.0, 0.0,//v2 图片左下角纹理坐标1.0, -1.0,  1.0,   1.0, 0.0,//v3 图片左下角纹理坐标1.0,  1.0,  1.0,   1.0, 1.0,//v0 图片右下角纹理坐标-1.0,  1.0,  1.0,   0.0, 1.0,//v1 图片左上角纹理坐标// 后面-1.0, -1.0, -1.0,   0.0, 0.0,//v5 同上1.0, -1.0, -1.0,   1.0, 0.0,//v4 同上1.0,  1.0, -1.0,   1.0, 1.0,//v7 同上-1.0,  1.0, -1.0,   0.0, 1.0,//v6 同上// 上面-1.0,  1.0,  1.0,   0.0, 0.0,//v1 同上1.0,  1.0,  1.0,   1.0, 0.0,//v0 同上1.0,  1.0, -1.0,   1.0, 1.0,//v7 同上-1.0,  1.0, -1.0,   0.0, 1.0,//v6 同上// 下面-1.0, -1.0, 1.0,   0.0, 0.0,//v2 同上1.0,  -1.0, 1.0,   1.0, 0.0,//v3 同上1.0,  -1.0,-1.0,   1.0, 1.0,//v4 同上-1.0, -1.0,-1.0,   0.0, 1.0,//v5 同上// 左面-1.0, -1.0, -1.0,   0.0, 0.0,//v5 同上-1.0, -1.0,  1.0,   1.0, 0.0,//v2 同上-1.0,  1.0,  1.0,   1.0, 1.0,//v1 同上-1.0,  1.0, -1.0,   0.0, 1.0,//v6 同上// 右面1.0, -1.0,  1.0,   0.0, 0.0,//v3 同上1.0, -1.0, -1.0,   1.0, 0.0,//v4 同上1.0,  1.0, -1.0,   1.0, 1.0,//v7 同上1.0,  1.0,  1.0,   0.0, 1.0,//v0 同上
]);const indices = new Uint8Array([0, 1, 2, 0, 2, 3, // 前面4, 5, 6, 4, 6, 7, // 后面8, 9, 10, 8, 10, 11, // 上面12, 13, 14, 12, 14, 15, // 下面16, 17, 18, 16, 18, 19, // 左面20, 21, 22, 20, 22, 23  // 右面
]);
gl.viewport(0, 0, canvas.width, canvas.height);
gl.enable(gl.DEPTH_TEST);
//顶点
let vertexColorBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER,vertexColorBuffer);
gl.bufferData(gl.ARRAY_BUFFER,verticesColors,gl.STATIC_DRAW);
let FSIZE = verticesColors.BYTES_PER_ELEMENT;
let a_Position = gl.getAttribLocation(program,'a_Position');
gl.vertexAttribPointer(a_Position,3,gl.FLOAT,false,FSIZE*5,0);
gl.enableVertexAttribArray(a_Position);
//指定纹理坐标值
let a_TexCoord = gl.getAttribLocation(program,'a_TexCoord');
gl.vertexAttribPointer(a_TexCoord,2,gl.FLOAT,false,5*FSIZE,3*FSIZE);
gl.enableVertexAttribArray(a_TexCoord);
let image = new Image();
image.src = 'static/sky.jpg';
image.onload = function(){console.log('image ok');//创建纹理对象let texture = gl.createTexture();//获取采样器let u_Sampler = gl.getUniformLocation(program,'u_Sampler');//反转Y轴,canvas的Y轴和WebGL的Y轴方向是反的gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL,1);//启用0号纹理gl.activeTexture(gl.TEXTURE0);gl.bindTexture(gl.TEXTURE_2D,texture);//设置纹理为,缩小纹理时,取纹理坐标周围四个像素的颜色均值gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_MIN_FILTER,gl.LINEAR);//设置对象使用的图片,mipmap层级,图像的格式,纹理的格式,纹理数据类型,图片gl.texImage2D(gl.TEXTURE_2D,0,gl.RGB,gl.RGB,gl.UNSIGNED_BYTE,image);//将0号纹理赋值给采样器gl.uniform1i(u_Sampler,0);//绑定索引缓冲let indexBuffer =  gl.createBuffer();gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER,indexBuffer);gl.bufferData(gl.ELEMENT_ARRAY_BUFFER,indices,gl.STATIC_DRAW);//清空颜色缓冲和深度缓冲gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);//绘制//顶点索引数组如果是Uint8Array,就是UNSIGNED_BYTE,表示数组里的值在0-2^8-1(255)//................Uint16Array,就是UNSIGNED_SHORT,表示数组里的值在0-2^16-1(65535)//................Uint32Array,就是UNSIGNED_INT,表示数组里的值在0-2^32-1(4294967295)gl.drawElements(gl.TRIANGLES, 36, gl.UNSIGNED_BYTE, 0);
};

效果:

在这里插入图片描述

3 动画思路

  现在我们要让这个立方体动起来。那怎么动呢?让立方体始终旋转就可以了。那怎么让它旋转呢?乘以我们之前讲到的旋转矩阵就可以了。那怎么让它一直动呢?用定时器可以实现,每秒都去执行,但是还有性能更好的实现方案,那就是requestAnimationFramerequestAnimationFrame能够让浏览器每一帧都去调用一个函数。

4 开始绘制

4.1 在顶点着色器中声明旋转矩阵

<script id="vertex-shader" type="x-shader/x-vertex">//顶点位置attribute vec4 a_Position;//旋转矩阵uniform mat4 uRotateMatrix;//纹理坐标attribute vec2 a_TexCoord;//传递纹理坐标varying vec2 v_TexCoord;void main(){//旋转矩阵乘以顶点的位置,表示每个顶点都经过了旋转gl_Position = uRotateMatrix * a_Position;//直接将纹理坐标赋值给传递变量v_TexCoord = a_TexCoord;}
</script>
<script id="fragment-shader" type="x-shader/x-fragment">precision highp float;//采样器,固定写法uniform sampler2D u_Sampler;//接收顶点着色器传过来的值varying vec2 v_TexCoord;void main(){//到某个纹理坐标去采样,也是固定写法gl_FragColor = texture2D(u_Sampler,v_TexCoord);}
</script>

4.2 获取旋转矩阵变量并进行赋值

//创建旋转矩阵
var modelMatrix = new Matrix4();
//绕Y轴旋转60度,0,1,0,表示Y轴上的单位向量
modelMatrix.setRotate(60,0,1,0);
var uRotateMatrix = gl.getUniformLocation(program, 'uRotateMatrix');
gl.uniformMatrix4fv(uRotateMatrix,false,modelMatrix .elements);

4.3 计算角度

var ANGLE_STEP = 30.0;//假设每秒旋转30度
var currentAngle = 0.0; //当前是0度
var g_last = Date.now();//记录一个开始时间
function animate(angle) {// 计算角度var now = Date.now();//获取当前时间var elapsed = now - g_last;//两个时间相减,得到两个时间之差,单位为毫秒g_last = now;// 更新角度,一秒是1000毫秒,先算出过了几秒,再乘以角度的步长var newAngle = angle + ANGLE_STEP * (elapsed / 1000.0);return newAngle %= 360;
}

4.4 每一帧都去绘制

let tick = function(){currentAngle = animate(currentAngle);modelMatrix.setRotate(currentAngle,0,1,0);//绘制//略。。。//每一帧都去调用requestAnimationFrame(tick);
};
tick();

4.5 效果

在这里插入图片描述

4.6 完整代码

const verticesColors = new Float32Array([// 前面-1.0, -1.0,  1.0,   0.0, 0.0,//v2 图片左下角纹理坐标1.0, -1.0,  1.0,   1.0, 0.0,//v3 图片左下角纹理坐标1.0,  1.0,  1.0,   1.0, 1.0,//v0 图片右下角纹理坐标-1.0,  1.0,  1.0,   0.0, 1.0,//v1 图片左上角纹理坐标// 后面1.0, -1.0, -1.0,   0.0, 0.0,//v4 同上-1.0, -1.0, -1.0,   1.0, 0.0,//v5 同上-1.0,  1.0, -1.0,   1.0, 1.0,//v6 同上1.0,  1.0, -1.0,   0.0, 1.0,//v7 同上// 上面-1.0,  1.0,  1.0,   0.0, 0.0,//v1 同上1.0,  1.0,  1.0,   1.0, 0.0,//v0 同上1.0,  1.0, -1.0,   1.0, 1.0,//v7 同上-1.0,  1.0, -1.0,   0.0, 1.0,//v6 同上// 下面-1.0, -1.0, 1.0,   0.0, 0.0,//v2 同上1.0,  -1.0, 1.0,   1.0, 0.0,//v3 同上1.0,  -1.0,-1.0,   1.0, 1.0,//v4 同上-1.0, -1.0,-1.0,   0.0, 1.0,//v5 同上// 左面-1.0, -1.0, -1.0,   0.0, 0.0,//v5 同上-1.0, -1.0,  1.0,   1.0, 0.0,//v2 同上-1.0,  1.0,  1.0,   1.0, 1.0,//v1 同上-1.0,  1.0, -1.0,   0.0, 1.0,//v6 同上// 右面1.0, -1.0,  1.0,   0.0, 0.0,//v3 同上1.0, -1.0, -1.0,   1.0, 0.0,//v4 同上1.0,  1.0, -1.0,   1.0, 1.0,//v7 同上1.0,  1.0,  1.0,   0.0, 1.0,//v0 同上
]);const indices = new Uint8Array([0, 1, 2, 0, 2, 3, // 前面4, 5, 6, 4, 6, 7, // 后面8, 9, 10, 8, 10, 11, // 上面12, 13, 14, 12, 14, 15, // 下面16, 17, 18, 16, 18, 19, // 左面20, 21, 22, 20, 22, 23  // 右面
]);
gl.viewport(0, 0, canvas.width, canvas.height);
gl.enable(gl.DEPTH_TEST);
//顶点
let vertexColorBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER,vertexColorBuffer);
gl.bufferData(gl.ARRAY_BUFFER,verticesColors,gl.STATIC_DRAW);
let FSIZE = verticesColors.BYTES_PER_ELEMENT;
let a_Position = gl.getAttribLocation(program,'a_Position');
gl.vertexAttribPointer(a_Position,3,gl.FLOAT,false,FSIZE*5,0);
gl.enableVertexAttribArray(a_Position);//指定纹理坐标值
let a_TexCoord = gl.getAttribLocation(program,'a_TexCoord');
gl.vertexAttribPointer(a_TexCoord,2,gl.FLOAT,false,5*FSIZE,3*FSIZE);
gl.enableVertexAttribArray(a_TexCoord);
let image = new Image();
image.src = 'static/sky.jpg';
// Rotation angle (degrees/second)
let ANGLE_STEP = 30.0;
let currentAngle = 0.0; 
let modelMatrix = new Matrix4();
// Last time that this function was called
let g_last = Date.now();
function animate(angle) {// Calculate the elapsed timelet now = Date.now();let elapsed = now - g_last;g_last = now;// Update the current rotation angle (adjusted by the elapsed time)let newAngle = angle + (ANGLE_STEP * elapsed) / 1000.0;return newAngle %= 360;
}
image.onload = function(){let tick = function(){// console.log('image ok');currentAngle = animate(currentAngle);modelMatrix.setRotate(currentAngle,0,1,0);let texture = gl.createTexture();let u_Sampler = gl.getUniformLocation(program,'u_Sampler');//反转Y轴gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL,1);//启用0号纹理gl.activeTexture(gl.TEXTURE0);gl.bindTexture(gl.TEXTURE_2D,texture);//设置纹理为,缩小纹理,取纹理坐标周围四个像素的颜色均值gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_MIN_FILTER,gl.LINEAR);//设置对象使用的图片,mipmap层级,图像的格式,纹理的格式,纹理数据类型,图片gl.texImage2D(gl.TEXTURE_2D,0,gl.RGB,gl.RGB,gl.UNSIGNED_BYTE,image);//将0号纹理赋值给采样器gl.uniform1i(u_Sampler,0);//绑定索引缓冲let indexBuffer =  gl.createBuffer();gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER,indexBuffer);gl.bufferData(gl.ELEMENT_ARRAY_BUFFER,indices,gl.STATIC_DRAW);//赋值给u_MvpMatrixvar uRotateMatrix= gl.getUniformLocation(program, 'uRotateMatrix');gl.uniformMatrix4fv(uRotateMatrix,false,uRotateMatrix.elements);//清空颜色缓冲和深度缓冲gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);//绘制//顶点索引数组如果是Uint8Array,就是UNSIGNED_BYTE,表示数组里的值在0-2^8-1(255)//................Uint16Array,就是UNSIGNED_SHORT,表示数组里的值在0-2^16-1(65535)//................Uint32Array,就是UNSIGNED_INT,表示数组里的值在0-2^32-1(4294967295)gl.drawElements(gl.TRIANGLES, 36, gl.UNSIGNED_BYTE, 0);requestAnimationFrame(tick, canvas);};tick();};

5 总结

  本文中我们讲解了动画的原理,并在绘制的立方体及纹理贴图后,绕Y轴旋转,形成了一个简单的动画。本篇的内容结合了之前讲过的所有内容,是一个比较综合的例子,希望读者仔细体会,回见~

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

TDengine 与 SCADA 强强联合:提升工业数据管理的效率与精准

随着时序数据库&#xff08;Time Series Database&#xff09;的日益普及&#xff0c;越来越多的工业自动化控制&#xff08;工控&#xff09;人员开始认识到其强大能力。然而&#xff0c;时序数据库在传统实时数据库应用领域&#xff0c;特别是在过程监控层的推广仍面临挑战&a…

【数据结构】排序算法---冒泡排序

文章目录 1. 定义2. 算法步骤3. 动图演示4. 性质5. 算法分析6. 代码实现C语言PythonJavaCGo 结语 1. 定义 冒泡排序&#xff08;英语&#xff1a;Bubble sort&#xff09;是一种简单的排序算法。它重复地走访过要排序的数列&#xff0c;一次比较两个元素&#xff0c;如果它们的…

Android 13 固定systemUI的状态栏为黑底白字,不能被系统应用或者三方应用修改

目录 一.背景 二.思路 三.代码流程 1.colos.xml自定义颜色 2.设置状态栏的背景颜色 3.对View进行操作 ①.对Clock(状态栏左侧的数字时钟)进行操作 ②.对电池(BatteryMeterView)进行操作 4.锁屏状态栏 5.patch汇总 一.背景 客户需求将状态栏固定成黑底白字,并且不能让系…

ipython里如何用?快速查阅帮助

1、&#xff1f;用于查询函数帮助文档&#xff0c;??用于查询带源码的帮助文档 ?用于搜索内容&#xff0c;*作为通配符。

C++调用C# DLL之踩坑记录

C是非托管代码&#xff0c;C#则是托管代码&#xff0c;无法直接调用 CLR的介绍见CLR简介 MSDN提到了两种非托管-托管的交互技术&#xff1a;CLR Interop和COM Interop 后者要将C# 类库注册为COM组件&#xff0c;本文只探讨CLR&#xff0c;要通过C CLR写中间层代码 方式一&…

IDEA 通义灵码 插件使用体验

目录 前言 主要功能 演示代码 解释代码 生成单元测试 生成代码注释 生成优化建议 代码片段补全 总结 前言 自从 AI 技术开始大规模应用&#xff0c;老板就想让下面的牛马借助 AI 工具来提高编码效率&#xff0c;由于团队都没有在实际编码中深度使用过 AI 工具&#x…

Miracast/WifiDisplay开发相关的深入调研分析-android投屏实战开发

Miracast/WifiDisplay概念介绍 Miracast Miracast是由Wi-Fi联盟于2012年所制定&#xff0c;以Wi-Fi直连&#xff08;Wi-Fi Direct&#xff09;为基础的无线显示标准。支持此标准的消费性电子产品&#xff08;又称3C设备&#xff09;可透过无线方式分享视频画面&#xff0c;例如…

VirtualBox 克隆已有的虚拟机

【前提】已经存在一个CentOS 7 虚拟机 【需求】克隆出来一个虚拟机,用于本机 【操作】 1.右击已有的虚拟机 -> 选择克隆 2.给新虚拟机起个名称 以及 生成新的MAC地址 3.克隆 4.修改网络和主机名称 # 修改网络编辑以下2个文件 vi /etc/sysconfig/network-scripts/ifcfg-enp…

Java之内部类

目录 实例内部类 静态内部类 局部内部类 匿名内部类 下面将讲解实例内部类&#xff0c;静态内部类&#xff0c;局部内部类和匿名内部类。 实例内部类 实例内部类&#xff08;也称为非静态内部类&#xff09;依赖于外部类的实例。这意味着&#xff0c;要创建实例内部类的实…

Kubernetes从零到精通(12-Ingress、Gateway API)

Ingress和Gateway API都是Kubernetes中用于管理外部访问集群服务的机制&#xff0c;但它们有不同的设计理念和适用场景。它们的基本原理是通过配置规则&#xff0c;将来自外部的网络流量路由到Kubernetes集群内部的服务上。 Ingress/Gateway API和Service Ingress/Gateway API…

Qt窗口——QToolBar

文章目录 工具栏创建工具栏设置toolTip工具栏配合菜单栏工具栏浮动状态 工具栏 QToolBar工具栏是应用程序中集成各种功能实现快捷键使用的一个区域。 可以有多个&#xff0c;也可以没有。 创建工具栏 #include "mainwindow.h" #include "ui_mainwindow.h&qu…

ARM 工业边缘计算机与 C# 编程的完美融合

在工业领域&#xff0c;随着智能化和数字化的不断推进&#xff0c;ARM 工业边缘计算机凭借其出色的性能和低功耗等优势&#xff0c;逐渐成为众多应用场景的重要支撑。而 C# 编程语言的强大功能和广泛适用性&#xff0c;使其在与 ARM 工业边缘计算机的结合中展现出了巨大的潜力。…

壹嘉情,中国与世界经济文化交流的新桥梁

壹嘉情正在全球华商领域迅速崛起。作为意大利华商总会的中国分部&#xff0c;壹嘉情承载着推动两岸及全球华商深度合作、实现资源共享和互利共赢的使命。它的成立标志着意大利华商总会在全球战略布局上的重要一步&#xff0c;同时也昭示了全球化浪潮中&#xff0c;华人企业正加…

LNMP的简单安装(ubuntu)

LNMP介绍 LNMP 是一种常见的开源软件组合&#xff0c;用于搭建高效的网站服务器环境。LNMP 代表以下四个组件&#xff1a; Linux&#xff1a;操作系统。Linux 是一种稳定、可靠、安全的开源操作系统&#xff0c;常用于服务器环境&#xff0c;特别是在企业级部署中。它负责底层…

小程序——生命周期

文章目录 运行机制更新机制生命周期介绍应用级别生命周期页面级别生命周期组件生命周期生命周期两个细节补充说明总结 运行机制 用一张图简要概述一下小程序的运行机制 冷启动与热启动&#xff1a; 小程序启动可以分为两种情况&#xff0c;一种是冷启动&#xff0c;一种是热…

js 深入理解生成器

目录 概述1 . 生成器基础2. 与普通函数的区别3. 通过 yield 中断执行3.1 yield 是干嘛的&#xff1f;3.2 yield 和 return 的区别3.3 每个生成器对象作用域都是独立的3.4 yeild 的使用位置3.5 生成器对象作为可迭代对象3.6 使用 yield 实现输入和输出3.6.1 yield实现输入3.6.1 …

【JVM安装MySQL】

环境 > VMware Workstation Pro > CentOS 7 >Navicat Premium Lite > MobaXterm添加 MySQL Yum 仓库 根据操作系统在下载界面选取对应yum库进行下载 wget https://dev.mysql.com/get/mysql80-community-release-el7-9.noarch.rpm在文件下载界面安装 rpm -ivh mysq…

<<编码>> 第 14 章 反馈与触发器(3)--锁存器与触发器 示例电路

电平触发 D 型触发器 info::操作说明 鼠标单击逻辑输入切换 0|1 状态 因复位和置位不应同时处在高电平, 因此在输入处加入一个非门反向, 然后复位和置位输入合并为 数据(Data) 输入 注: 当保持位为 0 时, 数据输入无效 primary::在线交互操作链接 https://cc.xiaogd.net/?star…

Stylized Smooth Clouds 卡通风格化云朵包

下载:​​Unity资源商店链接资源下载链接 效果图:

Spring考点总结

01.Spring框架的基本理解 关键字:核心思想IOC\AOP\作用(解耦、简化)&#xff0c;简单描述框架组成 Spring框架是一款轻量级的开发框架&#xff0c;核心思想是IOC&#xff08;控制反转&#xff09;和AOP&#xff08;面向切面编程&#xff09;&#xff0c; 为Java应用程序开发…