Threejs实现下雨,下雪,阴天,晴天,火焰

简介: Threejs实现下雨,下雪,阴天,晴天,火焰

1,介绍


该示例使用的是 r95版本Three.js库。使用Threejs粒子效果实现下雨,下雪,阴天,晴天,火焰。

效果图如下:

2,主要说明


阴天,晴天实现方式,主要是替换场景的背景图片实现。

下雨、下雪、火焰主要用粒子实现,创建粒子有两种方式:

1,使用THREE.Points创建一个粒子集合,使用THREE.PointsMaterial来样式化粒子。

2,使用Three.Sprite()构造函数手工创建粒子。我们传入的唯一参数是材质,它只能是THREE.SpriteMaterial或THREE.SpriteCanvasMaterial。

Sprite适用创建少量的粒子,如果想创建大量粒子应该使用Points。


我这里使用Points实现下雨和下雪,主要代码如下:


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 = 120;
  for (var i = 0; i < 1500; i++) {
    var particle = new THREE.Vector3(
      Math.random() * range - range / 2,
      Math.random() * range * 1.5,
      1 + (i / 10 - 80)
    )
    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);
}


使用Sprite实现火焰效果,主要代码如下:


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);
}


3,源码


<!DOCTYPE html>
<html>
  <head>
    <title>Threejs实现下雨,下雪,阴天,晴天,火焰</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();
      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(45, window.innerWidth / window.innerHeight, 0.1, 1000);
        // 将摄像机对准场景的中心
        camera.position.x = 10;
        camera.position.y = 50;
        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();
        // 将呈现器的输出添加到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 = 120;
          for (var i = 0; i < 1500; i++) {
            var particle = new THREE.Vector3(
              Math.random() * range - range / 2,
              Math.random() * range * 1.5,
              1 + (i / 10 - 80)
            )
            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(5, Math.random() + 5, 0);
          particle.scale.x = particle.scale.y = Math.random() * 3;
          //下面是一系列的动画
          var xx = Math.random() * 10 - 5;
          var yy = Math.cos((Math.PI / 100) * xx) * 20;
          //位移
          new TWEEN.Tween(particle.position)
            .delay(delay)
            .to({
              x: xx,
              y: yy,
              z: Math.random() * 10 - 5
            }, 2000)
            .onComplete(function() {
              initParticle(particle, delay);
            })
            .start();
          // 大小
          new TWEEN.Tween(particle.scale)
            .delay(delay)
            .to({
              x: 0.01,
              y: 0.01
            }, 1000)
            .start();
        }
        /**
         * 创建地面并添加材质
         * wrapS属性定义的是纹理沿x轴方向的行为,而warpT属性定义的是纹理沿y轴方向的行为。
         * Three.js为这些属性提供了如下两个选项:
         * ·THREE.RepeatWrapping允许纹理重复自己。
         * ·THREE.ClampToEdgeWrapping是属性的默认值。
         * 属性值为THREE.ClampToEdgeWrapping时,那么纹理的整体不会重复,只会重复纹理边缘的像素来填满剩下的空间。
         */
        function createPlaneGeometryBasicMaterial() {
          var cubeMaterial = new THREE.MeshStandardMaterial({
            map: textureLoader.load("assets/textures/stone/cd.jpg"),
            side: THREE.DoubleSide,
          });
          cubeMaterial.map.wrapS = THREE.RepeatWrapping;
          cubeMaterial.map.wrapT = THREE.RepeatWrapping;
          cubeMaterial.map.repeat.set(3, 3)
          // 创建地平面并设置大小
          var planeGeometry = new THREE.PlaneGeometry(100, 100);
          var plane = new THREE.Mesh(planeGeometry, cubeMaterial);
          // 设置平面位置并旋转
          plane.rotation.x = -0.5 * Math.PI;
          plane.position.x = 0;
          plane.position.z = -5;
          plane.position.y = -5;
          scene.add(plane);
        }
        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 * -1;
            });
            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>
目录
相关文章
Threejs实现模拟河流,水面水流,水管水流,海面
Threejs实现模拟河流,水面水流,水管水流,海面
2442 0
Threejs实现模拟河流,水面水流,水管水流,海面
|
2月前
|
开发者
ThreeJs实现小球自由落体效果
这篇文章详细介绍了如何在Three.js中利用物理引擎Cannon.js实现小球自由落体效果,包括物理世界的创建、物体的添加及同步物理状态到三维场景中的具体实现。
55 3
ThreeJs实现小球自由落体效果
|
2月前
threeJs用精灵模型Sprite实现下雨效果
这篇文章介绍了使用Three.js中的Sprite(精灵)模型来实现下雨特效的方法和技术细节。
111 2
threeJs用精灵模型Sprite实现下雨效果
|
7月前
超简单的html+css魔幻霓虹灯文字特效
超简单的html+css魔幻霓虹灯文字特效
63 3
超简单的html+css魔幻霓虹灯文字特效
|
7月前
如何用SCSS制作小铃铛振动/震动/摇晃/晃动的特效/效果?
如何用SCSS制作小铃铛振动/震动/摇晃/晃动的特效/效果?
wustojc2012自由落体
wustojc2012自由落体
35 0
|
Python
【兔年烟花】旖旎风景——浪漫烟花(Python实现)
【兔年烟花】旖旎风景——浪漫烟花(Python实现)
146 0
|
前端开发 容器
「CSS畅想」七夕寄情,我绘制了一副双色莲花图
用技术实现梦想,用梦想打开创意之门。七夕寄情,我用CSS绘制了一副双色莲花图。
202 1
「CSS畅想」七夕寄情,我绘制了一副双色莲花图
|
存储 程序员
七夕快到了,用SwiftUI做一个表达爱意的心形动画
传统的七夕快到了,作为一个程序猿,最浪漫的礼物当然是自己写的啦! 思来想去也不知道写什么好,在某天在某音上学习时看到点赞的动画效果还不错,那不如就做一个表达爱意的动画吧。
363 0
七夕快到了,用SwiftUI做一个表达爱意的心形动画
|
前端开发
2、CSS动画之行走的米兔、奔跑的小人
2、CSS动画之行走的米兔、奔跑的小人
283 0