• 粒子和粒子系统


    1.粒子材质和粒子几何体

        创建粒子系统的点,可以存放在Geometry几何体中。使用Geometry的vertices保存点、colors保存点的颜色。但我们必须使用粒子的专用材质对象PointCloudMaterial(原名叫做ParticleBasicMaterial)设置材质。包含的属性如下列表:

        名称/描述

        color/PointCloud对象中所有粒子的颜色。如果vertexColors设置为true,而且也指定了几何体的colors属性,那么该属性被忽略

        map/可以在粒子上应用某种材质。例如可以让粒子看起来像雪花

        size/粒子大小

        sizeAttenuation/如果设置为false,所有粒子都有拥有相同的尺寸。如果为true,粒子的大小取决于离相机的远近

        vertexColors/如果设置为true,并且几何体的colors数组也有值,那就使用颜色数组中的值

        opacity/跟transparent属性一起使用,用来设置粒子的透明度

        transparent/如果为true,那么opacity设置有效

        blending/渲染粒子时的融合方式

        fog/是否采用场景的雾化效果

        把点vertex和颜色color都附加到Geometry对象中后,使用PointCloud(原ParticleSystem对象)创建几何粒子系统。如下面的例子:

    function createParticle(size, transparent, opacity, vertexColors, sizeAttenuation, color){
                    var geom = new THREE.Geometry();
    
                    var material = new THREE.PointCloudMaterial({
                        size: size,
                        transparent: transparent,
                        opacity: opacity,
                        vertexColors: vertexColors,
                        sizeAttenuation: sizeAttenuation,
                        color: color,
                    });
                    var range = 500;
                    for(var i = 0; i < 15000; i++){
                        var particle = new THREE.Vector3(Math.random() * range - range / 2, Math.random() * range - range / 2, Math.random() * range - range / 2);
                        geom.vertices.push(particle);
                        var color = new THREE.Color(0x00ff00);
                        color.setHSL(color.getHSL().h, color.getHSL().s, Math.random() * color.getHSL().l);
                        geom.colors.push(color);
                    }
                    cloud = new THREE.PointCloud(geom, material);
                    cloud.name = "particles";
                    scene.add(cloud);
    
                }

    2.使用Canvas输出结果作为粒子材质

        和1中的创建PointCloudMaterial相似,只是这里增加了一个属性map,减少了属性vertexColors(因为直接使用纹理而不是颜色)。新增了一个map属性,具体值是调用getTexture函数返回一个Texture对象。创建PointCloudMaterial代码如下:

    var material = new THREE.PointCloudMaterial({
                        size: size,
                        transparent: transparent,
                        opacity: opacity,
                        map: getTexture(),
                        sizeAttenuation: sizeAttenuation,
                        color: color,
                    });

        Texture对象可以直接接受一个canvas对象,把canvas的输出作为纹理。创建Texture代码如下:

    var getTexture = function () {
                var canvas = document.createElement('canvas');
                canvas.width = 32;
                canvas.height = 32;
    
                var ctx = canvas.getContext('2d');
                // the body
                ctx.translate(-81, -84);
    
                ctx.fillStyle = "orange";
               ...
                ctx.fill();
    
    
                var texture = new THREE.Texture(canvas);
                texture.needsUpdate = true;
                return texture;
            };

        需要设置纹理的needsUpdate为true。另外,在创建PointCloud对象时也要额外设置它的sortParticles属性为true。这样可以保证粒子在渲染之前沿着屏幕上的z轴排好序。

    3.使用外部图片作为粒子的材质

        和2相似,只不过这里我们在创建纹理时直接如下方式读取外部图片作为纹理:

    var texture = THREE.ImageUtils.loadTexture("../assets/textures/particles/raindrop-3.png");

        如果我们想模拟下雨的效果,可在创建vertices时,给每个位置指定x和y方向的移动速度。

    for(var i = 0; i < 1500; i++){
                        var particle = new THREE.Vector3(Math.random() * range - range / 2, Math.random() * range * 1.5, Math.random() * range - range / 2);
                        particle.velocityY = 0.1 + Math.random() / 5;
                        particle.velocityX = (Math.random() - 0.5) / 3;
                        geom.vertices.push(particle);
                        var color = new THREE.Color(0x00ff00);
                        color.setHSL(color.getHSL().h, color.getHSL().s, Math.random() * color.getHSL().l);
                        geom.colors.push(color);
                    }

        在渲染的时候,可以没渲染一次,vertices根据速度移动一次。渲染代码如下:

    if(controls.rotateSystem){
                        var vertices = cloud.geometry.vertices;
                        vertices.forEach(function(vector){
                            vector.y -= vector.velocityY;
                            vector.x += vector.velocityX;
    
                            if(vector.y <= 0) vector.y = 60;
                            if(vector.x <= -20 || vector.x >= 20) vector.x = -vector.x;
                        });
                    }

        另外,在创建PointCloudBasicMaterial时,可以指定depthWrite属性为false。该属性决定这个对象是否影响WebGL的深度缓存。设置为false,可以保证各个粒子系统之间不受影响。如果不设置如此,当一个粒子处在另外一个粒子的前面,而后者来自别的例子系统,有时候会看到纹理的黑色背景。

    4.使用精灵THREE.Sprite

        THREE.Sprite类可用于如下两种目的:

        创建一个可以基于屏幕坐标移动、定位和缩放的对象。你可以用它来创建一个平视显示器(HUD),就像在三维场景上蒙了一层;

        创建一个类似粒子的、可以在三维空间移动的对象,类似使用CanvasRenderer的THREE.Particle。三维场景中的精灵有事有称作广告牌。总是相面镜头,就像高速公路上的广告牌总是面向司机。

        使用精灵,需要用到材质对象SpriteMaterial和网格对象Sprite。如果我们要实现一个外部图片作为纹理,像广告一样在屏幕底部左右移动。应该怎样实现?要使用精灵对象。摄像头一般使用正射投影。例如:

    var cameraOrtho = new THREE.OrthographicCamera(0, window.innerWidth, window.innerHeight, 0, -10, 10);

        left为0, right为window的width,top为window的height,bottom为0,near为-10, far为10。

        下面的一个方法实现了如何创建Sprite网格对象:

    function createSprite(size, transparent, opacity, color, spriteNumber){
                    var spriteMaterial = new THREE.SpriteMaterial({
                        transparent: transparent,
                        opacity: opacity,
                        color: color,
                        map: getTexture()
                    });
    
                    spriteMaterial.map.offset = new THREE.Vector2(0.2 * spriteNumber, 0);
                    spriteMaterial.map.repeat = new THREE.Vector2(1/5, 1);
                    spriteMaterial.depthTest = false;
    
                    spriteMaterial.blending = THREE.AdditiveBlending;
    
                    var sprite = new THREE.Sprite(spriteMaterial);
                    sprite.scale.set(size, size, size);
                    sprite.position.set(100, 50, -10);
                    sprite.velocityX = 5;
    
                    sceneOrtho.add(sprite);
                }

        首先创建了一个SpriteMaterial,传递的参数和创建粒子参数差不多。创建之后,需要设置map的一些属性,包括offset和repeat。由于我们使用的图片是一个横排包含了五个图标的压缩图片。所以需要指定偏移量offset。offset值从0到1。如果要显示第三个图标,那么x偏移量为0.2*2。y偏移量不变。repeat是指重复显示,x轴重复为1/5是指显示整个图片的1/5,y轴重复1次。设置SpriteMaterial,可以使Sprite和其他的粒子系统更好的融合显示。

        接下来床架Sprite对象,设置它的scale和position属性,velocityX是我们自定义x轴移动速度的因子。最后,我们还得考虑如何渲染。代码如下:

    function render(){
                    stats.update();
    
                    camera.position.y = Math.sin(step += 0.01) * 20;
    
                    sceneOrtho.children.forEach(function(e){
                        if(e instanceof THREE.Sprite){
                            e.position.x = e.position.x + e.velocityX;
                            if(e.position.x > window.innerWidth){
                                e.velocityX = -5;
                                e.material.map.offset.set(1/5 *(controls.sprite % 4), 0);
                            }
                            if(e.position.x < 0){
                                e.velocityX = 5;
                            }
                        }
                    });
    
                    requestAnimationFrame(render);
    
                    webGLRenderer.render(scene, camera);
                    webGLRenderer.autoClear = false;
                    webGLRenderer.render(sceneOrtho, cameraOrtho);
                }

        camera是用来显示三维图形的摄像头,和我们显示精灵的sceneOrtho互不影响。我们遍历了sceneOrtho.children集合。如何元素时THREE.Sprite,我们重重新设置position.x,让Sprite动起来。

       如果要让两个场景都渲染出来,我们需要设置WebGLRenderer的autoClear属性为false。

    5.使用THREE.Sprite实现三维效果

        要实现三维效果,可使用THREE.Object3D对象作为Sprite对象的集合。如下所示:

    function createSprites(){
                    group = new THREE.Object3D();
                    var range = 200;
                    for(var i = 0; i < 400; i++){
                        group.add(createSprite(10, false, 0.6, 0xffffff, i % 5, range));
                    }
                    scene.add(group);
                }

        createSprite和4中的createSprite函数显示,只是多了一个range参数,限制坐标范围。

    6.从高级几何体中创建粒子系统

        粒子系统所渲染的粒子来自于几何体的顶点。如果我们提供一个复杂的几何体,诸如环面纽结或管道,我们可以基于这个几何体的顶点创建出一个粒子系统。

       首先创建一个复杂几何体TorusKnotGeometry。创建代码如下:

    var geom = new THREE.TorusKnotGeometry(
                                controls.radius,
                                controls.tube,
                                Math.round( controls.radialSegments),
                                Math.round(controls.tubularSegments),
                                Math.round(controls.p), Math.round(controls.q),
                                controls.heightScale
                            );

        参数这里不再多说了。接下来基于这个几何体创建粒子对象PointCloud。代码如下:

    function createPointCloud(goem){
                var material = new THREE.PointCloudMaterial({
                    color: 0xffffff,
                    size: 3,
                    transparent: true,
                    blending: THREE.AdditiveBlending,
                    map: generateSprite()
                });
    
                var cloud = new THREE.PointCloud(goem, material);
                cloud.sortParticles = true;
    
                return cloud;
            }

        和创建其他网格相似,字节把复杂几何体传递和材质传递给PointCloud的构造函数。这里的generateSprite函数是通过Canvas绘制一个高亮的纹理。纹理的绘制如下:

    function generateSprite(){
                var canvas = document.createElement("canvas");
                canvas.width = 16;
                canvas.height = 16;
                var context = canvas.getContext("2d");
    
                var gradient = context.createRadialGradient(canvas.width/2, canvas.height/2, 0, canvas.width/2, canvas.height/2, canvas.width/2);
                gradient.addColorStop(0, "rgba(255, 255, 255, 1)");
                gradient.addColorStop(0.2, "rgba(0, 255, 255, 1)");
                gradient.addColorStop(0.4, "rgba(0, 0, 64, 1)");
                gradient.addColorStop(1, "rgba(0, 0, 0, 1)");
    
                context.fillStyle = gradient;
                context.fillRect(0, 0, canvas.width, canvas.height);
    
                var texture = new THREE.Texture(canvas);
                texture.needsUpdate = true;
                return texture;
            }

        通过createradialGradirent函数创建一个从内向外的渐变效果,达到的效果就是一个比较明亮的发光点。运行结果如下:

    image

  • 相关阅读:
    CS round--36
    Vijos 1002 过河 dp + 思维
    汇编模拟36选7
    1137
    E. Mike and Foam 容斥原理
    Even-odd Boxes hackerrank 分类讨论
    112. 作业之地理篇 最小费用最大流模板题
    1550: Simple String 最大流解法
    Sam's Numbers 矩阵快速幂优化dp
    java.sql.SQLSyntaxErrorException: ORA-01722: 无效数字
  • 原文地址:https://www.cnblogs.com/w-wanglei/p/6750967.html
Copyright © 2020-2023  润新知