07.ThreeJs开发指南-第七章-粒子系统

第七章 粒子系统

function createParticles(){

    var meaterial = new THREE.ParticleBasicMaterial();
    for(var x = -5 ; x < 5 ; x ++){
        for(var y = -5 ; y < 5 ; y ++){
        //手动创建一个粒子,只需传递一个材质即可。还材质可以是 ParticleBasicMaterial 或者 ParticleProgramMaterial材质
        var particle = new THREE.Particle(material);
            particle.position.set(x*10,y*10,0);
            scene.add(particle);            
        }
    }
}

如果在创建粒子的时候没有指定任何属性,那么粒子就会被渲染成二维的白色小方块。

THREE.Particle

和THREE.Mesh一样,THREE.Particle 也是对THREE.Object3D对象的扩展。也就说THREE.Mesh的大部分属性也可以用在THREE.Particle对象上。

这里,我们使用THREE.Particle创建粒子,必须使用CanvasRenderer才能渲染出来。

如果使用WebGLRenderer渲染器,我们需要首先创建一个THREE.ParticleSystem对象,然后通过这个对象来创建粒子。

例子如下:

function createParticles(){

    var geo = new THREE.Geometry();
    var material = new THREE.ParticleBasicMaterial({size:4,vertexColors:true,color:0xffffff});

    for(var x = -5;x<5;x++){
        for(var y= -5;y<5;y++){
            //为每一个粒子创建一个顶点,并添加到一个几何体中
            var particle = new THREE.Vector3(x*10,y*10,0);
            geo.geometry.push(particle);

            geo.colors.push(new THREE.Color(Math.random()*0x00ffff));

        }
    }

    var system = new THREE.ParticleSystem(geo,material);
    scene.add(system);

}

粒子、粒子系统、BasicParicleMaterial

function createParticles(size,transparent,opacity,vertexColors,sizeAttenuation,color){

    var geom = new THREE.Geometry();
    var material = new THREE.ParticleBasicMaterial({
        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 数组只有在 vertexColors设置为true的时候才会有用,这样每个顶点可以着不同的颜色。
        geom.colors.push(color);
    }

    system = new THREE.ParticleSystem(geom,material);
    scene.add(system);
}

ParticleBasicMaterial对象的属性:

color:ParticleSystem对象所有粒子的颜色。如果vertexColors属性为true,而且也指定了几何体的colors属性,则该属性会被忽略。默认是0xffffff
map:可以在粒子上应用某种材质。
size:指定粒子的大小。默认为1.
sizeAttenuation:设置为false,则所有的粒子拥有相同的大小,无论距离相机的远近。如果设置为true,则根据距离相机的不同距离拥有不同的大小。默认为true。
vertexColors:通常ParticleSystem中的粒子具有相同的颜色,如果该属性为true,且几何体也设置了colors属性,那就使用该颜色数组中的值为粒子着色。默认为false。
opacity:同transparent一起使用。设置粒子的透明度。默认为1(不透明)
transparent:设置为true,则粒子在渲染时根据opacity设置的值进行渲染。默认为false。
blending:渲染时粒子的融合模式。
fog:粒子是否受场景中的雾化效果的影响。默认为true。

目前,我们渲染的粒子都是正方形的。我们可以使用下面两种方法来格式化粒子。
1.应用ParticleCanvasMaterial对象,将HTML5画布上绘制的内容作为粒子的纹理。
2.使用ParticleBasicMaterial的map(贴图)属性。

一、使用HTML5画布格式化粒子

1.如果你使用的是CanvasRenderer渲染器,则可以在ParticleCanvasMaterial对象中直接引用HTML5画布。

ParticleCanvasMaterial 该材质是专门为CanvasRenderer创建的,而且只能用于这种渲染器。

该材质的属性:

color:粒子的颜色。根据特定的融合模式,可以影响画布的颜色。
program:以画布上下文为参数的函数。该函数在粒子渲染时调用。调用该函数将在画布上下文中产生一个属性,该输出将会以粒子的形态显示出来。
opacity:默认1,不透明。
transparent:粒子是否透明,同opacity一起使用 。
blending:渲染时粒子的融合模式。

function createParticles(){

    var material = new THREE.ParticleCanvasMaterial({
        program:draw,
        color:0xffffff
    });

    var range = 500;

    for(var i = 0 ;i<1000;i++){
        //由于使用的是CanvasRenderer,所以可以直接使用THREE.Particle创建粒子。
        var particle = new THREE.Particle(material);
        particle.position.set(
            Math.random() * range -range /2,
            Math.random() * range -range /2,
            Math.random() * range -range /2
        );

        particle.scale = 0.1;
        particle.rotation.z = Math.PI;
        scene.add(particle);
    }
}

//在该函数中绘制的结果将作为粒子的外形
var draw = function(ctx){

    ctx.fillStyle = 'orange';

    ...

    ctx.beginPath();
    ctx.fill();
}

2.如果你使用的WebGLRenderer渲染器,则需要采取额外的一些步骤,才能在格式化粒子时使用HTML5画布。

ParticleCanvasMaterial在WebGLRenderer中是不能使用的,只能使用ParticleBasicMaterial来达到相同的目的(将HTML5画布上的画的内容作为粒子的外形)。

ParticleBasicMaterial的map属性可以为粒子加载纹理,该纹理在Three.js中也可以是HTML5画布的输出。

var getTexture = function(){

    var canvas = document.createElement('canvas');
    canvas.width = 32;
    canvas.height = 32;

    var ctx = canvas.getContext('2d');

    //draw the ghost
    ...

    ctx.fill()l
    var texture = new THREE.Texture(canvas);
    texture.needsUpdate = true;

    return texture;
}


function createParticles(size,transparent,opacity,sizeAttenuation,color){

    var geom = new THREE.Geometry();

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

    var range = 500;

    for(var i = 0 ;i <5000;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);
    }

    system = new THREE.ParticleSystem(geom,material);
    //确保粒子在渲染前按照z轴排好序。纠正渲染错误用的。
    system.sortParticles = true;
    system.name = 'particles';
    scene.add(system);
}

