目录

功能描述

最终效果

准备工作

实现原理

参数配置

核心代码


功能描述

在基于THREE.js的场景中,实现雨雪天气效果,可设置下雨、雪的范围、位置、量级以及下落速度。

最终效果

准备工作

材质贴图,包含雨点、雪花、环境球贴图,其中环境球贴图可有可无,如不用环境球,直接忽略即可。

实现原理

雨和雪的原理是一样的,仅仅是材质贴图和下落速度不一样。

在某一个确定的空间范围内,随机生成N个三维坐标点。这个范围可以是球体,也可以是六面体,本例使用的是六面体。另外,N越大,雨雪的密度就越大,通过调整N,可以修改雨雪等级。

/*** 指定尺寸六面体内,随机生成三维向量* @param width* @param height* @param depth* @returns {THREE.Vector3}*/
export function randomVectorInBox (width, height, depth) {const x = random(-width / 2, width / 2)const y = random(-height / 2, height / 2)const z = random(-depth / 2, depth / 2)return new THREE.Vector3(x, y, z)
}// 向量坐标集合
const vectors = []// 循环生成向量
for (let i = 0; i < N; i++) {const v = randomVectorInBox(4000, 4000, 4000)vectors.push(v.x, v.y, v.z)
}      

创建一个BufferGeometry,将这N个坐标点以Float32BufferAttribute的形式,放入BufferGeometry的position属性中。

然后创建一个PointsMaterial材质,贴图就是雨或雪的贴图图片,这里的图片一定是png格式,否则你的雪花或者雨点会有棱有角非常难看。在设置下材质的透明度。

最后通过上述创建的几何体和材质对象,创建Points对象,添加到场景,这样我们就到了一个静止状态的雨雪效果。

const geometry = new THREE.BufferGeometry()
// 设置position
geometry.setAttribute('position', new THREE.Float32BufferAttribute(vectors, 3))// 加载材质贴图
const texture = new THREE.TextureLoader().load(img)// 创建材质
const materials = new THREE.PointsMaterial({size: size,map: texture,blending: THREE.AdditiveBlending,depthTest: true,transparent: true,opacity: 0.5
})// 创建Points粒子对象
const particle = new THREE.Points(geometry, materials)

但此时得到效果是静止的,我们需要让粒子下落来实现最终的效果,原理就是在requestAnimationFrame中,不断修改粒子的y轴坐标,实现下落,当然如果不想垂直下落,也可以同时修改其他坐标轴,来模拟风吹的效果,这里为了简单示意,就只修改y轴了。每次y轴的变化量V,决定了下落的速度。为了方便操作,我将上面生成的vectors集合,挂在了points对象上。下面是在requestAnimationFrame中每一帧刷新时执行的代码。

// 循环粒子坐标点
for (let i = 1; i < points.vectors.length; i += 3) {// 修改y轴坐标,每次变化V,注意是减法,否则就上升了points.vectors[i] = points.vectors[i] - V// 此处判断是为了让粒子下落到最低点后,重复从最高点继续下落,实现循环下落if (points.vectors[i] < -height / 2) {const v = randomVectorInBox(width, height, depth)particle.vectors[i] = height / 2particle.vectors[i - 1] = v.xparticle.vectors[i + 1] = v.z}
}// 更新几何体position
points.geometry.setAttribute('position', new THREE.Float32BufferAttribute(points.vectors, 3))

参数配置

修改V可以调整下落速度

修改六面体尺寸,可修改粒子范围

修改points的position,可以调整粒子整体在场景中的位置

修改N可以调整雨雪的量级

核心代码

demo是在vue上跑的,尽量把与本例无关的vue代码全部清除了。

