相关文章推荐
乐观的皮带  ·  .Net Core ...·  8 月前    · 
逼格高的仙人掌  ·  qt - How to style the ...·  1 年前    · 

1,介绍


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

效果图如下:

image

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>





    
        <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>
        <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>
如何做一个俄罗斯方块5:形状碰撞检测(下)
其实,两侧的碰撞判断跟我们上一节讲过的向下移动的碰撞判断原理是一样的,向下碰撞检测的是每一个方块下方的位置是否有其它方块,那么向左/右碰撞检测的就是每个方块左/右侧的位置是否有其他的方块。
然后我就发了一条朋友圈,问这个像什么?? 有的说 云层, 有的说云墨,有的说雾霭, 其实都不是, 我想做的是烟雾。好的话不不多说!, 本篇文章阅读大概5分钟。不耽误大家太多时间,主要是介绍思路, 说太多也没啥意义。如果你对three.js 还没有一点了解都没有, R 可视乎 | 绘制卡通圣诞树
先和大家说一句圣诞快乐呀,最近 DIY 涂鸦圣诞树非常受欢迎,小编琢磨着能否用 R 语言来绘制一颗圣诞树呢,最后终于让小编找到了教程[1],这不赶紧在今天分享出来给大家,一起动手试一试吧~