ParticleSystem的一个属性是叫做 ParticleSystem.FrustrumCulled属性,如果该属性设置为true,则落在相机可见范围之外的粒子将不会被绘制。必要时,使用该项设置可以提高效率和帧频。

使用纹理格式化粒子:

加载外部纹理图片:

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

var material = new THREE.ParticleBasicMaterial({
    size:3,
    transparent:true,
    opacity:true,
    map:texture,
    blending:THREE.AdditiveBlending,//画新像素的时候,背景像素的颜色会被添加到新像素上。因为雨滴的背景色是黑色的,这样就不会显示出来。另一种方式,是将纹理中的黑色定义成透明的,但是这种组合在粒子和WebGL中不起作用。
    sizeAttenuation:true,
    color:0xffffff
});

创建粒子,前面我们是让整个粒子系统旋转,这次我们让每个粒子运动起来。

var range = 40;
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;//粒子以多快的速度下降

    geom2.vertices.push(particle);
}

帧循环中改变粒子的位置:

var vertices = system2.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);

});

模拟降雪:

使用五种不同的图片。

ParticleSystem只能有一种材质, 如果要用多个材质,则只能使用多个粒子系统。

function createParticles(size,transparent,opacity,sizeAttenuation,color){

    var texture1 = THREE.ImageUtils.loadTexture('../assets/textures/particles/snowflake1.png');
    var texture2 = THREE.ImageUtils.loadTexture('../assets/textures/particles/snowflake2.png');
    var texture3 = THREE.ImageUtils.loadTexture('../assets/textures/particles/snowflake3.png');
    var texture4 = THREE.ImageUtils.loadTexture('../assets/textures/particles/snowflake4.png');

    scene.add(createSystem('system1',texture1,size,transparent,opacity,sizeAttenuation,color));
    scene.add(createSystem('system2',texture2,size,transparent,opacity,sizeAttenuation,color));
    scene.add(createSystem('system3',texture3,size,transparent,opacity,sizeAttenuation,color));
    scene.add(createSystem('system4',texture4,size,transparent,opacity,sizeAttenuation,color));

}

function createSystem(name,texture,size,transparent,opacity,sizeAttenuation,color){

    var geom = new THREE.Geometry();

    var color = new THREE.Color(color);
    color.setHSL(color.getHSL().h,color.getHSL().s,color.getHSL().l*(Math.random()));

    var material = new THREE.ParticleBasicMaterial({
        size:size,
        transparent:transparent,
        opacity:opacity,
        map:texture,
        blending:THREE.AdditiveBlending,
        depthWrite:false,//决定当前对象是否影响深度缓存。为false,保证各个粒子系统之间互补影响。
        sizeAttenuation:sizeAttenuation,
        color:color
    });

    var range = 40;
    for(var i = 0 ;i<50;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;
        particle.velocityZ = (Math.random() - 0.5) / 3;

        geom.vertices.push(particle);
    }

    var system = new THREE.ParticleSystem(geom,material);
    system.name = name;
    system.sortParticles = true;//在渲染之前沿着z轴排好序

    return system;
}

帧循环:

scene.children.forEach(function(child){

    if(child instanceof THREE.ParticleSystem){
        var vertices = child.geometry.vertices;

        vertices.forEach(function(v){
            v.y = v.y - (v.velocityY);
            v.x = v.x - (v.velocityX);
            v.z = v.z - (v.velocityZ);
        });

        if(v.y <= 0)v.y = 60;

        if(v.x<=-20 || v.x >=20)
            v.velocityX = v.velocityX * (-1);

        if(v.z <= -10 || v.z >=20)
            v.velocityZ = v.velocityZ * (-1);
    }
});

该方法的限制:如果我们要的纹理种类越多,即需要创建和管理更多的粒子系统。

THREE.Particle 不能和WebGLRenderer一起使用。

