如何基于Canvas来模拟真实雨景Part2:重力掉落和雨滴融合
在Part1中我们已经建立了绘制环境、背景和雨点对象。本文我们将接着讲述更为吸引人的动画部分。
重力掉落
我们先复习下自由落体的基本公式:
速度-时间公式:Δv=gΔt
位移-时间公式;h=gt^2/2
速度-位移公式:v^2=2gh
重力只作用于物体的y轴方向,一个处于自由落体状态的雨滴,不计空气阻力和风力的时候,其运动将符合上面的规律。
力作用于物体上时都会产生加速度(重力产生重力加速度g=9.8),因此我们需要给雨滴y轴的速度分量(vy)添加一个时间增量。
我们假定每秒帧数(fps)为30,那么每次刷新的时间间隔为1/30秒,乘以g,速度增量约为0.33的样子。但是,雨滴在窗户上的情况要复杂得多,因为其还受到窗户的阻力,下落的形式可能会包含滚动和滑动,其摩擦力的计算方法也不相同,另外窗户上的灰尘导致各个点的阻力并不完全相同,总体上我们可以认为越小的雨滴受窗户的影响越大(因其接触面积相对占比大更容易被吸附住),因此我们还需要给vy添加一个和雨滴半径成反比的随机摩擦系数,这样就造成一个时快时慢乃至有些雨点会停留在窗户上的效果。
在有风的环境中(通常是有的),除了y方向的速度,我们还要考虑x方向的速度,总的速度为:
v = Math.sqrt(vx*vx, vy*vy);
当然在画布上,我们还是按照x,y轴分开来计算偏移的。
RainyView.prototype.gravity = function(drop) { this.g = 9.8; this.fps = 30; var r_base = 3; if (drop.yspeed) { friction = this.base_friction + Math.ceil(Math.random() * this.g) / (Math.floor(drop.r) - r_base + 1); drop.yspeed += Math.floor(this.g - friction)/this.fps; if(drop.yspeed < 0) drop.yspeed = 0; drop.xspeed += Math.floor(drop.r); } else { drop.yspeed = 0.01; //给定一个启动速度 drop.xspeed = 0; } drop.y += drop.yspeed; drop.draw(); return false; };
你还可以给上面代码中的速度增量乘上一个系数来调整实际的动画效果,不需要过度模拟真实情况。
碰撞融合
要检测粒子(雨滴)之间的碰撞情况,数学上来讲就是计算两个雨滴中心点的距离,如果小于等于半径之和,则两个水滴已经碰在了一起,将开始融合。但是我们不能遍历所有的粒子,这样的计算效率是很差的。我们实际上只需要计算相邻粒子之间的距离,为此一个常用的方法是建立和画布对应的相邻矩阵,每个粒子占据矩阵中的一个位置,其相邻粒子最多有4个。我们每次移动雨滴粒子的时候,计算一下和相邻粒子的距离,决定是否进行融合。
RainyView.prototype.collision = function(drop, collisions) { var item = collisions; var drop2; // 计算矩阵(链表结构)中相邻粒子的距离,取出第一个需要融合的雨滴粒子 while (item != null) { var p = item.drop; var radiusSum = drop.r + p.r; var dx = drop.x - p.x; var dy = drop.y - p.y; if(Math.abs(dx) < radiusSum) { if(Math.abs(dy) < radiusSum) { if (Math.sqrt(Math.pow(drop.x - p.x, 2) + Math.pow(drop.y - p.y, 2)) < (drop.r + p.r)) { drop2 = p; break; } } } item = item.next; } if (!drop2) { return; } // 移除高一点的雨滴,融合到低一点的雨滴中 var higher, lower; if (drop.y > drop2.y) { higher = drop; lower = drop2; } else { higher = drop2; lower = drop; } this.clearDrop(lower); // force stopping the second drop this.clearDrop(higher, true); this.matrix.remove(higher); lower.r = Math.sqrt(higher.r*higher.r + lower.r*lower.r);//合并雨滴 lower.draw(); lower.collided = true; };
解决了重力掉落和碰撞检测后,我们需要实现一个“下雨”的动画主函数,这个函数用来驱动每个雨滴的运动:
RainyView.prototype.animate = function() { var dropsClone = this.drops.slice(); var newDrops = []; for (var i = 0; i < dropsClone.length; ++i) { if (dropsClone[i].move()) {//该函数定义参见Part1 newDrops.push(dropsClone[i]); } } this.drops = newDrops; window.requestAnimFrame(this.animateDrops.bind(this)); };
这样基本上就完成了整个的下雨动画,为了更加逼真,你还可以添加雨滴的滑落轨迹(水渍效果)和雨滴的反射效果(参考CSS3泡沫一文中的方法),还有一些实现细节,这里不做描述。
最终的效果看起来会是下面这样:
你可以自己在线试试。
后续我们计划讲解如何使用WebGL来实现3D版本的雨滴效果。


最新评论
- 相关文章
CentOS6 Apache2.2多站点HTTPS配置
可以使用letsencrypt(certbot)免费证书服务。支持多系统、多站点和多目录,支持wildcard(通配符域名),90天生效,可用定时任务自动更新。需要注意一点的是apache2.4以下版本需要在默认的ssl配置中添加如下的指令:NameVirtualHost
WebGL场景中多相机拍摄的原理和意义
一般而言,3D场景的渲染只需要一个相机,不过借助多相机可以获取一些单相机无法达到的特效。比如突显特定对象并模糊背景。
3D相机渲染的基本原理是依靠颜色...CSS3特性查询(Feature Query: @supports)功能简介
这是2017年不能不了解和学习的一个CSS新特性,非常实用,考虑到现实世界浏览器的复杂性,该特性本应该先于其他新特性出来。我们已经知道使用媒体查询(Media Que...
CSS3弹性布局内容对齐(justify-content)属性使用详解
内容对齐(justify-content)属性应用在弹性容器上,把弹性项沿着弹性容器的主轴线(main axis)对齐。该操作发生在弹性长度以及自动边距被确定后。 它用来在存...
CSS3弹性布局弹性流(flex-flow)属性详解和实例
弹性布局是CSS3引入的强大的布局方式,用来替代以前Web开发人员使用的一些复杂而易错hacks方法(如使用float进行类似流式布局)。其中flex-flow是flex-direction...
HTML5动画背后的数学2 - 仿生智能算法综述
计算WebGL中的uniforms变量使用数
在使用Three.js为人体模型加载皮肤材料时,启用了skinning:true的参数。有时候会导致GL编译错误,提示“too many uniforms”。下面的文章有助于理解错误原因和检...
Three.js 对象局部坐标转换为世界坐标
在Three.js中进行顶点几何计算时,一个需要注意的地方是,需要统一坐标系。比如你通过Three.js提供的API创建了一个球体网孔对象,那么默认情况下,各网孔顶点的...
纹理基础知识和过滤模式详解
1、 为什么在纹理采样时需要texture filter(纹理过滤)。
我们的纹理是要贴到三维图形表面的,而三维图形上的pixel中心和纹理上的texel中心并不一至(pixe...WebGL 纹理映射模式以及WRAP_S | WRAP_T参数详解
我们在纹理滤镜一文中已经说明了2个重要的纹理参数,用来定义对象缩放时纹理的处理方式:GL_TEXTURE_MIN_FILTERGL_TEXTURE_MAG_FILTER本文讲解其余几个纹理参数...
如何使用Three.js加载obj和mtl文件
OBJ和MTL是3D模型的几何模型文件和材料文件。在最新的three.js版本(r78)中,以前的OBJMTLLoader类已废弃。现在要加载OBJ和MTL文件,需要结合OBJLoader和MTLLoade...
WebGL入门教程5 - 详解纹理滤镜(Texture Filter)
WebGL中使用纹理贴图来实现细腻的物体表面观感,其中一个重要的参数是纹理滤镜(Texture Filter)。
这个参数用来处理当对象出现缩放时,纹理如何处理中间...浏览器控制台报JS脚本执行错误:Module is not defined
现在JS分成了两个分支,一部分在服务器端发展如NodeJS,一部分是传统的浏览器运行环境。
有些插件在编写JS代码时,是针对Node编写的,所以直接在浏览器中使...Processing.js和P5.js的功能简介和区别
什么是ProcessingProcessing是关于数字艺术的编程语言,支持跨平台,语言本身是一个类Java语言,程序文件的后缀为.pde。
什么是Processing.js为了能让Proce...SVG过滤器feColorMatrix矩阵变换效果用法详解
在计算机图形学(数学)中,矩阵乘法可用于把空间向量进行几何变换。我们可以把颜色的值(RGBA)表示成一个四维空间向量:color = (r, g, b, a);那么就可以应用...
更多...