个人博客地址: https://cxx001.gitee.io
前面我们了解到了场景中的网格对象由几何体和材质组成,并且分别系统学习了它们。这节我们将学习一个特殊的网格对象-----粒子(精灵)。
了解粒子
一个粒子(新版叫精灵)是 一个二维平面(小方块) ,它总是面向摄像机。
<!-- chapter-07-01.html -->
<!DOCTYPE html>
<html>
<head>
<title>Particles</title>
<script type="text/javascript" src="../libs/three.js"></script>
<script type="text/javascript" src="../libs/stats.js"></script>
<script type="text/javascript" src="../libs/dat.gui.js"></script>
<style>
body {
margin: 0;
overflow: hidden;
background-color: #000000;
}
</style>
</head>
<body>
<div id="Stats-output">
</div>
<div id="WebGL-output">
</div>
<script type="text/javascript">
function init() {
var stats = initStats();
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000);
//var canvasRenderer = new THREE.CanvasRenderer();
var canvasRenderer = new THREE.WebGLRenderer();
canvasRenderer.setClearColor(new THREE.Color(0x000000, 1.0));
canvasRenderer.setSize(window.innerWidth, window.innerHeight);
camera.position.x = 0;
camera.position.y = 0;
camera.position.z = 150;
document.getElementById("WebGL-output").appendChild(canvasRenderer.domElement);
createSprites();
render();
function createSprites() {
// 创建精灵材质
var material = new THREE.SpriteMaterial();
for (var x = -5; x < 5; x++) {
for (var y = -5; y < 5; y++) {
// 创建精灵,只需要指定材质
var sprite = new THREE.Sprite(material);
sprite.position.set(x * 10, y * 10, 0);
scene.add(sprite);
}
}
}
function render() {
stats.update();
requestAnimationFrame(render);
canvasRenderer.render(scene, camera);
}
function initStats() {
var stats = new Stats();
stats.setMode(0); // 0: fps, 1: ms
stats.domElement.style.position = 'absolute';
stats.domElement.style.left = '0px';
stats.domElement.style.top = '0px';
document.getElementById("Stats-output").appendChild(stats.domElement);
return stats;
}
}
window.onload = init;
</script>
</body>
</html>
大量粒子
如果要创建大量粒子,使用上面THREE.Sprite
一个个创建那就太耗性能了。对于这种场景,我们要使用THREE.PointCloudMaterial
和THREE.PointCloud
来管理。
THREE.PointCloud
构造函数接收两个属性:几何体和材质,和创建网格对象类似。材质用来给粒子着色或添加纹理,而几何体则用来指定粒子的位置,几何体每个顶点将会以粒子的形态展示出来。
THREE.PointCloudMaterial
材质的属性:
<!-- chapter-07-02.html -->
<!DOCTYPE html>
<html>
<head>
<title>Particle Basic Material</title>
<script type="text/javascript" src="../libs/three.js"></script>
<script type="text/javascript" src="../libs/stats.js"></script>
<script type="text/javascript" src="../libs/dat.gui.js"></script>
<style>
body {
margin: 0;
overflow: hidden;
background-color: #000000;
}
</style>
</head>
<body>
<div id="Stats-output">
</div>
<div id="WebGL-output">
</div>
<script type="text/javascript">
function init() {
var stats = initStats();
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000);
var webGLRenderer = new THREE.WebGLRenderer();
webGLRenderer.setClearColor(new THREE.Color(0x000000, 1.0));
webGLRenderer.setSize(window.innerWidth, window.innerHeight);
camera.position.x = 20;
camera.position.y = 0;
camera.position.z = 150;
document.getElementById("WebGL-output").appendChild(webGLRenderer.domElement);
var cloud;
var controls = new function () {
this.size = 4;
this.transparent = true;
this.opacity = 0.6;
this.vertexColors = true;
this.color = 0xffffff;
this.sizeAttenuation = true;
this.rotateSystem = true;
this.redraw = function () {
if (scene.getObjectByName("particles")) {
scene.remove(scene.getObjectByName("particles"));
}
createParticles(controls.size, controls.transparent, controls.opacity, controls.vertexColors, controls.sizeAttenuation, controls.color);
};
};
var gui = new dat.GUI();
gui.add(controls, 'size', 0, 10).onChange(controls.redraw);
gui.add(controls, 'transparent').onChange(controls.redraw);
gui.add(controls, 'opacity', 0, 1).onChange(controls.redraw);
gui.add(controls, 'vertexColors').onChange(controls.redraw);
gui.addColor(controls, 'color').onChange(controls.redraw);
gui.add(controls, 'sizeAttenuation').onChange(controls.redraw);
gui.add(controls, 'rotateSystem');
controls.redraw();
render();
// 使用THREE.PointCloud创建大量粒子
function createParticles(size, transparent, opacity, vertexColors, sizeAttenuation, color) {
// 创建点云材质
var material = new THREE.PointCloudMaterial({
size: size,
transparent: transparent,
opacity: opacity,
vertexColors: vertexColors,
sizeAttenuation: sizeAttenuation,
color: color
});
// 创建自定义几何体
var geom = new THREE.Geometry();
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);
}
var step = 0;
function render() {
stats.update();
if (controls.rotateSystem) {
step += 0.01;
cloud.rotation.x = step;
cloud.rotation.z = step;
}
requestAnimationFrame(render);
webGLRenderer.render(scene, camera);
}
function initStats() {
var stats = new Stats();
stats.setMode(0); // 0: fps, 1: ms
stats.domElement.style.position = 'absolute';
stats.domElement.style.left = '0px';
stats.domElement.style.top = '0px';
document.getElementById("Stats-output").appendChild(stats.domElement);
return stats;
}
}
window.onload = init;
</script>
</body>
</html>
样式化粒子
默认粒子会渲染成小方块,下面来看看怎么改变粒子样式。
1. 使用HTML5画布样式化粒子
前面提到过,THREE.PointCloudMaterial
的map属性可以为粒子加载纹理,以此来改变粒子样式(关于纹理后续会详细介绍,这里只需关注使用)。首先我们看看把HTML5的画布转换为纹理来作为粒子的纹理运用。
<!-- chapter-07-03.html -->
<!DOCTYPE html>
<html>
<head>
<title>Particles - Canvas based texture - WebGL</title>
<script type="text/javascript" src="../libs/three.js"></script>
<script type="text/javascript" src="../libs/stats.js"></script>
<script type="text/javascript" src="../libs/dat.gui.js"></script>
<style>
body {
margin: 0;
overflow: hidden;
background-color: #000000;
}
</style>
</head>
<body>
<div id="Stats-output">
</div>
<div id="WebGL-output">
</div>
<script type="text/javascript">
function init() {
var stats = initStats();
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000);
var webGLRenderer = new THREE.WebGLRenderer();
webGLRenderer.setClearColor(new THREE.Color(0x000000, 1.0));
webGLRenderer.setSize(window.innerWidth, window.innerHeight);
camera.position.x = 20;
camera.position.y = 0;
camera.position.z = 150;
document.getElementById("WebGL-output").appendChild(webGLRenderer.domElement);
// 获取画布的纹理
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.beginPath();
ctx.moveTo(83, 116);
ctx.lineTo(83, 102);
ctx.bezierCurveTo(83, 94, 89, 88, 97, 88);
ctx.bezierCurveTo(105, 88, 111, 94, 111, 102);
ctx.lineTo(111, 116);
ctx.lineTo(106.333, 111.333);
ctx.lineTo(101.666, 116);
ctx.lineTo(97, 111.333);
ctx.lineTo(92.333, 116);
ctx.lineTo(87.666, 111.333);
ctx.lineTo(83, 116);
ctx.fill();
// the eyes
ctx.fillStyle = "white";
ctx.beginPath();
ctx.moveTo(91, 96);
ctx.bezierCurveTo(88, 96, 87, 99, 87, 101);
ctx.bezierCurveTo(87, 103, 88, 106, 91, 106);
ctx.bezierCurveTo(94, 106, 95, 103, 95, 101);
ctx.bezierCurveTo(95, 99, 94, 96, 91, 96);
ctx.moveTo(103, 96);
ctx.bezierCurveTo(100, 96, 99, 99, 99, 101);
ctx.bezierCurveTo(99, 103, 100, 106, 103, 106);
ctx.bezierCurveTo(106, 106, 107, 103, 107, 101);
ctx.bezierCurveTo(107, 99, 106, 96, 103, 96);
ctx.fill();
// the pupils
ctx.fillStyle = "blue";
ctx.beginPath();
ctx.arc(101, 102, 2, 0, Math.PI * 2, true);
ctx.fill();
ctx.beginPath();
ctx.arc(89, 102, 2, 0, Math.PI * 2, true);
ctx.fill();
var texture = new THREE.Texture(canvas);
texture.needsUpdate = true;
return texture;
};
var cloud;
var controls = new function () {
this.size = 15;
this.transparent = true;
this.opacity = 0.6;
this.color = 0xffffff;
this.rotateSystem = true;
this.sizeAttenuation = true;
this.redraw = function () {
if (scene.getObjectByName("pointcloud")) {
scene.remove(scene.getObjectByName("pointcloud"));
}
createPointCloud(controls.size, controls.transparent, controls.opacity, controls.sizeAttenuation, controls.color);
};
};
var gui = new dat.GUI();
gui.add(controls, 'size', 0, 20).onChange(controls.redraw);
gui.add(controls, 'transparent').onChange(controls.redraw);
gui.add(controls, 'opacity', 0, 1).onChange(controls.redraw);
gui.addColor(controls, 'color').onChange(controls.redraw);
gui.add(controls, 'sizeAttenuation').onChange(controls.redraw);
gui.add(controls, 'rotateSystem');
controls.redraw();
render();
function createPointCloud(size, transparent, opacity, sizeAttenuation, color) {
var geom = new THREE.Geometry();
var material = new THREE.PointCloudMaterial({
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);
}
cloud = new THREE.PointCloud(geom, material);
cloud.name = 'pointcloud';
// 渲染之前所以粒子按z轴排序,解决部分粒子交叠问题,但是注意会影响性能。
cloud.sortParticles = true;
// 开启后摄像机可见范围外的粒子不会渲染,必要时,使用可以提高性能和帧率。
cloud.FrustumCulled = true;
scene.add(cloud);
}
var step = 0;
function render() {
stats.update();
if (controls.rotateSystem) {
step += 0.01;
cloud.rotation.x = step;
cloud.rotation.z = step;
}
requestAnimationFrame(render);
webGLRenderer.render(scene, camera);
}
function initStats() {
var stats = new Stats();
stats.setMode(0); // 0: fps, 1: ms
stats.domElement.style.position = 'absolute';
stats.domElement.style.left = '0px';
stats.domElement.style.top = '0px';
document.getElementById("Stats-output").appendChild(stats.domElement);
return stats;
}
}
window.onload = init;
</script>
</body>
</html>
2. 使用纹理样式化粒子
前面使用HTML5画布输出纹理还是显得麻烦,我们还有一种更直接的方式,你可以使用THREE.ImageUtils.loadTexture()
函数将图像加载为THREE.Texture
,然后赋值给材质的map属性。
注意图片应该是正方形的,并且尺寸最好是2的幂,背景选择黑色(能够更正确地融合)。
<!-- chapter-07-04.html -->
<!DOCTYPE html>
<html>
<head>
<title>Particles - Rainy scene</title>
<script type="text/javascript" src="../libs/three.js"></script>
<script type="text/javascript" src="../libs/stats.js"></script>
<script type="text/javascript" src="../libs/dat.gui.js"></script>
<style>
body {
margin: 0;
overflow: hidden;
background-color: #000000;
}
</style>
</head>
<body>
<div id="Stats-output">
</div>
<div id="WebGL-output">
</div>
<script type="text/javascript">
function init() {
var stats = initStats();
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 200);
var webGLRenderer = new THREE.WebGLRenderer();
webGLRenderer.setClearColor(new THREE.Color(0x000000, 1.0));
webGLRenderer.setSize(window.innerWidth, window.innerHeight);
camera.position.x = 20;
camera.position.y = 40;
camera.position.z = 110;
camera.lookAt(new THREE.Vector3(20, 30, 0));
document.getElementById("WebGL-output").appendChild(webGLRenderer.domElement);
var cloud;
var controls = new function () {
this.size = 3;
this.transparent = true;
this.opacity = 0.6;
this.color = 0xffffff;
this.sizeAttenuation = true;
this.redraw = function () {
scene.remove(scene.getObjectByName("particles1"));
scene.remove(scene.getObjectByName("particles2"));
createPointCloud(controls.size, controls.transparent, controls.opacity, controls.sizeAttenuation, controls.color);
};
};
var gui = new dat.GUI();
gui.add(controls, 'size', 0, 20).onChange(controls.redraw);
gui.add(controls, 'transparent').onChange(controls.redraw);
gui.add(controls, 'opacity', 0, 1).onChange(controls.redraw);
gui.addColor(controls, 'color').onChange(controls.redraw);
gui.add(controls, 'sizeAttenuation').onChange(controls.redraw);
controls.redraw();
render();
function createPointCloud(size, transparent, opacity, sizeAttenuation, color) {
// 加载本地图片作为粒子纹理
var texture = THREE.ImageUtils.loadTexture("../assets/textures/particles/raindrop-3.png");
var geom = new THREE.Geometry();
var material = new THREE.ParticleBasicMaterial({
size: size,
transparent: transparent,
opacity: opacity,
map: texture,
blending: THREE.AdditiveBlending,
sizeAttenuation: sizeAttenuation,
color: color
});
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;
geom.vertices.push(particle);
}
cloud = new THREE.ParticleSystem(geom, material);
cloud.sortParticles = true;
scene.add(cloud);
}
function render() {
stats.update();
// 模拟下雨效果
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;
});
requestAnimationFrame(render);
webGLRenderer.render(scene, camera);
}
function initStats() {
var stats = new Stats();
stats.setMode(0); // 0: fps, 1: ms
stats.domElement.style.position = 'absolute';
stats.domElement.style.left = '0px';
stats.domElement.style.top = '0px';
document.getElementById("Stats-output").appendChild(stats.domElement);
return stats;
}
}
window.onload = init;
</script>
</body>
</html>
使用精灵贴图
精灵贴图是很多小精灵图片打包在一张图片上,一次加载这张大图后,使用时通过纹理的偏移来选取需要的小图片。
这个示例展示了创建两个场景使用不同摄像机(游戏里场景和UI界面就可以这样使用)
<!-- chapter-07-05.html -->
<!DOCTYPE html>
<html>
<head>
<title>Particles - Sprites</title>
<script type="text/javascript" src="../libs/three.js"></script>
<script type="text/javascript" src="../libs/stats.js"></script>
<script type="text/javascript" src="../libs/dat.gui.js"></script>
<style>
body {
margin: 0;
overflow: hidden;
background-color: #000000;
}
</style>
</head>
<body>
<div id="Stats-output">
</div>
<div id="WebGL-output">
</div>
<script type="text/javascript">
function init() {
var stats = initStats();
// 透视相机场景和正交相机场景
var scene = new THREE.Scene();
var sceneOrtho = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 250);
var cameraOrtho = new THREE.OrthographicCamera(0, window.innerWidth, window.innerHeight, 0, -10, 10);
var webGLRenderer = new THREE.WebGLRenderer();
webGLRenderer.setClearColor(new THREE.Color(0x000000, 1.0));
webGLRenderer.setSize(window.innerWidth, window.innerHeight);
camera.position.x = 0;
camera.position.y = 0;
camera.position.z = 50;
document.getElementById("WebGL-output").appendChild(webGLRenderer.domElement);
// 球体
var material = new THREE.MeshNormalMaterial();
var geom = new THREE.SphereGeometry(15, 20, 20);
var mesh = new THREE.Mesh(geom, material);
scene.add(mesh);
// 获取精灵贴图纹理
var getTexture = function () {
var texture = new THREE.ImageUtils.loadTexture("../assets/textures/particles/sprite-sheet.png");
return texture;
};
var controls = new function () {
this.size = 150;
this.sprite = 0;
this.transparent = true;
this.opacity = 0.6;
this.color = 0xffffff;
this.rotateSystem = true;
this.redraw = function () {
sceneOrtho.children.forEach(function (child) {
if (child instanceof THREE.Sprite) sceneOrtho.remove(child);
});
createSprite(controls.size, controls.transparent, controls.opacity, controls.color, controls.sprite);
};
};
var gui = new dat.GUI();
gui.add(controls, 'sprite', 0, 4).step(1).onChange(controls.redraw);
gui.add(controls, 'size', 0, 120).onChange(controls.redraw);
gui.add(controls, 'transparent').onChange(controls.redraw);
gui.add(controls, 'opacity', 0, 1).onChange(controls.redraw);
gui.addColor(controls, 'color').onChange(controls.redraw);
controls.redraw();
render();
function createSprite(size, transparent, opacity, color, spriteNumber) {
var spriteMaterial = new THREE.SpriteMaterial({
opacity: opacity,
color: color,
transparent: transparent,
map: getTexture()
}
);
// 通过map的offset/repeat选择精灵贴图里的精灵图片,范围0~1
spriteMaterial.map.offset = new THREE.Vector2(0.2 * spriteNumber, 0);
spriteMaterial.map.repeat = new THREE.Vector2(1 / 5, 1);
var sprite = new THREE.Sprite(spriteMaterial);
sprite.scale.set(size, size, size);
sprite.position.set(100, 50, -10);
sprite.velocityX = 5; // 精灵x轴移动速度因子
sceneOrtho.add(sprite); // 小人精灵添加到正交场景中
}
var step = 0;
function render() {
stats.update();
// 透视相机y轴运动
camera.position.y = Math.sin(step += 0.01) * 20;
// 正交相机里的精灵x轴左右移动
sceneOrtho.children.forEach(function (e) {
if (e instanceof THREE.Sprite) {
// move the sprite along the bottom
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; // 关闭自动清理,不然在sceneOrtho render时会清理scene的,那么就会导致看不到scene里的球体了
webGLRenderer.render(sceneOrtho, cameraOrtho);
}
function initStats() {
var stats = new Stats();
stats.setMode(0); // 0: fps, 1: ms
stats.domElement.style.position = 'absolute';
stats.domElement.style.left = '0px';
stats.domElement.style.top = '0px';
document.getElementById("Stats-output").appendChild(stats.domElement);
return stats;
}
}
window.onload = init;
</script>
</body>
</html>
从高级几何体创建THREE.PointCloud
前面我们知道了THREE.PointCloud是基于几何体的顶点来渲染每个粒子的。前面我们都是使用的自定义顶点,其实也可以直接用高级几何体创建点云。
示例:使用上节的环状扭结几何体创建粒子
<!-- chapter-07-06.html -->
<!DOCTYPE html>
<html>
<head>
<title>3D Torusknot</title>
<script type="text/javascript" src="../libs/three.js"></script>
<script type="text/javascript" src="../libs/stats.js"></script>
<script type="text/javascript" src="../libs/dat.gui.js"></script>
<style>
body {
margin: 0;
overflow: hidden;
}
</style>
</head>
<body>
<div id="Stats-output">
</div>
<div id="WebGL-output">
</div>
<script type="text/javascript">
function init() {
var stats = initStats();
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000);
var webGLRenderer = new THREE.WebGLRenderer();
webGLRenderer.setClearColor(new THREE.Color(0x000000, 1.0));
webGLRenderer.setSize(window.innerWidth, window.innerHeight);
webGLRenderer.shadowMapEnabled = true;
camera.position.x = -30;
camera.position.y = 40;
camera.position.z = 50;
camera.lookAt(new THREE.Vector3(10, 0, 0));
document.getElementById("WebGL-output").appendChild(webGLRenderer.domElement);
var knot;
var controls = new function () {
this.radius = 13;
this.tube = 1.7;
this.radialSegments = 156;
this.tubularSegments = 12;
this.p = 5;
this.q = 4;
this.heightScale = 3.5;
this.asParticles = false;
this.rotate = false;
this.redraw = function () {
if (knot) scene.remove(knot);
// 创建环状扭结几何体
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);
if (controls.asParticles) {
// 创建点云对象
knot = createPointCloud(geom);
} else {
// 创建网格对象
knot = createMesh(geom);
}
scene.add(knot);
};
};
var gui = new dat.GUI();
gui.add(controls, 'radius', 0, 40).onChange(controls.redraw);
gui.add(controls, 'tube', 0, 40).onChange(controls.redraw);
gui.add(controls, 'radialSegments', 0, 400).step(1).onChange(controls.redraw);
gui.add(controls, 'tubularSegments', 1, 20).step(1).onChange(controls.redraw);
gui.add(controls, 'p', 1, 10).step(1).onChange(controls.redraw);
gui.add(controls, 'q', 1, 15).step(1).onChange(controls.redraw);
gui.add(controls, 'heightScale', 0, 5).onChange(controls.redraw);
gui.add(controls, 'asParticles').onChange(controls.redraw);
gui.add(controls, 'rotate').onChange(controls.redraw);
controls.redraw();
render();
// 获取当前画布的纹理信息
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 createPointCloud(geom) {
var material = new THREE.PointCloudMaterial({
color: 0xffffff,
size: 3,
transparent: true,
blending: THREE.AdditiveBlending,
map: generateSprite() // 使用HTML5画布的纹理
});
var cloud = new THREE.PointCloud(geom, material);
cloud.sortParticles = true;
return cloud;
}
function createMesh(geom) {
var meshMaterial = new THREE.MeshNormalMaterial({});
meshMaterial.side = THREE.DoubleSide;
var mesh = THREE.SceneUtils.createMultiMaterialObject(geom, [meshMaterial]);
return mesh;
}
function render() {
stats.update();
if (controls.rotate) {
knot.rotation.y += 0.01;
}
requestAnimationFrame(render);
webGLRenderer.render(scene, camera);
}
function initStats() {
var stats = new Stats();
stats.setMode(0); // 0: fps, 1: ms
stats.domElement.style.position = 'absolute';
stats.domElement.style.left = '0px';
stats.domElement.style.top = '0px';
document.getElementById("Stats-output").appendChild(stats.domElement);
return stats;
}
}
window.onload = init;
</script>
</body>
</html>