提交 54e3bc61 编写于 作者: A alteredq

Added elementary support for particles to WebGLRenderer.

It works more or less the same as lines:

  - particles are passed to ParticleSystem object as geometry
  - material is ParticleBasicMaterial (textures should be supported, not tested yet)
  - geometry can be updated by setting "__dirtyVertices" property of the geometry
上级 114b673a
此差异已折叠。
此差异已折叠。
此差异已折叠。
<!DOCTYPE HTML>
<html lang="en">
<head>
<title>three.js - particles - webgl</title>
<meta charset="utf-8">
<style type="text/css">
body {
background-color: #000000;
margin: 0px;
overflow: hidden;
}
a {
color:#0078ff;
}
</style>
</head>
<body>
<script type="text/javascript" src="../build/Three.js"></script>
<script type="text/javascript" src="js/Stats.js"></script>
<script type="text/javascript">
var container, stats;
var camera, scene, renderer, particles, geometry, materials = [], parameters, i, h, color;
var mouseX = 0, mouseY = 0;
var windowHalfX = window.innerWidth / 2;
var windowHalfY = window.innerHeight / 2;
init();
setInterval( loop, 1000 / 60 );
function init() {
container = document.createElement( 'div' );
document.body.appendChild( container );
camera = new THREE.Camera( 75, window.innerWidth / window.innerHeight, 1, 3000 );
camera.position.z = 1000;
scene = new THREE.Scene();
scene.fog = new THREE.FogExp2( 0x000000, 0.0007 );
geometry = new THREE.Geometry();
for ( i = 0; i < 4000; i++ ) {
vector = new THREE.Vector3( Math.random() * 2000 - 1000, Math.random() * 2000 - 1000, Math.random() * 2000 - 1000 );
geometry.vertices.push( new THREE.Vertex( vector ) );
}
parameters = [ [ [1.0, 1.0, 1.0], 5 ], [ [0.95, 1, 1], 4 ], [ [0.90, 1, 1], 3 ], [ [0.85, 1, 1], 2 ], [ [0.80, 1, 1], 1 ] ];
//parameters = [ [ 0xff0000, 5 ], [ 0xff3300, 4 ], [ 0xff6600, 3 ], [ 0xff9900, 2 ], [ 0xffaa00, 1 ] ];
//parameters = [ [ 0xffffff, 5 ], [ 0xdddddd, 4 ], [ 0xaaaaaa, 3 ], [ 0x999999, 2 ], [ 0x777777, 1 ] ];
for ( i = 0; i < parameters.length; i++ ) {
size = parameters[i][1];
color = parameters[i][0];
//materials[i] = new THREE.ParticleBasicMaterial( { color: color, size: size } );
materials[i] = new THREE.ParticleBasicMaterial( { size: size } );
materials[i].color.setHSV( color[0], color[1], color[2] );
particles = new THREE.ParticleSystem( geometry, materials[i] );
particles.rotation.x = Math.random() * 6;
particles.rotation.y = Math.random() * 6;
particles.rotation.z = Math.random() * 6;
particles.updateMatrix();
scene.addObject( particles );
}
scene.addObject( particles );
renderer = new THREE.WebGLRenderer();
renderer.setSize( window.innerWidth, window.innerHeight );
container.appendChild( renderer.domElement );
stats = new Stats();
stats.domElement.style.position = 'absolute';
stats.domElement.style.top = '0px';
container.appendChild( stats.domElement );
document.addEventListener( 'mousemove', onDocumentMouseMove, false );
document.addEventListener( 'touchstart', onDocumentTouchStart, false );
document.addEventListener( 'touchmove', onDocumentTouchMove, false );
}
function onDocumentMouseMove( event ) {
mouseX = event.clientX - windowHalfX;
mouseY = event.clientY - windowHalfY;
}
function onDocumentTouchStart( event ) {
if ( event.touches.length == 1 ) {
event.preventDefault();
mouseX = event.touches[ 0 ].pageX - windowHalfX;
mouseY = event.touches[ 0 ].pageY - windowHalfY;
}
}
function onDocumentTouchMove( event ) {
if ( event.touches.length == 1 ) {
event.preventDefault();
mouseX = event.touches[ 0 ].pageX - windowHalfX;
mouseY = event.touches[ 0 ].pageY - windowHalfY;
}
}
function loop() {
var time = new Date().getTime() * 0.00005;
camera.position.x += ( mouseX - camera.position.x ) * 0.05;
camera.position.y += ( - mouseY - camera.position.y ) * 0.05;
for( i = 0; i < scene.objects.length; i++ ) {
scene.objects[i].rotation.y = time * ( i < 4 ? i+1 : - (i+1) );
scene.objects[i].updateMatrix();
}
for( i = 0; i < materials.length; i++ ) {
color = parameters[i][0];
h = ( 360 * ( color[0] + time ) % 360 ) / 360;
materials[i].color.setHSV( h, color[1], color[2] );
}
renderer.render( scene, camera );
stats.update();
}
</script>
</body>
</html>
......@@ -26,6 +26,52 @@ THREE.Color.prototype = {
},
// based on MochiKit implementation by Bob Ippolito
// h,s,v ranges are < 0.0 - 1.0 >
setHSV: function ( h, s, v ) {
var red, green, blue, i, f, p, q, t;
if ( v == 0.0 ) {
red = green = blue = 0;
} else {
i = Math.floor( h * 6 );
f = ( h * 6 ) - i;
p = v * ( 1 - s );
q = v * ( 1 - ( s * f ) );
t = v * ( 1 - ( s * ( 1 - f ) ) );
switch ( i ) {
case 1: red = q; green = v; blue = p; break;
case 2: red = p; green = v; blue = t; break;
case 3: red = p; green = q; blue = v; break;
case 4: red = t; green = p; blue = v; break;
case 5: red = v; green = p; blue = q; break;
case 6: // fall through
case 0: red = v; green = t; blue = p; break;
}
}
this.r = red;
this.g = green;
this.b = blue;
if ( this.autoUpdate ) {
this.updateHex();
this.updateStyleString();
}
},
setHex: function ( hex ) {
this.hex = ( ~~ hex ) & 0xffffff;
......
......@@ -14,6 +14,8 @@ THREE.ParticleBasicMaterial = function ( parameters ) {
this.color = new THREE.Color( 0xffffff );
this.map = null;
this.opacity = 1;
this.size = 1;
this.blending = THREE.NormalBlending;
this.offset = new THREE.Vector2(); // TODO: expose to parameters
......@@ -23,6 +25,7 @@ THREE.ParticleBasicMaterial = function ( parameters ) {
if ( parameters.color !== undefined ) this.color.setHex( parameters.color );
if ( parameters.map !== undefined ) this.map = parameters.map;
if ( parameters.opacity !== undefined ) this.opacity = parameters.opacity;
if ( parameters.size !== undefined ) this.size = parameters.size;
if ( parameters.blending !== undefined ) this.blending = parameters.blending;
}
......@@ -37,6 +40,7 @@ THREE.ParticleBasicMaterial.prototype = {
'color: ' + this.color + '<br/>' +
'map: ' + this.map + '<br/>' +
'opacity: ' + this.opacity + '<br/>' +
'size: ' + this.size + '<br/>' +
'blending: ' + this.blending + '<br/>' +
')';
......
......@@ -154,7 +154,7 @@ THREE.WebGLRenderer = function ( parameters ) {
this.createParticleBuffers = function( geometry ) {
geometry.__webGLVertexBuffer = _gl.createBuffer();
geometry.__webGLFaceBuffer = _gl.createBuffer();
geometry.__webGLParticleBuffer = _gl.createBuffer();
};
......@@ -188,6 +188,17 @@ THREE.WebGLRenderer = function ( parameters ) {
};
this.initParticleBuffers = function( geometry ) {
var nvertices = geometry.vertices.length;
geometry.__vertexArray = new Float32Array( nvertices * 3 );
geometry.__particleArray = new Uint16Array( nvertices );
geometry.__webGLParticleCount = nvertices;
};
this.initMeshBuffers = function( geometryChunk, object ) {
var f, fl, nvertices = 0, ntris = 0, nlines = 0,
......@@ -662,6 +673,50 @@ THREE.WebGLRenderer = function ( parameters ) {
};
this.setParticleBuffers = function( geometry, hint, dirtyVertices, dirtyElements ) {
var v, vertex, offset,
vertices = geometry.vertices,
vl = vertices.length,
vertexArray = geometry.__vertexArray,
particleArray = geometry.__particleArray;
if ( dirtyVertices ) {
for ( v = 0; v < vl; v++ ) {
vertex = vertices[ v ].position;
offset = v * 3;
vertexArray[ offset ] = vertex.x;
vertexArray[ offset + 1 ] = vertex.y;
vertexArray[ offset + 2 ] = vertex.z;
}
}
// yeah, this is silly as order of element indices is currently fixed
// though this could change if some use case arises
// (like depth-sorting of semi-opaque particles)
if ( dirtyElements ) {
for ( v = 0; v < vl; v++ ) {
particleArray[ v ] = v;
}
}
_gl.bindBuffer( _gl.ARRAY_BUFFER, geometry.__webGLVertexBuffer );
_gl.bufferData( _gl.ARRAY_BUFFER, vertexArray, hint );
_gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, geometry.__webGLParticleBuffer );
_gl.bufferData( _gl.ELEMENT_ARRAY_BUFFER, particleArray, hint );
};
function setMaterialShaders( material, shaders ) {
......@@ -734,6 +789,32 @@ THREE.WebGLRenderer = function ( parameters ) {
};
function refreshUniformsParticle( material, fog ) {
material.uniforms.color.value.setRGB( material.color.r * material.opacity, material.color.g * material.opacity, material.color.b * material.opacity );
material.uniforms.opacity.value = material.opacity;
material.uniforms.size.value = material.size;
material.uniforms.map.texture = material.map;
if ( fog ) {
material.uniforms.fogColor.value.setHex( fog.color.hex );
if ( fog instanceof THREE.Fog ) {
material.uniforms.fogNear.value = fog.near;
material.uniforms.fogFar.value = fog.far;
} else if ( fog instanceof THREE.FogExp2 ) {
material.uniforms.fogDensity.value = fog.density;
}
}
};
function refreshUniformsPhong( material ) {
//material.uniforms.ambient.value.setHex( material.ambient.hex );
......@@ -786,6 +867,10 @@ THREE.WebGLRenderer = function ( parameters ) {
setMaterialShaders( material, THREE.ShaderLib[ 'basic' ] );
} else if ( material instanceof THREE.ParticleBasicMaterial ) {
setMaterialShaders( material, THREE.ShaderLib[ 'particle_basic' ] );
}
// heuristics to create shader parameters according to lights in the scene
......@@ -847,6 +932,13 @@ THREE.WebGLRenderer = function ( parameters ) {
if ( material instanceof THREE.LineBasicMaterial ) {
refreshUniformsLine( material, fog );
}
if ( material instanceof THREE.ParticleBasicMaterial ) {
refreshUniformsParticle( material, fog );
}
if ( material instanceof THREE.MeshPhongMaterial ) {
......@@ -859,6 +951,7 @@ THREE.WebGLRenderer = function ( parameters ) {
material.uniforms.mNear.value = camera.near;
material.uniforms.mFar.value = camera.far;
}
setUniforms( program, material.uniforms );
......@@ -940,13 +1033,20 @@ THREE.WebGLRenderer = function ( parameters ) {
_gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, geometryChunk.__webGLLineBuffer );
_gl.drawElements( primitives, geometryChunk.__webGLLineCount, _gl.UNSIGNED_SHORT, 0 );
// render triangles
// render particles
} else {
} else if ( material instanceof THREE.ParticleBasicMaterial ) {
_gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, geometryChunk.__webGLParticleBuffer );
_gl.drawElements( _gl.POINTS, geometryChunk.__webGLParticleCount, _gl.UNSIGNED_SHORT, 0 );
// render triangles
} else {
_gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, geometryChunk.__webGLFaceBuffer );
_gl.drawElements( _gl.TRIANGLES, geometryChunk.__webGLFaceCount, _gl.UNSIGNED_SHORT, 0 );
}
};
......@@ -1199,11 +1299,24 @@ THREE.WebGLRenderer = function ( parameters ) {
if( ! geometry.__webGLVertexBuffer ) {
this.createParticleBuffers( geometry );
this.initParticleBuffers( geometry );
geometry.__dirtyVertices = true;
geometry.__dirtyElements = true;
}
if( geometry.__dirtyVertices ) {
this.setParticleBuffers( geometry, _gl.DYNAMIC_DRAW, geometry.__dirtyVertices, geometry.__dirtyElements );
}
add_buffer( objmap, 0, geometry, object );
geometry.__dirtyVertices = false;
geometry.__dirtyElements = false;
}/*else if ( object instanceof THREE.Particle ) {
......@@ -1955,7 +2068,30 @@ THREE.Snippets = {
].join("\n"),
// COLOR MAP
// COLOR MAP (particles)
map_particle_pars_fragment: [
"#ifdef USE_MAP",
"uniform sampler2D map;",
"#endif"
].join("\n"),
map_particle_fragment: [
"#ifdef USE_MAP",
"mapColor = texture2D( map, gl_PointCoord );",
"#endif"
].join("\n"),
// COLOR MAP (triangles)
map_pars_fragment: [
......@@ -2230,8 +2366,22 @@ THREE.UniformsLib = {
"pointLightPosition" : { type: "fv", value: [] },
"pointLightColor" : { type: "fv", value: [] }
}
},
particle: {
"color" : { type: "c", value: new THREE.Color( 0xeeeeee ) },
"opacity" : { type: "f", value: 1 },
"size" : { type: "f", value: 1 },
"map" : { type: "t", value: 0, texture: null },
"fogDensity": { type: "f", value: 0.00025 },
"fogNear" : { type: "f", value: 1 },
"fogFar" : { type: "f", value: 2000 },
"fogColor" : { type: "c", value: new THREE.Color( 0xffffff ) }
}
};
THREE.ShaderLib = {
......@@ -2508,6 +2658,52 @@ THREE.ShaderLib = {
].join("\n")
},
'particle_basic': {
uniforms: THREE.UniformsLib[ "particle" ],
fragment_shader: [
"uniform vec3 color;",
"uniform float opacity;",
THREE.Snippets[ "map_particle_pars_fragment" ],
THREE.Snippets[ "fog_pars_fragment" ],
"void main() {",
"vec4 mColor = vec4( color, opacity );",
"vec4 mapColor = vec4( 1.0 );",
THREE.Snippets[ "map_particle_fragment" ],
"gl_FragColor = mColor * mapColor;",
THREE.Snippets[ "fog_fragment" ],
"}"
].join("\n"),
vertex_shader: [
"uniform float size;",
"void main() {",
"vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );",
"gl_Position = projectionMatrix * mvPosition;",
"gl_PointSize = size;",
//"gl_PointSize = 10.0 + 6.0 * mvPosition.z;";
"}"
].join("\n")
}
};
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册