1,介绍
该示例使用的是 r95版本Three.js库。主要实现功能:引用模型进行工厂布局展示并贴图,使用粒子实现天气和火焰效果。效果图如下:
2,主要说明
下雨下雪火焰说明可以查看上一篇文章。
工厂布局主要使用OBJLoader加载模型,使用TextureLoader加载纹理,使用MeshBasicMaterial材质进行渲染,并设置展示的位置。
创建厂房和大小圆仓方式差不多,创建厂房代码如下:
// 创建房屋 function createBoxGeometryBasicMaterial() { var objLoader = new THREE.OBJLoader(); objLoader.load('assets/textures/particles/003.obj', function(obj) { var mesh = obj.children[0]; mesh.material = new THREE.MeshBasicMaterial({ map: new THREE.TextureLoader().load('assets/textures/particles/003.png'), }); mesh.scale.set(1.3, 1.4, 1.5); mesh.position.set(11, 0, -85); for (let i = 0; i < 2; i++) { for (let j = 0; j < 3; j++) { var mc = mesh.clone(); mc.translateX(i * 52); mc.translateZ(j * 83); scene.add(mc); } } }); }
3,源码
<!DOCTYPE html> <html> <head> <title>物联网,工厂3D可视化系统</title> <script type="text/javascript" src="libs/three.js"></script> <script type="text/javascript" src="libs/OrbitControls.js"></script> <script type="text/javascript" src="libs/OBJLoader.js"></script> <script type="text/javascript" src="libs/other/Tween.min.js"></script> <script type="text/javascript" src="libs/dat.gui.js"></script> <style> body { margin: 0; overflow: hidden; } </style> </head> <body> <div id="dom"></div> <script type="text/javascript"> var camera; var renderer; var cloud; var rainy_sw = 3; // 1雨2雪3晴4阴 var flame_sw = true; //初始化一个空容器,装载粒子 var krq = new THREE.Object3D(); var textureLoader = new THREE.TextureLoader(); var group3 = new THREE.Group(); function init() { // 创建一个场景,它将包含我们所有的元素,如物体,相机和灯光。 var scene = new THREE.Scene(); var urls = [ 'assets/textures/cubemap/flowers/posx.jpg', 'assets/textures/cubemap/flowers/negx.jpg', 'assets/textures/cubemap/flowers/posy.jpg', 'assets/textures/cubemap/flowers/negy.jpg', 'assets/textures/cubemap/flowers/posz.jpg', 'assets/textures/cubemap/flowers/negz.jpg' ]; var urls1 = [ 'assets/textures/cubemap/flowers/posx1.jpg', 'assets/textures/cubemap/flowers/negx1.jpg', 'assets/textures/cubemap/flowers/posy1.jpg', 'assets/textures/cubemap/flowers/negy1.jpg', 'assets/textures/cubemap/flowers/posz1.jpg', 'assets/textures/cubemap/flowers/negz1.jpg' ]; var cubeLoader = new THREE.CubeTextureLoader(); scene.background = cubeLoader.load(urls); // 创建一个摄像机,它定义了我们正在看的地方 camera = new THREE.PerspectiveCamera(65, window.innerWidth / window.innerHeight, 0.1, 20000); // 将摄像机对准场景的中心 camera.position.x = 180; camera.position.y = 80; camera.position.z = 90; camera.lookAt(scene.position); var orbit = new THREE.OrbitControls(camera); // 创建一个渲染器并设置大小,WebGLRenderer将会使用电脑显卡来渲染场景 renderer = new THREE.WebGLRenderer({ antialias: true, logarithmicDepthBuffer: true, }); renderer.setClearColor(new THREE.Color(0x121A39)); renderer.setSize(window.innerWidth, window.innerHeight); var alight = new THREE.AmbientLight("#ffffff", 1); alight.name = "aLight"; scene.add(alight); // 在屏幕上显示坐标轴 var axes = new THREE.AxesHelper(100); // scene.add(axes); // 将平面添加到场景中 createPlaneGeometryBasicMaterial(); // 立方体 createBoxGeometryBasicMaterial(); creatRoadSurface(); createRoundGeometryBasicMaterialMax(); createRoundGeometryBasicMaterialMin(); crateWall(); // 将呈现器的输出添加到HTML元素 document.getElementById("dom").appendChild(renderer.domElement); // 使用GUI调试库 var controls = new function() { this.rainy = function() { scene.remove(scene.getObjectByName("particles_snowy")); if (rainy_sw != 1) { rainy_sw = 1; scene.background = cubeLoader.load(urls1); scene.getObjectByName("aLight").intensity = 0.6; createPointRainy(); } } this.snowy = function() { scene.remove(scene.getObjectByName("particles_rainy")); if (rainy_sw != 2) { rainy_sw = 2; scene.background = cubeLoader.load(urls1); scene.getObjectByName("aLight").intensity = 2; createPointRainy(); } } this.sunny = function() { if (rainy_sw != 3) { scene.remove(scene.getObjectByName("particles_rainy")); scene.remove(scene.getObjectByName("particles_snowy")); scene.background = cubeLoader.load(urls); scene.getObjectByName("aLight").intensity = 1.2; rainy_sw = 3; } } this.cloudy = function() { if (rainy_sw != 4) { scene.remove(scene.getObjectByName("particles_rainy")); scene.remove(scene.getObjectByName("particles_snowy")); scene.background = cubeLoader.load(urls1); scene.getObjectByName("aLight").intensity = 1; rainy_sw = 4; } } this.flame = function() { if (flame_sw) { initFlame(); flame_sw = !flame_sw; } } } var gui = new dat.GUI(); gui.add(controls, 'rainy'); // 雨 gui.add(controls, 'snowy'); // 雪 gui.add(controls, 'sunny'); // 晴 gui.add(controls, 'cloudy'); // 阴 gui.add(controls, 'flame'); // 火焰 // 启动动画 renderScene(); function createPointRainy() { var img = rainy_sw == 1 ? "raindrop-4.png" : rainy_sw == 2 ? "snowflake3.png" : ""; var name = rainy_sw == 1 ? "particles_rainy" : rainy_sw == 2 ? "particles_snowy" : ""; var texture = new THREE.TextureLoader().load("assets/textures/particles/" + img); var geom = new THREE.Geometry(); var material = new THREE.PointsMaterial({ size: 1.5, transparent: true, // 是否设置透明度 opacity: 1, // 透明 map: texture, // 粒子材质 blending: THREE.AdditiveBlending, sizeAttenuation: true, // 是否相同尺寸 color: 0xffffff }); var range = 350; for (var i = 0; i < 3500; i++) { var particle = new THREE.Vector3( Math.random() * range - range / 2, Math.random() * range * 1.5, 1 + (i / 10 - 180) ) if (rainy_sw == 2) { // 定义雨滴以多快的速度落下,纵向运动速度的范围是0.1~0.3 particle.velocityY = (0.1 + Math.random() / 5) - 0.1; // 定义粒子(雨滴)如何水平移动,横向运动速度的范围是-0.16~+0.16 particle.velocityX = ((Math.random() - 0.5) / 3) - 0.05; } else { particle.velocityY = 0.15 + Math.random() / 5; particle.velocityX = (Math.random() - 0.5) / 3; } geom.vertices.push(particle); } cloud = new THREE.Points(geom, material); cloud.sortParticles = true; cloud.name = name; scene.add(cloud); } // 添加火焰 function initFlame() { var texture = new THREE.TextureLoader().load("assets/textures/particles/flamex.png"); //sprite材质 var material = new THREE.SpriteMaterial({ //以canvas作为纹理 map: texture, //混合度 加法混合 blending: THREE.AdditiveBlending }); //循环1000 添加粒子 for (var i = 0; i < 2000; i++) { var particle = new THREE.Sprite(material); initParticle(particle, i); krq.add(particle); krq.name = "particles_flame"; } scene.add(krq); } /** * 粒子 延迟发散 * @param particle * @param delay */ function initParticle(particle, delay) { particle.position.set(0, Math.random() + 12, 0); particle.scale.x = particle.scale.y = Math.random() * 13; //下面是一系列的动画 var xx = Math.random() * 40 - 20; var yy = Math.cos((Math.PI / 100) * xx) * 80; //位移 new TWEEN.Tween(particle.position) .delay(delay) .to({ x: xx, y: yy, z: Math.random() * 40 - 20 }, 2000) .onComplete(function() { initParticle(particle, delay); }) .start(); // 大小 new TWEEN.Tween(particle.scale) .delay(delay) .to({ x: 0.01, y: 0.01 }, 1000) .start(); } // 创建圆仓大 function createRoundGeometryBasicMaterialMax() { var objLoader = new THREE.OBJLoader(); objLoader.load('assets/textures/particles/gong001.obj', function(obj) { var mesh = obj.children[0]; // mesh.rotateZ(Math.PI); mesh.material = new THREE.MeshBasicMaterial({ map: new THREE.TextureLoader().load('assets/textures/particles/d001.png'), transparent: true, side: THREE.DoubleSide, clipIntersection: true, }); mesh.rotateZ(Math.PI); mesh.position.set(-40, 36, -105); for (let i = 0; i < 2; i++) { for (let j = 0; j < 3; j++) { var mc = mesh.clone(); mc.translateX(i * 28); mc.translateZ(j * 20); scene.add(mc); } } }); } // 创建圆仓小 function createRoundGeometryBasicMaterialMin() { var objLoader = new THREE.OBJLoader(); objLoader.load('assets/textures/particles/002.obj', function(obj) { var mesh = obj.children[0]; mesh.material = new THREE.MeshBasicMaterial({ map: new THREE.TextureLoader().load('assets/textures/particles/002.png'), transparent: true, side: THREE.DoubleSide, clipIntersection: true, }); mesh.rotateZ(Math.PI); mesh.position.set(-40, 20, -19); for (let i = 0; i < 2; i++) { for (let j = 0; j < 6; j++) { var mc = mesh.clone(); mc.translateX(i * 28); mc.translateZ(j * 24); scene.add(mc); } } }); } // 创建围栏 function crateWall() { var objLoader = new THREE.OBJLoader(); objLoader.load('assets/textures/particles/wall.obj', function(obj) { obj.scale.set(0.98, 0.6, 1); var texLan = new THREE.TextureLoader().load('assets/textures/particles/lan2.png'); // 纹理重复 texLan.wrapS = THREE.RepeatWrapping; texLan.wrapT = THREE.RepeatWrapping; texLan.repeat.set(40, 1); obj.children[0].material = new THREE.MeshBasicMaterial({ side: THREE.DoubleSide, map: texLan, transparent: true, }); obj.children[1].material = new THREE.MeshBasicMaterial({ map: new THREE.TextureLoader().load('assets/textures/particles/door.png'), side: THREE.DoubleSide, transparent: true, }); scene.add(obj) }); } // 创建房屋 function createBoxGeometryBasicMaterial() { var objLoader = new THREE.OBJLoader(); objLoader.load('assets/textures/particles/003.obj', function(obj) { var mesh = obj.children[0]; mesh.material = new THREE.MeshBasicMaterial({ map: new THREE.TextureLoader().load('assets/textures/particles/003.png'), }); mesh.scale.set(1.3, 1.4, 1.5); mesh.position.set(11, 0, -85); for (let i = 0; i < 2; i++) { for (let j = 0; j < 3; j++) { var mc = mesh.clone(); mc.translateX(i * 52); mc.translateZ(j * 83); scene.add(mc); } } }); } /** * 创建地面并添加材质 * wrapS属性定义的是纹理沿x轴方向的行为,而warpT属性定义的是纹理沿y轴方向的行为。 * Three.js为这些属性提供了如下两个选项: * ·THREE.RepeatWrapping允许纹理重复自己。 * ·THREE.ClampToEdgeWrapping是属性的默认值。 * 属性值为THREE.ClampToEdgeWrapping时,那么纹理的整体不会重复,只会重复纹理边缘的像素来填满剩下的空间。 */ function createPlaneGeometryBasicMaterial() { var cubeMaterial = new THREE.MeshStandardMaterial({ map: textureLoader.load("assets/textures/particles/floor3.png"), transparent: true, side: THREE.DoubleSide, }); // 创建地平面并设置大小 var planeGeometry = new THREE.PlaneGeometry(270, 260); var plane = new THREE.Mesh(planeGeometry, cubeMaterial); // 设置平面位置并旋转 plane.rotation.x = -0.5 * Math.PI; plane.position.y = -0.1; scene.add(plane); } function creatRoadSurface() { var textureLoader = new THREE.TextureLoader(); var geometry = new THREE.PlaneGeometry(24, 280); var texture = textureLoader.load('assets/textures/particles/road2.png'); texture.wrapS = THREE.RepeatWrapping; texture.wrapT = THREE.RepeatWrapping; texture.repeat.set(1, 10); var material = new THREE.MeshBasicMaterial({ map: texture, side: THREE.DoubleSide, }); var mesh = new THREE.Mesh(geometry, material); scene.add(mesh); mesh.rotateX(-Math.PI / 2); mesh.position.x = 105.5 } function renderScene() { orbit.update(); // 拖动 TWEEN.update(); if (cloud) { var vertices = cloud.geometry.vertices; vertices.forEach(function(v) { v.y = v.y - (v.velocityY); v.x = v.x - (v.velocityX); if (v.y <= 0) v.y = 60; if (v.x <= -20 || v.x >= 20) v.velocityX = v.velocityX * -0.8; }); cloud.geometry.verticesNeedUpdate = true; } // 使用requestAnimationFrame函数进行渲染 requestAnimationFrame(renderScene); renderer.render(scene, camera); } // 渲染的场景 renderer.render(scene, camera); } window.onload = init; // 随着窗体的变化修改场景 function onResize() { camera.aspect = window.innerWidth / window.innerHeight; camera.updateProjectionMatrix(); renderer.setSize(window.innerWidth, window.innerHeight); } // 监听窗体调整大小事件 window.addEventListener('resize', onResize, false); </script> </body> </html>
4,源码和模型
需要完整代码、模型或者其他源码,请进入博客首页查看其他文章或者留言