CanvasRenderer 渲染器有性能的问题。

使用精灵

THREE.Sprite类

作用:
1.创建一个基于屏幕坐标移动、定位和缩放的对象。你可以用它来创建一个平视显示器(HUD),就像在三维场景上蒙了一层。
2.创建一个类似粒子的、可以在三维空间移动的对象,类似使用CanvasRenderer的THREE.Particle。三维场景中的精灵有时候也称为广告牌。

function getTexture(){
    //加载包含5张不同颜色的精灵图片
    var texture = new THREE.ImageUtils.loadTexture('../assets/textures/particles/sprite-sheet.png');

    return texture;
}

function createSprite(size,transparent,opacity,color,spriteNumber){

    var spriteMaterial = new THREE.SpriteMaterial({
        opacity:opacity,
        color:color,
        transparent:transparent,
        useScreenCoordinates:true,
        map:getTexture()
    });

    //uvOffset、uvScale 正确选择要显示的精灵图形
    spriteMaterial.uvOffset.set(1/5 * spriteNumber ,0);//决定纹理在x轴和y轴的偏移量,我们这里只有一行图片,所以,y偏移量为0

    //这里如果我们显示第三个图形,则x轴偏移量为0.4。如果我们只设置偏移量,那么会将第三、四、五个图形压缩在一起。所以,要想只显示第三个,则需要放大。

    //我们将uvScale的u值设置为0.2,这意味着放大(沿x轴)纹理,只显示其中的20%,也就是一个精灵。
    spriteMaterial.uvScale.set(1/5,1);//缩放比例,取值范围0-1

    spriteMaterial.alignmnet = THREE.SpriteAlignmnet.bottomCenter;
    spriteMaterial.scaleByViewport = true;
    spriteMaterial.blending = THREE.AdditiveBlending;

    var sprite = new THREE.Sprite(spriteMaterial);
    sprite.scale.set(size,size,size);
    sprite.position.set(200,window.innderHeight - 2,0);
    sprite.velocityX = 5;

    scene.add(sprite);
}

THREE.SpriteMaterial 材质属性:

Color:粒子的颜色
Map:精灵使用的纹理
sizeAttenuation:若为false,则距离镜头的远近不会影响精灵的大小。默认为true。
opacity:设置精灵的透明度。默认1,不透明。
blending:渲染精灵时所以的融合模式。
fog:精灵是否受创建雾化效果的影响。
useScreenCoordinates:若设置为true,则精灵的位置相对于窗口左上角的x和y来定位,创建中的相机就会被完全忽略。
scaleByViewport:精灵的大小取决于视图窗口的尺寸。如果设置为true,则精灵的尺寸 = 图片宽度 / 视图窗口高度。若为false,则精灵的尺寸 = 图片宽度 / 1.0.
alignmnet:当精灵被压缩时(使用scale属性),该属性指定精灵从哪里开始缩放。如果该属性设置为THREE.SpriteAlignmnet.topLeft,则当增加或减少精灵的缩放比例的时候,精灵的左上角保持不动。
uvOffset:结合nvScale选择精灵所用的纹理。
uvScale:结合uvOffset选择精灵所用的纹理。

在三维空间中定位粒子

如果将useScreenCoordinates设置为false,那么精灵的行为就会跟前面所讨论的粒子一样,使用的是相机坐标。

function createSprite(size,transparent,opacity,color,spriteNumber){

    var spriteMaterial = new THREE.SpriteMaterial({
        opacity:opacity,
        color:color,
        transparent:transparent,
        useScreenCoordinates:false,
        sizeAttenuation:true,
        map:getTexture()
    });

    spriteMaterial.uvOffset.set(1/5*spriteNumber,0);
    spriteMaterial.uvScale.set(1/5,1);

    spriteMaterial.alignmnet = THREE.SpriteAlignmnet.bottomCenter;
    spriteMaterial.blending = THREE.AdditiveBlending;

    var sprite = new THREE.Sprite(spriteMaterial);
    sprite.scale.set(size,size,size);
    sprite.position = new THREE.Vector3(
        Math.random() * range  -range /2 ,
        Math.random() * range  -range /2 ,
        Math.random() * range  -range /2 
    );

    sprite.velocityX = 5;

    return sprite;
}

group.rotation.x += 0.1;

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

从环面扭结创建粒子系统。

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


function createParticleSystem(geom){

    var material = new THREE.ParticleBasicMaterial({

        color:0xffffff,
        size:3,
        transparent.true,
        blending:THREE.AdditiveBlending,
        map:getTexture()
    });

    var system = new THREE.ParticleSystem(geom,material);
    system.sortParticles = true;

    return system;
}

var geom = new THREE.TorusknotGeometry(...);
var knot = createParticleSystem(gemo);

如果你想创建大量的粒子,并共享同一个材质,那么你应该使用THREE.ParticleSystem对象。这样几何体中的每个顶点都会被渲染成粒子,并使用指定的材质。

你可能感兴趣的:(Three.js)