点和精灵
在前面的章节中,我们讨论了Three.js提供的最重要的概念、对象和API。
在这 一章中,我们将深入研究直到现在为止我们跳过的唯一概念:点和精灵。
通过THREE.Points(有时也称为精灵),
可以很容易地创建许多始终朝向摄像机的小矩形,
您可以使用它们来模拟雨、雪、烟等有趣的效果。
例如,您可以将单个几何体渲染为一组点,并分别控制这些点。
在本章中,我们将探讨Three.js提供的各种与点和精灵相关的功能。
更具体地说,在本章中,我们将讨论以下主题:
- 使用
THREE.SpriteMaterial和THREE.PointsMaterial创建和样式化粒子 - 使用
THREE.Points创建一组点 - 使用画布分别为每个点设置样式
- 使用纹理为单个点设置样式
- 动画
THREE.Points对象 - 从现有几何体创建
THREE.Points对象
在本章中使用的一些名称的简要说明
在较新的Three.js版本中,与点相关的对象的名称已经多次更改。
THREE.Points对象以前的名称是THREE.PointCloud,
在更旧的版本中它被称为THREE.ParticleSystem。
THREE.Sprite以前被称为THREE.Particle,而材质也经历了几次更名。
因此,如果您看到在线示例使用这些旧名称,请记住它们谈论的是相同的概念。
让我们开始探讨粒子是什么以及如何创建它。
理解点和精灵
和大多数新概念一样,我们将从一个例子开始。
在本章的源代码中,您将找到一个名为sprite.html的例子。
打开此示例时,您将看到一个极简的场景,包含一个简单的彩色正方形:
您可以使用鼠标在此 场景中旋转。 您将注意到的一点是,无论您如何查看正方形,它始终看起来相同。 例如,以下截图显示了从不同位置查看相同场景的视图:
如您所见,精灵仍然朝向摄像机倾斜,您无法查看其背后。 您可以将精灵视为始终朝向摄像机的二维平面。 如果创建一个没有任何属性的精灵,它们将呈现为小的白色二维正方形。 要创建精灵,我们只需要提供一个材质:
const material = new THREE.SpriteMaterial({ size: 0.1, color: 0xff0000 })
const sprite = new THREE.Sprite(material)
sprite.position.copy(new THREE.Vector3(1, 1, 1))
您可以使用THREE.SpriteMaterial配置精灵的外观:
color:这是精灵的颜色。默认颜色为白色。sizeAttenuation:如果设置为false,精灵的大小将不受其离摄像机的位置影响。如果设置为true,大小将基于与摄像机的距离。默认值为true。请注意,仅在使用THREE.PerspectiveCamera时,此选项才起作用。对于THREE.OrthographicCamera,它始终表现得像false。map:使用此属性,您可以将纹理应用于精灵。您可以使它们看起来像雪花,例如。此属性未在此示例中显示,但将在本章后面的使用纹理样式化粒子部分中解释。opacity:与transparent属性一起,设置精灵的不透明 度。默认值为1(完全不透明)。transparent:如果设置为true,则将使用由opacity属性设置的精灵的不透明度进行渲染。默认值为false。blending:在渲染精灵时使用的混合模式。
请注意,THREE.SpriteMaterial扩展自基本的THREE.Material对象,
因此该对象的所有属性也可用于THREE.SpriteMaterial。
在继续研究更有趣的THREE.Points对象之前
,让我们更仔细地查看THREE.Sprite对象。
THREE.Sprite对象与THREE.Mesh一样扩展自THREE.Object3D对象。
这意味着您可以在THREE.Sprite上使用大多数从THREE.Mesh了解的属性和函数。
您可以使用position属性设置其位置,使用scale属性缩放它,
并使用translate属性沿其轴移动它。
使用THREE.Sprite,您可以很容易地创建一组对象并在场景中移动它们。
当您使用少量对象时,这很有效,但是当您想要使用大量THREE.Sprite对象时,
您很快就会遇到性能问题。这是因为每个对象都需要被Three.js单独管理。
Three.js提供了使用THREE.Points对象处理大量精灵的替代方法。
使用THREE.Points,Three.js不必单独管理许多个体的THREE.Sprite对象,
而只需管理THREE.Points实例。
这将使Three.js能够优化如何绘制精灵,并将导致更好的性能。
以下截图显示了使用THREE.Points对象渲染的几个精灵:
要创建THREE.Points对象,我们需要为其提供THREE.BufferGeometry。
对于前面的截图,我们可以这样创建THREE.BufferGeometry:
const createPoints = () => {
const points = []
for (let x = -15; x < 15; x++) {
for (let y = -10; y < 10; y++) {
let point = new THREE.Vector3(x / 4, y / 4, 0)
points.push(point)
}
}
const colors = new Float32Array(points.length * 3)
points.forEach((e, i) => {
const c = new THREE.Color(Math.random() * 0xffffff)
colors[i * 3] = c.r
colors[i * 3 + 1] = c.g
colors[i * 3 + 2] = c.b
})
const geom = new THREE.BufferGeometry().setFromPoints(points)
geom.setAttribute('color', new THREE.BufferAttribute(colors, 3, true))
return geom
}
const material = new THREE.PointsMaterial({ size: 0.1, vertexColors: true, color: 0xffffff })
const points = new THREE.Points(createPoints(), material)
如您从此代码片段中看到的,
首先,我们创建了一个THREE.Vector3对象数组 - 为我们想要创建精灵的每个位置一个。
此外,我们在THREE.BufferGeometry上设置了color属性,用于为每个精灵着色。
使用THREE.BufferGeometry和THREE.PointsMaterial的实例,
我们可以创建THREE.Points对象。
THREE.PointsMaterial的属性基本上与THREE.SpriteMaterial的属性相同:
color:这是点的颜色。默认颜色为0xffffff。sizeAttenuation:如果设置为false,所有点的大小将相同,无论它们离摄像机有多远。如果设置为true,大小将基于与摄像机的距离。默认值为true。map:使用此属性,您可以将纹理应用于点。您可以使它们看起来像雪花,例如。此属性未在此示例中显示,但将在本章后面的使用纹理样式化粒子部分中解释。opacity:与transparent属性一起,设置精灵的不透明度。默认值为1(完全不透明)。transparent:如果设置为true,则将使用由opacity属性设置的精灵的不透明度进行渲染。默认值为false。blending:在渲染精灵时使用的混合模式。vertexColors:通常,THREE.Points中的所有点具有相同的颜色。如果将此属性设置为true,并且在几何上已经设置了颜色的缓冲属性,则每个点将从该数组中获取颜色。默认值为false。
与往常一样,您可以使用每个示例右侧的菜单调整这些属性。 到目前为止,我们只渲染了粒子作为小正方形,这是默认行为。 然而,有两种额外的方式可以样式化粒子,我们将在下一节中展示。
使用纹理样式化粒子
在本节中,我们将讨论改变精灵外观的两种方式:
- 使用HTML画布绘制图像并为每个精灵显示
- 加载外部图像文件以定义每个精灵的外观
让我们首先自己绘制图像。
在画布上绘制一张图像
在THREE.PointsMaterial的属性中,我们提到了map属性。
使用map属性,我们可以为单个点加载纹理。
在Three.js中,这个纹理也可以是来自HTML5画布的输出。
在看代码之前,让我们看一个例子(canvastexture.js):
在这里,您可以看到我们在屏幕上有一组类似吃豆人的幽灵。 这使用了我们之前在"理解点和精灵"部分看到的相同方法。 这一次,我们不显示一个简单的正方形,而是显示一张图像。 要创建这个纹理,我们可以使用以下代码:
const createGhostTexture = () => {
const canvas = document.createElement('canvas')
canvas.width = 32
canvas.height = 32
const 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.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.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()
const texture = new THREE.Texture(canvas)
texture.needsUpdate = true
return texture
}
如您所见,首先,我们创建了一个HTML画布,在上面使用各种ctx函数开始绘制。
最后,我们通过调用new THREE.Texture(canvas)将此画布转换为
THREE.Texture,这会产生一个我们可以用于精灵的纹理。
记得将texture.needsUpdate设置为true,
这会 触发Three.js将实际画布数据加载到纹理中。
现在我们有了一个纹理,我们可以用它来创建THREE.PointsMaterial,
就像我们在"理解点和精灵"部分中所做的那样:
const createPoints = () => {
const points = []
const range = 15
for (let i = 0; i < 15000; i++) {
let particle = new THREE.Vector3(
Math.random() * range - range / 2,
Math.random() * range - range / 2,
Math.random() * range - range / 2
)
points.push(particle)
}
const colors = new Float32Array(points.length * 3)
points.forEach((e, i) => {
const c = new THREE.Color(Math.random() * 0xffffff)
colors[i * 3] = c.r
colors[i * 3 + 1] = c.g
colors[i * 3 + 2] = c.b
})
const geom = new THREE.BufferGeometry().setFromPoints(points)
geom.setAttribute('color', new THREE.BufferAttribute(colors, 3, true))
return geom
}
const material = new THREE.PointsMaterial({ size: 0.1, vertexColors: true, color: 0xffffff, map: createGhostTexture() })
const points = new THREE.Points(createPoints(), material)
如您所见,我们为此示例创建了15000个点,并在指定的范围内随机放置它们。
您可能会注意到,即使打开透明度,一些精灵似乎重叠在其他精灵上。
这是因为Three.js不会根据它们的z-index对精灵进行排序,
因此在渲染期间,它无法正确确定哪个在另一个之前。
有两种方法可以解决这个问题:您可以关闭depthWrite,
或者您可以使用alphaTest属性(从0.5开始是一个不错的起点)进行调整。
如果缩小视图,您将看到15,000个单独的精灵:
令人惊奇的是,即使有100万个点,
一切仍然非常顺利渲染(当然,这取决于您在这些示例上运行的硬件)。
在下一节中,我们将从外部图像加载一些纹理,并使用它们来代替自己绘制纹理。
使用纹理样式化粒子
在本节中,我们将探讨改变精灵外观的两种方式:
- 使用HTML画布绘制图像并为每个精灵显示
- 加载外部图像文件以定义每个精灵的外观
让我们首先自己绘制图像。
在THREE.PointsMaterial的属性中,我们提到了map属性。
使用map属性,我们可以为单个点加载纹理。
在Three.js中,这个纹理也可以是来自HTML5画布的输出。
在看代码之前,让我们看一个例子(canvastexture.js):
// 请参阅上文以查看完整的代码
// 创建幽灵纹理的函数
const createGhostTexture = () => {
const canvas = document.createElement('canvas')
canvas.width = 32
canvas.height = 32
const ctx = canvas.getContext('2d')
// 绘制幽灵的身体、眼睛等
// ...
const texture = new THREE.Texture(canvas)
texture.needsUpdate = true
return texture
}
如您所见,首先,我们创建了一个HTML画布,然后使用各种ctx函数在其上绘制。
最后,通过调用new THREE.Texture(canvas)将此画布转换为THREE.Texture,
这会产生一个我们可以用于精灵的纹理。
记得将texture.needsUpdate设置为true,
这会触发Three.js将实际画布数据加载到纹理中。
现在我们有了一个纹理,我们可以用它来创建THREE.PointsMaterial,
就像我们在"理解点和精灵"部分中所做的那样:
// 请参阅上文以查看完整的代码
// 创建精灵的函数
const createPoints = () => {
const points = []
const range = 15
for (let i = 0; i < 15000; i++) {
let particle = new THREE.Vector3(
Math.random() * range - range / 2,
Math.random() * range - range / 2,
Math.random() * range - range / 2
)
points.push(particle)
}
const colors = new Float32Array(points.length * 3)
// 为每个精灵设置随机颜色
// ...
const geom = new THREE.BufferGeometry().setFromPoints(points)
geom.setAttribute('color', new THREE.BufferAttribute(colors, 3, true))
return geom
}
// 创建精灵材质的实例
const material = new THREE.PointsMaterial({
size: 0.1,
vertexColors: true,
color: 0xffffff,
map: createGhostTexture() // 使用自定义绘制的纹理
})
// 创建精灵对象
const points = new THREE.Points(createPoints(), material)
如您所见,我们为此示例创建了15000个点,并在指定的范围内随机放置它们。
您可能会注意到,即使打开透明度,一些精灵似乎重叠在其他精灵上。
这是因为Three.js不会根据它们的z-index对精灵进行排序,因此在渲染期间,
它无法正确确定哪个在另一个之前。
有两种方法可以解决这个问题:您 可以关闭depthWrite,
或者您可以使用alphaTest属性(从0.5开始是一个不错的起点)进行调整。
如果缩小视图,您将看到15,000个单独的精灵。
令人惊奇的是,即使有100万个点,
一切仍然非常顺利渲染(当然,这取决于您在这些示例上运行的硬件)。
在下一节中,我们将从外部图像加载一些纹理,并使用它们来代替自己绘制纹理。
使用纹理样式化粒子
在“在画布上绘制图像”部分的示例中,我们看到了如何使用HTML画布样式化THREE.Points。
由于您可以绘制任何您想要的东西,甚至加载外部图像,您可以使用这种方法为粒子系统添加各种样式。
然而,有一种更直接的方法可以使用图像来样式化您的粒子:
您可以使用THREE.TextureLoader().load()函数将图像加载为THREE.Texture对象。
然后,这个THREE.Texture对象可以分配给材质的map属性。
在本节中,我们将展示两个示例,并解释如何创建它们。
这两个示例都使用图像作为粒子的纹理。
在第一个示例中,我们将创建模拟雨的效果(rain.html):
首先,我们需要获取一个代表雨滴的纹理。
您可以在assets/textures/particles文件夹中找到一些示例。
在即将到来的章节中,我们将解释纹理的所有详细信息和要求。
现在,您只需要知道纹理应该是正方形的,
最好是2的幂次方(例如,64 x 64、128 x 128或256 x 256)。
对于这 个示例,我们将使用这个纹理:
这个纹理是一个简单的透明图像,显示了雨滴的形状和颜色。
在我们可以在THREE.PointsMaterial中使用这个纹理之前,我们需要加载它。
这可以通过以下代码完成:
const texture = new THREE.TextureLoader().load("../../assets/textures/particles/raindrop-3t.png");
使用这行代码,Three.js将加载纹理,我们可以在我们的材质中使用它。 对于这个示例,我们定义了材质如下:
const material = new THREE.PointsMaterial({
size: 0.1,
vertexColors: false,
color: 0xffffff,
map: texture,
transparent: true,
opacity: 0.8,
alphaTest: 0.01
});
在这一章中,我们已经讨论了所有这些属性。
在这里需要理解的主要事情是map属性指向我们使用THREE.TextureLoader.load函数加载的纹理。
请注意,我们再次使用了alphaTest属性,以确保两个精灵在彼此前移动时没有奇怪的伪影。
这样就完成了对THREE.Points对象的样式化。
当您打开这个示例时,您还会看到点本身在移动。
这样做非常简单。每个点都表示为用于创建THREE.Points对象的几何体中的顶点。
让我们看看如何为这个THREE.Points对象添加点:
const count = 25000;
const range = 20;
const createPoints = () => {
const points = [];
for (let i = 0; i < count; i++) {
let particle = new THREE.Vector3(
Math.random() * range - range / 2,
Math.random() * range - range / 2,
Math.random() * range - range / 1.5
);
points.push(particle);
}
const velocityArray = new Float32Array(count * 2);
for (let i = 0; i < count * 2; i += 2) {
velocityArray[i] = ((Math.random() - 0.5) / 5) * 0.1;
velocityArray[i + 1] = (Math.random() / 5) * 0.1 + 0.01;
}
const geom = new THREE.BufferGeometry().setFromPoints(points);
geom.setAttribute('velocity', new THREE.BufferAttribute(velocityArray, 2));
return geom;
};
const points = new THREE.Points(createPoints(), material);
这与本章中之前示例中看到的并没有太大的不同。
这里,我们为每个粒子添加了另一个名为velocity的属性。
该属性包含两个值:velocityX和velocityY。
第一个定义了粒子(雨滴)水平移动的方式,而第二个定义了雨滴下落的速度。
现在,每个雨滴都有自己的速度,我们可以在渲染循环中移动单个粒子:
const positionArray = points.geometry.attributes.position.array;
const velocityArray = points.geometry.attributes.velocity.array;
for (let i = 0; i < points.geometry.attributes.position.count; i++) {
const velocityX = velocityArray[i * 2];
const velocityY = velocityArray[i * 2 + 1];
positionArray[i * 3] += velocityX;
positionArray[i * 3 + 1] -= velocityY;
if (positionArray[i * 3] <= -(range / 2) || positionArray[i * 3] >= range / 2)
positionArray[i * 3] = positionArray[i * 3] * -1;
if (positionArray[i * 3 + 1] <= -(range / 2) || positionArray[i * 3 + 1] >= range / 2)
positionArray[i * 3 + 1] = positionArray[i * 3 + 1] * -1;
}
points.geometry.attributes.position.needsUpdate = true;
在这段代码中,我们从用于创建THREE.Points的几何体中获取所有顶点(粒子)。
对于每个粒子,我们取velocityX和velocityY,并使用它们来改变粒子的当前位置。
然后,我们确保粒子保持在我们定义的范围内。如果v.y位置低于0,我们将雨滴添加回顶部;
如果v.x位置达到任何边缘,我们通过反转水平速度使其弹回。
最后,我们需要告诉Three.js我们在bufferGeometry中改变了一些内容,
以便在下次渲染时它知道正确的值。
让我们看另一个例子。这次,我们不会下雨,而是会下雪。
此外,我们将不仅使用单一纹理 - 我们将使用三个单独的图像(取自Three.js示例)。
让我们先看看结果(snow.html):
在上述截图中,如果仔细看,您会看到我们没有仅使用单一图像作为纹理,而是使用了具有透明背景的多个图像。
您可能想知道我们是如何做到这一点的。正如您可能记得的,对于THREE.Points对象,我们只能有一个材质。
如果我们想要有多个材质,我们只需创建多个THREE.Points实例,如下所示:
const texture1 = new THREE.TextureLoader().load('/assets/textures/particles/snowflake4_t.png');
const texture2 = new THREE.TextureLoader().load('/assets/textures/particles/snowflake2_t.png');
const texture3 = new THREE.TextureLoader().load('/assets/textures/particles/snowflake3_t.png');
const baseProps = {
size: 0.1,
color: 0xffffff,
transparent: true,
opacity: 0.5,
blending: THREE.AdditiveBlending,
depthTest: false,
alphaTest: 0.01
};
const material1 = new THREE.PointsMaterial({
...baseProps,
map: texture1
});
const material2 = new THREE.PointsMaterial({
...baseProps,
map: texture2
});
const material3 = new THREE.PointsMaterial({
...baseProps,
map: texture3
});
const points1 = new THREE.Points(createPoints(), material1);
const points2 = new THREE.Points(createPoints(), material2);
const points3 = new THREE.Points(createPoints(), material3);
在这段代码片段中,您可以看到我们创建了三个不同的THREE.Points实例,每个都有自己的材质。
为了移动雪花,我们使用了与雨相同的方法,所以我们在这里不显示createPoint和渲染循环的详细信息。
这里需要注意的一点是,可以有一个THREE.Points实例,其中个别精灵具有不同的纹理。
但是,这将需要一个自定义的片段着色器和您自己的THREE.ShaderMaterial实例。
在我们进入下一节之前,请注意使用THREE.Points是向现有场景添加视觉效果的好方法。
例如,我们在前面示例中看到 的雪花可以迅速将标准场景变成雪景:
另一种使用精灵的方法是在现有场景的顶部创建一个简单的2D提示显示(HUD)。 我们将在下一节中探讨如何实现这一点。
使用精灵图工作
在本章的开始,我们使用了THREE.Sprite对象来渲染单个点。
这些精灵位于3D世界的某个位置,它们的大小基于与摄像机的距离(有时也称为广告牌效果)。
在本节中,我们将展示THREE.Sprite对象的另一种用法:
我们将向您展示如何使用THREE.Sprite创建类似于HUD的图层,用于您的3D内容,
使用额外的THREE.OrthographicCamera实例和一个额外的THREE.Scene。
我们还将向您展示如何使用精灵图为THREE.Sprite对象选择图像。
作为示例,我们将创建一个简单的THREE.Sprite对象,它从屏幕左侧移动到右侧。
在背景中,我们将渲染一个带有相机的3D场景,您可以移动相机以说明THREE.Sprite对象独立于相机移动。
以下截图显示了我们将为第一个示例创建的内容(spritemap.html):
如果您在浏览器中打开此示例,您会看到一个类似于Pac-Man鬼怪的精灵在屏幕上移动,
每当它碰到右边缘时,颜色和形状都会发生变化。
我们要做的第一件事是看看如何创建
THREE.OrthographicCamera以及一个单独的场景来渲染这个THREE.Sprite:
const sceneOrtho = new THREE.Scene();
sceneOrtho.backgroundColor = new THREE.Color(0x000000);
const cameraOrtho = new THREE.OrthographicCamera(0, window.innerWidth, window.innerHeight, 0, -10, 10);
接下来,让我们看看如何构建THREE.Sprite对象以及如何加载精灵可以采用的各种形状:
const getTexture = () => {
const texture = new THREE.TextureLoader().load('/assets/textures/particles/sprite-sheet.png');
return texture;
}
const createSprite = (size, transparent, opacity, spriteNumber) => {
const spriteMaterial = new THREE.SpriteMaterial({
opacity: opacity,
color: 0xffffff,
transparent: transparent,
map: getTexture()
});
// 我们有1行,每行有五个精灵
spriteMaterial.map.offset = new THREE.Vector2(0.2 * spriteNumber, 0);
spriteMaterial.map.repeat = new THREE.Vector2(1 / 5, 1);
// 确保对象始终呈现在最前面
spriteMaterial.depthTest = false;
const sprite = new THREE.Sprite(spriteMaterial);
sprite.scale.set(size, size, size);
sprite.position.set(100, 50, -10);
sprite.velocityX = 5;
sprite.name = 'Sprite';
sceneOrtho.add(sprite);
}
在getTexture()函数中,我们加载了一个纹理。
但是,与其为每个鬼加载五张不同的图像,我们加载了一个包含所有精灵的单一纹理(也称为精灵图)。
我们用作纹理的图像如下:
使用map.offset和map.repeat属性,我们可以选择在屏幕上显示的正确精灵。
通过map.offset属性,我们确定了我们加载的纹理在x轴(u)和y轴(v)上的偏移。
这些属性的范围从0到1。
在我们的示例中,如果我们想选择第三个幽灵,
我们必须将u偏移(x轴)设置为0.4,因为我们只有一行,我们不需要更改v偏移(y轴)。
如果我们只设置这个属性,纹理会在屏幕上压缩显示第三、第四和第五个幽灵。
为了只显示一个幽灵,我们需要放大。
我们可以通过将map.repeat属性设置为u值的1/5来实现这一点。
这意味着我们在x轴上(仅限x轴)放大,只显示纹理的20%,这正好是一个幽灵。
最后,我们需要更新渲染函数:
renderer.render(scene, camera);
renderer.autoClear = false;
renderer.render(sceneOrtho, cameraOrtho);
首先,我们使用普通相机和两个网格渲染场景;之后,我们渲染包含我们精灵的场景。 在渲染循环中,我们还切换了一些属性,以在它击中右墙时显示下一个精灵并更改精灵的方向(未显示的代码)。
到目前为止,在本章中,我们主要看了如何从头开始创建精灵和点云。
然而,一个有趣的选项是从现有几何体创建THREE.Points。
从现有几何体创建THREE.Points
正如您可能记得的那样,THREE.Points根据提供的THREE.BufferGeometry的顶点渲染每个点。
这意味着如果我们提供一个复杂的几何体(例如,一个环结或管道),
我们可以基于该特定几何体的顶点创建THREE.Points。
在本章的最后一节中,我们将创建一个环结,
就像我们在第6章“探索高级几何体”中看到的那样,并将其渲染为THREE.Points对象。
我们在第6章中解释了环结,因此在这里我们不会详细介绍。
以下截图显示了示例(points-from-geom.html):
如前面的截图所示,用于生成环结的每个顶点都被用作一个点。 我们可以这样设置:
const texture = new THREE.TextureLoader().load('/assets/textures/particles/glow.png');
const geometry = new THREE.TorusKnotGeometry(2, 0.5, 100, 30, 2, 3);
const material = new THREE.PointsMaterial({
size: 0.2,
vertexColors: false,
color: 0xffffff,
map: texture,
depthWrite: false,
opacity: 0.1,
transparent: true,
blending: THREE.AdditiveBlending
});
const points = new THREE.Points(geometry, material);
如您所见,我们只需创建一个几何体,并将其用作THREE.Points对象的输入。
通过这种方式,我们可以将每个几何体呈现为点对象。
如果使用Three.js模型加载器(例如,glTF模型)加载外部模型,
通常会得到对象的层次结构 - 通常分组在THREE.Group或THREE.Object3D对象中。
在这些情况下,您必须将每个组中的每个几何体转换为THREE.Points对象。
总结
这就是本章的全部内容。我们解释了精灵和点是什么,以及如何使用可用的材质样式化这些对象。
在本章中,您了解了如何直接使用THREE.Sprite,
以及如果要创建大量粒子,应该使用THREE.Points对象。
使用THREE.Points,所有元素共享相同的材质,
您可以为单个粒子更改的唯一属性是将材质的vertexColors属性设置为true,
并在THREE.BufferGeometry的colors数组中提供颜色值,该数组用于创建THREE.Points。
我们还展示了如何通过更改它们的位置轻松地对粒子进行动画处理。
对于单个THREE.Sprite实例和用于创建THREE.Points对象的几何体的顶点,这一点是相同的。
到目前为止,我们已经基于Three.js提供的几何体创建了网格。 这对于简单的模型,如球体和立方体,效果很好,但当您想创建复杂的3D模型时,这不是最佳方法。 对于这些模型,通常会使用3D建模应用程序,如Blender或3D Studio Max。 在下一章中,您将学习如何加载和显示由这些3D建模应用程序创建的模型。