基于精灵(Sprite)管道烟雾流动效果

您所在的位置:网站首页 opennms源码 基于精灵(Sprite)管道烟雾流动效果

基于精灵(Sprite)管道烟雾流动效果

2023-01-21 06:40| 来源: 网络整理| 查看: 265

效果

 

概念

首先要明白threejs中精灵的概念,官网中对精灵的描述是:

A sprite is a plane that always faces towards the camera, generally with a partially transparent texture applied. Sprites do not cast shadows, setting will have no effect.

详情参见:THREEJS官方文档-精灵

就是说,精灵是一种始终朝向相机,通常会使用部分透明的文理作为材质。并且不支持投阴影。

那么我们就可以通过细小的烟雾图片创建精灵,然后通过某种规律,批量创建精灵,组合后,就会构成烟雾的效果。比如下图:

原理

        一般来说,管道的横截面是个圆形,那么我们就可以基于半径为R横截面圆,在圆内随机生成N个三维坐标,N越大,烟雾越浓,R越大,烟雾越粗。然后基于这N个坐标创建N个精灵,然后将这N个精灵放入一个Group,为一个组。如果不是圆形,比如正方形,矩形等,也可以通过计算,在这个形状范围内随机生成坐标。

        在一个圆内生成三维坐标公式:y = r * cos(a),z = r * sin(a),其中r是随机半径(r { if (t.v === 1) { const smoke1 = getSmokeGroup(sprite, controls.smokeRadius) ts.scene.add(smoke1) smoke1.position.set(-1000, 100, 0) animation1(smoke1).start() const smoke2 = getSmokeGroup(sprite, controls.smokeRadius) ts.scene.add(smoke2) animation2(smoke2, points).start() } } }).start() } /** * 初始化场景 * @return {TS} */ function initScene () { // 创建场景封装对象(场景、光源等于本例无关的内容) const ts = new TS('container') ts.scene.remove(ts.helpers.gridHelper) ts.helpers.axesHelper.visible = true ts.helpers.gridHelper = new THREE.GridHelper(5000, 50, '#B6B6B6', '#B6B6B6') ts.scene.add(ts.helpers.gridHelper) ts.lights.spotLight.angle = 0.8 ts.lights.spotLight.helper.update() return ts } /** * 初始化圆柱体,用于直管道 * @return {Mesh} */ function initCylinder () { const cylinderGeometry = new THREE.CylinderGeometry(100, 100, 2100, 100, 100) const cylinderMaterial = new THREE.MeshPhongMaterial({ color: '#474747', transparent: true, opacity: 0.2, side: 1 }) const cylinder = new THREE.Mesh(cylinderGeometry, cylinderMaterial) cylinder.position.y = 100 cylinder.rotation.z = Math.PI / 2 return cylinder } /** * 初始化管道,用于弯曲管道 * @param points Curve曲线 * @return {Mesh} */ function initCube (points) { // 管道几何体 const tubeGeometry = new THREE.TubeGeometry(points, 100, 100, 100, false, 0) // 管道材质 const tubeMaterial = new THREE.MeshPhongMaterial({ color: new THREE.Color('#474747'), transparent: true, opacity: 0.2, side: 1 }) const tube = new THREE.Mesh(tubeGeometry, tubeMaterial) tube.geometry.computeBoundingBox() const center = new THREE.Vector3() tube.geometry.boundingBox.getCenter(center) // 中心锚点 tube.geometry.center() console.log(center) // 重新按照中心点设置坐标 tube.position.set(center.x, center.y, center.z) return tube } /** * 初始化烟雾精灵 * 场景中所有的精灵都与此进行克隆,减少内存消耗 * @return {Sprite} */ function initSmokeSprite () { // 加载烟雾贴图 const map = new THREE.TextureLoader().load('img/smoke.png') // 创建精灵材质 const material = new THREE.SpriteMaterial({ map: map, color: controls.smokeColor, transparent: true, opacity: controls.smokeOpacity }) // 创建精灵对象 return new THREE.Sprite(material) } /** * 直管烟雾动画 * @param group * @returns {*} */ function animation1 (group) { const anim = AnimUtil.animation({ from: { x: -1000 }, to: { x: 1000 }, duration: controls.smokeSpeed, onUpdate: (t) => { group.position.x = t.x }, onComplete: () => { AnimUtil.remove(anim) group.traverse(child => { if (child instanceof THREE.Mesh) { child.material.dispose() child.geometry.dispose() child.removeFromParent() } }) group.removeFromParent() }, onStart: () => { group.visible = true } }) return anim } /** * 弯管烟雾动画 * @param group * @param curve * @returns {*} */ function animation2 (group, curve) { const anim = AnimUtil.animation({ from: { v: 0 }, to: { v: 1 }, duration: controls.smokeSpeed, onUpdate: (t) => { const point = new THREE.Vector3() curve.getPointAt(t.v, point) group.position.copy(point) }, onComplete: () => { AnimUtil.remove(anim) group.traverse(child => { if (child instanceof THREE.Mesh) { child.material.dispose() child.geometry.dispose() child.removeFromParent() } }) group.removeFromParent() }, onStart: () => { group.visible = true } }) return anim } /** * 生成烟雾组 * @param s 精灵对象 * @param o 圆心 * @param r 半径 * @returns {Group} */ function getSmokeGroup (s, r) { const group = new THREE.Group() for (let i = 0; i < 5; i++) { const sprite = s.clone() const p = randomVector3ByRadius(r) sprite.position.copy(p) const sc = r / (5 * 10) sprite.scale.set(100 * sc, 100 * sc, 100 * sc) group.add(sprite) } group.renderOrder = 1 group.visible = false return group } /** * 根据半径和圆心,在圆内随机生成三维坐标 * @param r 半径越大,烟雾越粗 * @param o 圆心 * @return {Vector3} */ function randomVector3ByRadius (r) { const a = random(-360, 360) const radius = random(0, r) const y = radius * Math.cos(a) const z = radius * Math.sin(a) return new THREE.Vector3(0, y, z) }

AnimUtil和TS对象为我自己封装的对象,方便调用,与本文技术没有太多关系。



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3