ThreeJS学习笔记(四)——粒子系统
粒子和粒子系统
Threejs中制作粒子效果有两种方法:
THREE.Particle和THREE.ParticleSytem
如果项目使用的渲染器是CanvasRenderer,直接使用THREE.Particle创建完粒子即可直接添加到scene中,但是如果使用的是WebGlRenderer渲染器,那就要先创建THREE.ParticleSytem对象,然后通过这个对象来创建粒子。PatricleSystem具有形体和材质两个属性,因此使用PatitlceSystem可以借助几何体生成粒子,也可以先创建一个由多个点构成的形体后去创建粒子。
THREE.Sprite
使用Sprite来制作粒子可以实现以下两种需求:
1.使用useScreenCoordinates:true属性建立一个独立于camera摄像范围之外的元素,即它在屏幕上的位置不受camera的位置和焦点变化而变化,它的移动、定位、缩放是基于屏幕坐标的。
2.与在CanvasRenderer渲染器中使用THREE.Particle创建粒子一样,不需要使用THREE.ParticleSytem, 在WebGlRenderer渲染器中使用THREE.Sprite创建的粒子可以直接添加到scene中。三维场景中创建出来的精灵总是面向镜头的。即不会有倾斜变形之类透视变化,只有近大远小的变化
THREE.Sprite
- Sprite默认大小是1*1,设置Sprite大小需要使用scale进行缩放
- Sprite的样式通过参数meterial定义,参数meterial是使用THREE.SpriteMaterial类创建的材质对象
- THREE.SpriteMaterial可以定义精灵的颜色和纹理,纹理对象可以调用new THREE.ImgesUtils.loadTexture(url)来引用一张图片作为纹理,也可以调用new THREE.CanvasTexture( canvas),使用动态绘制的canvas图作为纹理。
创建粒子
以下创建了300个粒子,并添加到场景中,
function createCanvas(width,height,colors){//创建画布并绘制精灵纹理
var canvas = document.createElement( 'canvas' );
canvas.width = width;
canvas.height = height;
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 );
colors.forEach(function(color){
gradient.addColorStop( color.start, color.rgba );
})
context.fillStyle = gradient;
context.fillRect( 0, 0, canvas.width, canvas.height );
return canvas;
}
function createMaterial(width,height,colors){//使用画布创建材质
var sprite=createCanvas(width,height,colors);
return new THREE.SpriteMaterial( {
map: new THREE.CanvasTexture( sprite ),
blending: THREE.AdditiveBlending,//使用饱和度叠加的混和模式渲染粒子
overdraw:false,
depthWrite:false
} )
}
function initParticle( particle,coord,size) {//初始化粒子
var particle = this instanceof THREE.Sprite ? this : particle;
if(!size&&size!=0){
size=Math.random() * 32 + 16;
}
particle.scale.x = particle.scale.y = size;
particle.position.set( coord.x,coord.y,coord.z);
}
function addParticles(){//在场景中创建300个粒子,粒子颜色在一个范围内随机取色
for ( var i = 0; i < 300; i++ ) {
var deepColor=Math.round(Math.random()*255);
var lightColor=Math.round(deepColor*32/255);
var material= createMaterial(32,32,[
{start:0,rgba:'rgba(255,255,255,1)'},
{start:0.2,rgba:'rgba(0,'+deepColor+','+Math.round(Math.random()*80+175)+',1)'},
{start:0.4,rgba:'rgba(0,'+lightColor+',64,1)'},
{start:1,rgba:'rgba(0,0,0,1)'}
])
var particle = new THREE.Sprite(material);
initParticle(particle, new THREE.Vector3(Math.random()*500-250,Math.random()*500-250,Math.random()*500-250),10);
scene.add( particle );
}
}
执行addParticles()后如图所示
粒子的运动
粒子的缓运动画也都是用上一节说到的Tween.js实现的。循环动画示例如下:
function addParticles(){//在场景中创建300个粒子,粒子颜色在一个范围内随机取色
for ( var i = 0; i < 300; i++ ) {
var deepColor=Math.round(Math.random()*255);
var lightColor=Math.round(deepColor*32/255);
var material= createMaterial(32,32,[
{start:0,rgba:'rgba(255,255,255,1)'},
{start:0.2,rgba:'rgba(0,'+deepColor+','+Math.round(Math.random()*80+175)+',1)'},
{start:0.4,rgba:'rgba(0,'+lightColor+',64,1)'},
{start:1,rgba:'rgba(0,0,0,1)'}
])
var particle = new THREE.Sprite(material);
_particles.push(particle);
//initParticle(particle, new THREE.Vector3(Math.random()*500-250,Math.random()*500-250,Math.random()*500-250),10)
var delay=i*5;
particleLoop(particle,delay)
scene.add( particle );
}
}
function initParticle( particle,coord,size) {
var particle = this instanceof THREE.Sprite ? this : particle;
if(!size&&size!=0){
size=Math.random() * 32 + 16;
}
particle.scale.x = particle.scale.y = size;
particle.position.set( coord.x,coord.y,coord.z);
}
function particleLoop(particle,delay){
particle=particle?particle:this;
initParticle(particle,new THREE.Vector3(Math.random()*500-250,Math.random()*500-250,Math.random()*500-250),Math.random() * 12 + 8);
delay=delay?delay:0;
new TWEEN.Tween( particle )
.delay( delay )
.to( {}, 1500 )
.onComplete(particleLoop )
.start();
new TWEEN.Tween( particle.position )
.delay( delay )
.to( { x:0, y: 0, z: 0}, 1500 )
.start();
new TWEEN.Tween( particle.scale )
.delay( delay )
.to( { x: 8, y: 8 }, 1500 )
.start();
}
本章示例
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<div id="space"></div>
<script src="https://code.jquery.com/jquery-1.12.4.min.js" integrity="sha256-ZosEbRLbNQzLpnKIkEdrPv7lOy9C27hHQ+Xp8a4MxAQ=" crossorigin="anonymous"></script>
<script src="../js/lib/threejs/three.js"></script>
<script src="../js/lib/threejs/OrbitControls.js"></script>
<script src="../js/lib/Tween.js"></script>
<script>
var container, stats;
var camera, scene, renderer,_particles=[];
var mouseX = 0, mouseY = 0;
var windowHalfX = window.innerWidth / 2;
var windowHalfY = window.innerHeight / 2;
init();
animate();
var mesh;
function init() {
container = document.getElementById("space")
camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 1, 5000 );
camera.position.set(0, 0, 1500);
scene = new THREE.Scene();
var ambient = new THREE.AmbientLight( 0xffffff );
scene.add( ambient );
var directionalLight = new THREE.DirectionalLight( 0xffffff );
directionalLight.position.set( -5, 5, 5).normalize();
scene.add( directionalLight );
var pointlight = new THREE.PointLight(0x63d5ff, 1, 200);
pointlight.position.set(0, 0, 200);
scene.add( pointlight );
var pointlight2 = new THREE.PointLight(0xffffff, 1, 200);
pointlight2.position.set(-200, 200, 200);
scene.add( pointlight2 );
var pointlight3 = new THREE.PointLight(0xffffff, 1.5, 200);
pointlight3.position.set(-200, 200, 0);
scene.add( pointlight3 );
var controls = new THREE.OrbitControls(camera,container);
//controls.maxPolarAngle=1.5;
//controls.minPolarAngle=1;
controls.enableDamping=true;
controls.enableKeys=false;
controls.enablePan=false;
controls.dampingFactor = 0.1;
controls.rotateSpeed=0.1;
// controls.enabled = false;
//controls.minDistance=1000;
//controls.maxDistance=3000;
var path = "../resource/sky/";
var format = '.jpg';
var urls = [
path + 'px' + format, path + 'nx' + format,
path + 'py' + format, path + 'ny' + format,
path + 'pz' + format, path + 'nz' + format
];
var skyMaterials = [];
for (var i = 0; i < urls.length; ++i) {
var loader = new THREE.TextureLoader();
loader.setCrossOrigin( this.crossOrigin );
var texture = loader.load( urls[i], function(){}, undefined, function(){} );
skyMaterials.push(new THREE.MeshBasicMaterial({
//map: THREE.ImageUtils.loadTexture(urls[i], {},function() { }),
map: texture,
overdraw: true,
side: THREE.BackSide,
//transparent: true,
//needsUpdate:true,
premultipliedAlpha: true
//depthWrite:true,
// wireframe:false,
})
);
}
var cube = new THREE.Mesh(new THREE.CubeGeometry(500, 500,500), new THREE.MeshFaceMaterial(skyMaterials));
cube.name="sky";
//scene.add(cube);
addParticles();
renderer = new THREE.WebGLRenderer();
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
container.appendChild( renderer.domElement );
document.addEventListener( 'mousemove', onDocumentMouseMove, false );
window.addEventListener( 'resize', onWindowResize, false );
}
function createCanvas(width,height,colors){//创建画布并绘制精灵纹理
var canvas = document.createElement( 'canvas' );
canvas.width = width;
canvas.height = height;
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 );
colors.forEach(function(color){
gradient.addColorStop( color.start, color.rgba );
})
context.fillStyle = gradient;
context.fillRect( 0, 0, canvas.width, canvas.height );
return canvas;
}
function createMaterial(width,height,colors){//使用画布创建材质
var sprite=createCanvas(width,height,colors);
return new THREE.SpriteMaterial( {
map: new THREE.CanvasTexture( sprite ),
blending: THREE.AdditiveBlending,
overdraw:false,
depthWrite:false
} )
}
function addParticles(){//在场景中创建300个粒子,粒子颜色在一个范围内随机取色
for ( var i = 0; i < 300; i++ ) {
var deepColor=Math.round(Math.random()*255);
var lightColor=Math.round(deepColor*32/255);
var material= createMaterial(32,32,[
{start:0,rgba:'rgba(255,255,255,1)'},
{start:0.2,rgba:'rgba(0,'+deepColor+','+Math.round(Math.random()*80+175)+',1)'},
{start:0.4,rgba:'rgba(0,'+lightColor+',64,1)'},
{start:1,rgba:'rgba(0,0,0,1)'}
])
var particle = new THREE.Sprite(material);
_particles.push(particle);
//initParticle(particle, new THREE.Vector3(Math.random()*500-250,Math.random()*500-250,Math.random()*500-250),10)
var delay=i*5;
particleLoop(particle,delay)
scene.add( particle );
}
}
function initParticle( particle,coord,size) {
var particle = this instanceof THREE.Sprite ? this : particle;
if(!size&&size!=0){
size=Math.random() * 32 + 16;
}
particle.scale.x = particle.scale.y = size;
particle.position.set( coord.x,coord.y,coord.z);
}
function particleLoop(particle,delay){
particle=particle?particle:this;
initParticle(particle,new THREE.Vector3(Math.random()*500-250,Math.random()*500-250,Math.random()*500-250),Math.random() * 12 + 8);
delay=delay?delay:0;
new TWEEN.Tween( particle )
.delay( delay )
.to( {}, 1500 )
.onComplete(particleLoop).onStart(function(){
})
.start();
new TWEEN.Tween( particle.position )
.delay( delay )
.to( { x:0, y: 0, z: 0}, 1500 )
.start();
new TWEEN.Tween( particle.scale )
.delay( delay )
.to( { x: 8, y: 8 }, 1500 )
.start();
}
function onWindowResize() {
windowHalfX = window.innerWidth / 2;
windowHalfY = window.innerHeight / 2;
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
}
function onDocumentMouseMove( event ) {
mouseX = ( event.clientX - windowHalfX ) / 2;
mouseY = ( event.clientY - windowHalfY ) / 2;
}
//
function animate() {
requestAnimationFrame( animate );
render();
TWEEN.update();
}
function render() {
// camera.position.x += ( mouseX - camera.position.x ) ;
// camera.position.y += ( mouseY - camera.position.y ) ;
// for(var i=0,len=_particles.length; i<len; i++){
// var posx=(Math.random()-0.5)*1;
// var posy=(Math.random()-0.5)*1;
// var posz=(Math.random()-0.5)*1;
// _particles[i].position.set(_particles[i].position.x+posx,_particles[i].position.y+posy,_particles[i].position.z+posz)
// }
camera.lookAt( scene.position );
renderer.render( scene, camera );
}
</script>
</body>
</html>
手机阅读请扫描下方二维码:
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
555
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1