// 创建场景封装对象(场景、光源等于本例无关的内容)
const ts = new TS('container')// UI控制参数对象
this.controls = {// 开启下雨rainVisible: false,// 雨滴四度rainSpeed: 10,// 雨量等级(密度)rainGrade: 10000,// 开启下雪snowVisible: false,// 雪花速度snowSpeed: 5,// 雪量等级(密度)snowGrade: 10000,// 覆盖范围-Xwidth: 4000,// 覆盖范围-Yheight: 4000,// 覆盖范围-Zdepth: 4000,// 雨坐标rx: 0,ry: 0,rz: 0,// 雪坐标sx: 0,sy: 0,sz: 0
}// 雨雪对象
this.rain = null
this.snow = null// GUI 方便调试
const gui = new GUI()// 覆盖范围
gui.add(this.controls, 'width', 1000, 10000).step(10).name('宽').onChange(v => {this.controls.width = v// 刷新几何体updateGeometry.call(this, 'rain')updateGeometry.call(this, 'snow')
})gui.add(this.controls, 'width', 1000, 10000).step(10).name('高').onChange(v => {this.controls.height = v// 刷新几何体updateGeometry.call(this, 'rain')updateGeometry.call(this, 'snow')
})gui.add(this.controls, 'depth', 1000, 10000).step(10).name('长').onChange(v => {this.controls.depth = v// 刷新几何体updateGeometry.call(this, 'rain')updateGeometry.call(this, 'snow')
})// 雨相关UI控制
const rainGroup = gui.addFolder('雨')rainGroup.add(this.controls, 'rainVisible', false).name('开启').onChange(v => {this.controls.rainVisible = v// 创建粒子对象,否则删除并销毁if (v) {this.rain = generateParticle.call(this, 'img/rain.png', 'snow', 10)ts.scene.add(this.rain)} else {this.rain.geometry.dispose()this.rain.material.dispose()this.rain.removeFromParent()this.rain = null}
})rainGroup.add(this.controls, 'rainSpeed', 5, 15).step(1).name('速度').onChange(v => {this.controls.rainSpeed = v
})rainGroup.add(this.controls, 'rainGrade', 100, 200000).step(100).name('量级').onChange(v => {this.controls.rainGrade = vif (!this.rain) returnupdateGeometry.call(this, 'rain')
})rainGroup.add(this.controls, 'rx').step(1).name('X坐标').onChange(v => {this.controls.rx = vif (this.rain) this.rain.position.x = v
})rainGroup.add(this.controls, 'ry').step(1).name('Y坐标').onChange(v => {this.controls.ry = vif (this.rain) this.rain.position.y = v
})rainGroup.add(this.controls, 'rz').step(1).name('Z坐标').onChange(v => {this.controls.rz = vif (this.rain) this.rain.position.z = v
})// 雪相关UI控制
const snowGroup = gui.addFolder('雪')snowGroup.add(this.controls, 'snowVisible', false).name('开启').onChange(v => {this.controls.snowVisible = vif (v) {this.snow = generateParticle.call(this, 'img/snow.png', 'snow')ts.scene.add(this.snow)} else {this.snow.geometry.dispose()this.snow.material.dispose()this.snow.removeFromParent()this.snow = null}
})snowGroup.add(this.controls, 'snowSpeed', 1, 10).step(1).name('速度').onChange(v => {this.controls.snowSpeed = v
})snowGroup.add(this.controls, 'snowGrade', 100, 200000).step(100).name('量级').onChange(v => {this.controls.snowGrade = vif (!this.snow) returnupdateGeometry.call(this, 'snow')
})snowGroup.add(this.controls, 'sx').step(1).name('X坐标').onChange(v => {this.controls.sx = vif (this.snow) this.snow.position.x = v
})snowGroup.add(this.controls, 'sy').step(1).name('Y坐标').onChange(v => {this.controls.sy = vif (this.snow) this.snow.position.y = v
})snowGroup.add(this.controls, 'sz').step(1).name('Z坐标').onChange(v => {this.controls.sz = vif (this.snow) this.snow.position.z = v
})// 此处相当于requestAnimationFrame,只不过内部封装了一下
ts.updates.push(() => {update.call(this, 'rain')update.call(this, 'snow')
})/*** 更新几何体* @param type*/
function updateGeometry (type) {// 获取粒子对象const particle = this[type]if (!particle) return// 重新计算粒子坐标const vectors = computeVectors.call(this, type)// 更新属性particle.vectors = vectorsparticle.geometry.setAttribute('position', new THREE.Float32BufferAttribute(particle.vectors, 3))
}/*** 更新粒子移动* @param particle* @param speed*/
function update (type) {// 获取粒子对象const particle = this[type]if (!particle || !particle.vectors) return// 获取速度const speed = this.controls[type + 'Speed']// 下落动画for (let i = 1; i < particle.vectors.length; i += 3) {particle.vectors[i] = particle.vectors[i] - speedif (particle.vectors[i] < -this.controls.height / 2) {const v = randomVectorInBox(this.controls.width, this.controls.height, this.controls.depth)particle.vectors[i] = this.controls.height / 2particle.vectors[i - 1] = v.xparticle.vectors[i + 1] = v.z}}particle.geometry.setAttribute('position', new THREE.Float32BufferAttribute(particle.vectors, 3))
}/*** 计算粒子坐标* @param type* @returns {*[]}*/
function computeVectors (type) {const vectors = []for (let i = 0; i < this.controls[type + 'Grade']; i++) {// 从六面体范围内获取随机坐标const v = randomVectorInBox(this.controls.width, this.controls.height, this.controls.depth)vectors.push(v.x, v.y, v.z)}return vectors
}/*** 雨雪粒子* @param img* @param size* @returns {any}*/
function generateParticle (img, type, size = 15) {const geometry = new THREE.BufferGeometry()// 获取粒子坐标点集合const vectors = computeVectors.call(this, type)// 设置到几何体geometry.setAttribute('position', new THREE.Float32BufferAttribute(vectors, 3))// 加载贴图const texture = new THREE.TextureLoader().load(img)// 创建材质对象const materials = new THREE.PointsMaterial({size: size,map: texture,blending: THREE.AdditiveBlending,// 是否永远置顶depthTest: true,transparent: true,opacity: 0.5})const particle = new THREE.Points(geometry, materials)particle.vectors = vectorsreturn particle
}/*** 指定尺寸六面体内,随机生成三维向量* @param width* @param height* @param depth* @returns {THREE.Vector3}*/
export function randomVectorInBox (width, height, depth) {const x = random(-width / 2, width / 2)const y = random(-height / 2, height / 2)const z = random(-depth / 2, depth / 2)return new THREE.Vector3(x, y, z)
}

基于粒子(Points)模拟雨雪天气效果相关推荐

  1. Cesium-通过Shader添加雨雪天气效果

    前言 实验效果 代码 自定义GLSL代码 外部调用 总结 参考链接 前言 作为一个三维地球,在场景中来点雨雪效果,貌似可以增加一点真实感.Cesium 官网 Demo 中有天气系统的实例,用的是 Ce ...

  2. 在Unity中实现基于粒子的水模拟(二:开始着色)

    在Unity中实现基于粒子的水模拟(二:开始着色) 文章目录 在Unity中实现基于粒子的水模拟(二:开始着色) 前言 一.生成顶点 二.偏移模拟 1.接收细分着色器输出的顶点 2.根据数据调用对应的 ...

  3. 在Unity中实现基于粒子的水模拟(三:混合屏幕)

    在Unity中实现基于粒子的水模拟(三:混合屏幕) 文章目录 在Unity中实现基于粒子的水模拟(三:混合屏幕) 前言 一.着色算法介绍 1.折射 2.反射 二.准备纹理 1.获取纹理 2.模糊纹理 ...

  4. Cesium雨雪雾天气效果

    Cesium雨雪雾天气效果 实现效果 关键代码 //雪着色器getSnow_fs() {return 'uniform sampler2D colorTexture;\n' +'varying vec ...

  5. 基于粒子群算法的组卷系统的研究与实现

    摘 要 组卷系统的主要任务是根据用户的需要用当前数据库中的试题组成一套符合用户需求的试卷.随着数据库与题量增大,传统采用随机选取和回朔试探法的组卷抽提算法因其抽题时间长,占用的空间复杂度太大,容易陷入 ...

  6. cesium-雨雪雾天气效果

    cesium-天气效果 cesium可以实现天气变化的效果,官方是使用cesium粒子系统做的效果. 但这种实现方式有问题:当视角变化(拉近.拉远.平移.旋转)时,粒子就会变化,甚至会消失. 我这里通 ...

  7. Cesium 系列3- SceneWeather添加雨雪天气场景

    最近项目中要实现一个根据天气状况自动添加雨雪天气场景的功能,先看官网demo,发现已经有示例Particle System Weaher cesium中显示雪的效果时雪的颗粒度比较大,而且切换视角后就 ...

  8. 鬼影、雨雪天气点云噪声处理

    雨雪天气点云噪声处理 在雨雪天气中,点云噪声可能来自以下几个方面: 雨滴.雪花等降水物体对激光雷达的反射造成的干扰: 湿度.温度等气象因素对激光雷达的影响: 环境中的遮挡物或反射体造成的多次反射或回波 ...

  9. 基于粒子群算法的神经网络非线性函数拟合

    基于粒子群算法的神经网络非线性函数拟合 文章初心 最近在学机器学习,自己的方向是智能算法,课程报告需要,于是试着把机器学习和粒子群算法相结合,写出来供大家参考,交流. 文末有这部分内容相关的代码,已开 ...

最新文章

  1. 物品回收平台java代码_java垃圾回收
  2. 解惑:如何使用html+css+js实现旋转相册,立方体相册等动画效果
  3. 取消IE不允许下载文件的提示
  4. 【转】图解phpstorm常用快捷键
  5. 怎么安装php模板,PHPWind八风格模板的安装及制作教程
  6. css flexbox模型_Flexbox-Ultimate CSS Flex速查表(带有动画图!)
  7. 三星S10+顶配版现身GeekBench:搭载Exynos 9820处理器
  8. kong使用mysql_Kong官方文档翻译:安装Kong
  9. 【10天基于STM32F401RET6智能锁项目实战第2天】(分别用库函数和寄存器点灯)
  10. xtrabackup mysql 5.1_mysql 5.1 选哪个 xtrabackup
  11. [Java] 蓝桥杯ADV-135 算法提高 三角形面积
  12. unity3d android自动打包,Unity自动化打包(二)
  13. 利用Python实现财务分析/经营分析自动化
  14. 上网账号口令怎么获取_如何获取自己路由器的上网账号和上网口令
  15. Simulink 快速入门(二)--创建简单模型
  16. 2020南京大学919经济学原理金融学学硕-上岸
  17. 服务器1U,2U的含义
  18. 【反演复习计划】【COGS2433】【bzoj3930,CQOI2015选数】爱蜜莉雅的冰魔法
  19. 看完这篇人工智能的文章,能帮你超越90%的人
  20. AUTOCAD2012安装失败解决方法

热门文章

  1. Mali GPU OpenGL ES 应用性能优化--基本概念
  2. Adobe Creative Suite 3 Design Premium 中文版下载
  3. 视觉优化-立体图片实现
  4. php文件直链源码,PHP萌心上传直链外链网盘源码
  5. Oracle 存储过程的调试
  6. Rosetta中文教程(二)
  7. linux下curses的用法
  8. 网络高手必备十款经典软件
  9. 听写单词好助手:从有道py一个单词下载器(绿色免安装)
  10. 不用光盘,不用4G优盘,教你用PE+NT6安装win7,让装机门槛降到最低,绝对实用!一楼已更新!