当前位置: 首页 > news >正文

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轴旋转,形成了一个简单的动画。本篇的内容结合了之前讲过的所有内容,是一个比较综合的例子,希望读者仔细体会,回见~

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • Mysql SqlServer 分页
  • 【算法】BFS—解开密码锁的最少次数
  • 简单说说MySQL中 SELECT 语句执行流程
  • 优化器与现有网络模型的修改
  • 软件编程随想
  • 内存dump文件分析
  • STM32--基于PWM的呼吸灯实验
  • 服务器断电重启后报XFS文件系统错误 XFS (dm-0)_ Metadata I_O error
  • 多线程之CompletableFuture
  • nodejs 011: nodejs事件驱动编程 EventEmitter 与 IPC
  • SLA 概念和计算方法
  • 智慧课堂学生行为数据集
  • AI预测福彩3D采取888=3策略+和值012路或胆码测试9月19日新模型预测第92弹
  • 基于深度学习的零售柜商品识别系统实战思路
  • Vue2篇
  • [ JavaScript ] 数据结构与算法 —— 链表
  • 「前端早读君006」移动开发必备:那些玩转H5的小技巧
  • Angular数据绑定机制
  • Docker: 容器互访的三种方式
  • ES6核心特性
  • If…else
  • Javascripit类型转换比较那点事儿,双等号(==)
  • JavaScript设计模式之工厂模式
  • learning koa2.x
  • MySQL常见的两种存储引擎:MyISAM与InnoDB的爱恨情仇
  • Python实现BT种子转化为磁力链接【实战】
  • SpringBoot几种定时任务的实现方式
  • SQLServer插入数据
  • 翻译 | 老司机带你秒懂内存管理 - 第一部(共三部)
  • 免费小说阅读小程序
  • 如何用vue打造一个移动端音乐播放器
  • 小程序开发中的那些坑
  • 再次简单明了总结flex布局,一看就懂...
  • AI算硅基生命吗,为什么?
  • 支付宝花15年解决的这个问题,顶得上做出十个支付宝 ...
  • ​【数据结构与算法】冒泡排序:简单易懂的排序算法解析
  • ​configparser --- 配置文件解析器​
  • # 飞书APP集成平台-数字化落地
  • #HarmonyOS:软件安装window和mac预览Hello World
  • #我与Java虚拟机的故事#连载15:完整阅读的第一本技术书籍
  • $$$$GB2312-80区位编码表$$$$
  • $(selector).each()和$.each()的区别
  • $.each()与$(selector).each()
  • (16)Reactor的测试——响应式Spring的道法术器
  • (2020)Java后端开发----(面试题和笔试题)
  • (2024最新)CentOS 7上在线安装MySQL 5.7|喂饭级教程
  • (27)4.8 习题课
  • (3)llvm ir转换过程
  • (补充)IDEA项目结构
  • (二)延时任务篇——通过redis的key监听,实现延迟任务实战
  • (二)正点原子I.MX6ULL u-boot移植
  • (附源码)spring boot车辆管理系统 毕业设计 031034
  • (回溯) LeetCode 131. 分割回文串
  • (强烈推荐)移动端音视频从零到上手(下)
  • (三)Kafka离线安装 - ZooKeeper开机自启