跳到主要内容

探索高级几何体

在第5章《学习使用几何体》中,我们向您展示了Three.js提供的所有基本几何体。 除了这些基本几何体之外,Three.js还提供了一组更高级和专业化的对象。 在本章中,我们将向您展示这些高级几何体的用法:

  • 如何使用高级几何体,例如 THREE.ConvexGeometryTHREE.LatheGeometryTHREE.BoxLineGeometryTHREE.RoundeBoxGeometryTHREE.TeapotGeometryTHREE.TubeGeometry
  • 如何使用THREE.ExtrudeGeometry从2D形状创建3D形状。 我们将从2D SVG图像创建3D形状,并从2D Three.js形状挤出以创建新颖的3D形状。
  • 如果您想自己创建自定义形状,可以继续玩弄我们在前几章中讨论的形状。 然而,Three.js还提供了一个THREE.ParametricGeometry对象。 使用参数几何体,您可以创建具有可更改形状的几何体。
  • 我们还将展示如何使用THREE.TextGeometry创建3D文本效果, 并向您展示在场景中添加2D文本标签时如何使用Troika库。
  • 此外,我们将向您展示如何使用两个辅助几何体, 即THREE.WireframeGeometryTHREE.EdgesGeometry。 这些辅助几何体使您能够查看其他几何体的更多细节。

我们将从这个列表中的第一个开始,即THREE.ConvexGeometry

学习高级几何体

在本节中,我们将查看一些先进的Three.js几何体。 我们将从THREE.ConvexGeometry开始,您可以使用它来创建凸包。

THREE.ConvexGeometry

使用THREE.ConvexGeometry,我们可以从一组点创建凸包。 凸包是包围所有这些点的最小形状。 理解这一点最简单的方法是通过查看示例。如果您打开convex-geometry.html示例, 您将看到一组随机点的凸包。以下截图显示了这个几何体:

在这个例子中,我们生成了一组随机点,并基于这些点创建了THREE.ConvexGeometry。 在例子中,您可以使用右侧菜单中的重绘按钮,它将生成20个新点并绘制凸包。 如果您自己尝试这样做,请启用材质的透明度并将不透明度设置为低于1的水平,以查看用于创建此几何体的点。 在这个例子中,这些点被创建为小的THREE.SphereGeometry对象。

要创建THREE.ConvexGeometry,我们需要一组点。 以下代码片段显示了我们如何做到这一点:

const generatePoints = () => {
const spGroup = new THREE.Object3D();
spGroup.name = 'spGroup';
const points = [];
for (let i = 0; i < 20; i++) {
const randomX = -5 + Math.round(Math.random() * 10);
const randomY = -5 + Math.round(Math.random() * 10);
const randomZ = -5 + Math.round(Math.random() * 10);
points.push(new THREE.Vector3(randomX, randomY, randomZ));
}
const material = new THREE.MeshBasicMaterial({ color: 0xff0000, transparent: false });
points.forEach(function (point) {
const spGeom = new THREE.SphereGeometry(0.04);
const spMesh = new THREE.Mesh(spGeom, material);
spMesh.position.copy(point);
spGroup.add(spMesh);
});
return {
spGroup,
points
};
}

正如您在这段代码片段中看到的,我们创建了20个随机点(THREE.Vector3),并将它们推入数组中。 接下来,我们遍历此数组并创建THREE.SphereGeometry, 其位置设置为这些点之一(position.copy(point))。 所有点都添加到一个组中,这样我们就可以轻松地替换它们一旦进行重绘。 一旦您拥有这组点,从中创建THREE.ConvexGeometry就非常容易,如下面的代码片段所示:

const convexGeometry = new THREE.ConvexGeometry(points);

THREE.ConvexGeometry接受的唯一参数是包含顶点(THREE.Vector3类型)的数组。 请注意,如果要渲染光滑的THREE.ConvexGeometry, 应该调用computeVertexNormals, 正如我们在第2章《构成Three.js应用程序的基本组件》中解释的那样。

接下来的复杂几何体是THREE.LatheGeometry,例如,可以用来创建花瓶形状。

THREE.LatheGeometry

THREE.LatheGeometry允许您从一组形成曲线的点中创建形状。 如果您查看图6.2,您将看到我们创建了一些点(红点), Three.js使用这些点创建THREE.LatheGeometry

再次,理解THREE.LatheGeometry的外观最简单的方法是查看示例。 此几何体显示在lathe-geometry.html中。以下是从示例中截取的屏幕截图, 显示了这个几何体:

在上述截图中,您可以看到用于创建此几何体的点,它们显示为一组小红色球体。 这些点的位置与THREE.LatheGeometry一起传递,以及定义几何体形状的参数。 在查看所有参数之前,让我们先看看用于创建单个点以及THREE.LatheGeometry如何使用这些点的代码:

const generatePoints = () => {
// ...
const points = [];
const height = 0.4;
const count = 25;
for (let i = 0; i < count; i++) {
points.push(new THREE.Vector3((Math.sin(i * 0.4) + Math.cos(i * 0.4)) * height + 3, i / 6, 0));
}
// ...
}

// 使用相同的点创建LatheGeometry
const latheGeometry = new THREE.LatheGeometry(points, segments, phiStart, phiLength);
latheMesh = createMesh(latheGeometry);
scene.add(latheMesh);

在这段JavaScript中,我们可以看到我们生成了25个点, 其x坐标基于正弦和余弦函数的组合,而y坐标基于icount变量。 这样创建了在上述截图中以红点可视化的样条。 基于这些点,我们可以创建THREE.LatheGeometry。 除了顶点数组外,THREE.LatheGeometry还接受其他一些参数。 以下列表解释了这些属性:

  • points:这些是用于生成钟/花瓶形状的样条的点。
  • segments:创建形状时使用的段数。此数值越高,生成的形状就越圆润和平滑。这的默认值为12。
  • phiStart:这决定了生成形状时在圆上的起始位置。这可以在0到2*PI之间取值。默认值为0。
  • phiLength:这定义了形状生成的完整程度。例如,四分之一形状将是0.5PI。默认值为完整的360度或2PI。此形状将从phiStart属性的位置开始。

在第5章中,我们已经看到了BoxGeometry。Three.js还提供了另外两个类似于盒子的几何体, 我们将在下面讨论。

BoxLineGeometry

如果您只想显示轮廓,可以使用THREE.BoxLineGeometry。 此几何体的工作方式与THREE.BoxGeometry完全相同,但它不是渲染实心物体, 而是使用线条渲染盒子(来自box-line-geometry.html):

您可以以与THREE.BoxGeometry相同的方式使用此几何体, 但是与创建THREE.Mesh不同,我们需要创建THREE.LineSegments, 使用可用的线条特定材质之一:

import { BoxLineGeometry } from 'three/examples/jsm/geometries/BoxLineGeometry'

const material = new THREE.LineBasicMaterial({ color: 0x000000 });
const geometry = new BoxLineGeometry(width, height, depth, widthSegments, heightSegments, depthSegments);
const lines = new THREE.LineSegments(geometry, material);
scene.add(lines);

有关可以传递到此几何体的属性的解释,请参阅第5章的THREE.BoxGeometry部分。

Three.js还提供了略微更高级的THREE.BoxGeometry,其中可以具有漂亮的圆角。 您可以使用RoundedBoxGeometry实现这一点。

THREE.RoundedBoxGeometry

这个几何体使用与THREE.BoxGeometry相同的属性,但它还允许您指定角落的圆角程度。 在rounded-box-geometry示例中,您可以看到它的外观:

对于这个几何体,我们可以通过指定widthheightdepth来指定盒子的尺寸。 除了这些属性,此几何体提供了另外两个:

  • radius(半径):这是圆角的大小。该值越高,角落就越圆。
  • segments(段数):此属性定义角落的详细程度。 如果将其设置为较低的值,Three.js将对角落的定义使用较少的顶点。

在继续展示如何从2D对象创建3D几何体之前,我们将看看Three.js提供的最后一个几何体,TeapotGeometry

TeapotGeometry

TeapotGeometry是一个用于渲染茶壶的几何体,这可能并不令人惊讶。 这个茶壶是3D渲染的标准参考模型,自1975年以来一直被使用。 有关此模型的历史信息,请参阅此处:https://www.computerhistory.org/revolution/computer-graphics-music-and-art/15/206

使用此模型的方式与迄今为止我们所见的所有其他模型完全相同:

import { TeapotGeometry } from 'three/examples/jsm/geometries/TeapotGeometry'
// ...
const geom = new TeapotGeometry(size, segments, bottom, lid, body, fitLid, blinn)

您指定特定的属性,然后创建几何体,将其分配给THREE.Mesh。根据属性的不同,结果看起来像这样(在teapot-geometry.html示例中):

要配置此几何体,您可以使用以下属性:

  • size(大小):这是茶壶的大小。
  • segments(段数):这定义了用于创建茶壶线框的段数。使用的段数越多,茶壶看起来越平滑。
  • bottom(底部):如果设置为true,则将呈现茶壶的底部。如果为false,则不呈现底部,这在茶壶位于表面上且无需呈现底部时可能会使用。
  • lid(盖子):如果设置为true,则呈现茶壶的盖子。如果为false,则不呈现盖子。
  • body(主体):如果设置为true,则呈现茶壶的主体。如果为false,则不呈现主体。
  • fitLid(适应盖子):如果设置为true,则盖子将完全适应茶壶。如果为false,则盖子与茶壶的主体之间会有一小段空间。
  • blinn(布林):这定义了是否使用与1975年原始模型相同的茶壶纵横比。

在接下来的几节中,我们将通过从2D形状中提取3D几何体的方式来查看创建几何体的另一种选择。

通过挤压2D形状创建几何体

Three.js提供了一种将2D形状挤压成3D形状的方法。 通过挤压,我们指的是沿着其z轴拉伸2D形状以将其转换为3D形状。 例如,如果我们挤压THREE.CircleGeometry,我们会得到一个类似圆柱的形状, 如果我们挤压THREE.PlaneGeometry,我们会得到一个类似立方体的形状。 挤压形状的最灵活的方法是使用THREE.ExtrudeGeometry

THREE.ExtrudeGeometry

使用THREE.ExtrudeGeometry,您可以从2D形状创建一个3D对象。 在我们深入了解此几何体的详细信息之前, 让我们首先看一个例子,extrude-geometry.html。 从该例子中截取的以下屏幕截图显示了这个几何体:

在此示例中,我们使用了在第5章的2D几何体部分创建的2D形状, 并使用THREE.ExtrudeGeometry将其转换为3D。 正如您在前面的屏幕截图中所看到的,该形状沿z轴挤压,从而产生了3D形状。 创建THREE.ExtrudeGeometry的代码非常简单:

const geometry = new THREE.ExtrudeGeometry(drawShape(), {
curveSegments,
steps,
depth,
bevelEnabled,
bevelThickness,
bevelSize,
bevelOffset,
bevelSegments,
amount
})

在此代码中,我们使用了drawShape()函数创建了形状,就像我们在第5章中所做的那样。 这个形状与一组属性一起传递给THREE.ExtrudeGeometry构造函数。

通过这些属性,您可以精确定义形状应该如何被挤压。 以下列表解释了您可以传递到THREE.ExtrudeGeometry的选项:

  • shapes(形状):需要一个或多个形状(THREE.Shape对象)以挤压几何体。 有关如何创建这样的形状,请参见第5章。
  • depth(深度):这确定形状应该被挤压多远(深度)。默认值为100
  • bevelThickness(斜角厚度):这确定斜角的深度。斜角是前后面和挤压之间的圆角。 该值定义了斜角深入形状的程度。默认值为6
  • bevelSize(斜角大小):这确定斜角的高度。这被添加到形状的普通高度上。 默认值为bevelThickness - 2
  • bevelSegments(斜角段数):这定义了斜角将使用的段数。 使用的段数越多,斜角看起来越平滑。默认值为3。 请注意,如果添加更多的段,还会增加顶点数,这可能会对性能产生不利影响。
  • bevelEnabled(启用斜角):如果设置为true,则添加斜角。默认值为true
  • bevelOffset(斜角偏移):斜角开始的形状轮廓距离。默认值为0
  • curveSegments(曲线段数):这确定在挤压形状的曲线时将使用多少段。 使用的段数越多,曲线看起来越平滑。默认值为12
  • steps(步数):这定义了形状在挤压深度沿着多少段被划分。 默认值为1,这意味着它将在深度上有一个单独的段,没有不必要的额外顶点。
  • extrudePath(挤压路径):这是形状应该沿其进行挤压的路径(THREE.CurvePath)。 如果没有指定,形状将沿z轴挤压。 请注意,如果您有一条弯曲的路径,您还需要确保为steps属性设置较高的值,以便可以准确地沿着曲线进行挤压。
  • uvGenerator(UV生成器):当您在材质中使用纹理时,UV映射确定纹理的哪一部分用于特定面。 使用uvGenerator属性,您可以传入自己的对象,该对象将为传入的形状创建面的UV设置。 有关UV设置的更多信息,请参见第10章,加载和使用纹理。 如果未指定任何内容,将使用THREE.ExtrudeGeometry.WorldUVGenerator。 如果您希望为面和侧面使用不同的材质,可以将一个材质数组传递给THREE.Mesh。 传递的第一个材质将应用于面,第二个材质将用于侧面。 您可以使用extrude-geometry.html示例中的菜单来尝试这些选项。 在这个例子中,我们沿着其z轴挤压了形状。 正如您在本节前面列出的选项中所看到的,您还可以使用extrudePath选项沿路径挤压形状。 在接下来的几何体THREE.TubeGeometry中,我们将演示这个过程。

THREE.TubeGeometry

THREE.TubeGeometry创建沿3D样条线挤出的管道。 您使用一些顶点指定路径,而THREE.TubeGeometry将创建该管道。 您可以在本章的源代码中找到一个可以尝试的示例(tube-geometry.html)。 以下截图显示了此示例:

正如您在此示例中所看到的,我们生成了一些随机点并使用这些点绘制了管道。 通过菜单中的控件,我们可以定义管道的外观。 创建管道所需的代码非常简单,如下所示:

const points = ... // THREE.Vector3对象的数组
const tubeGeometry = new TubeGeometry(
new THREE.CatmullRomCurve3(points),
tubularSegments,
radius,
radiusSegments,
closed
);

首先,我们需要获取一组THREE.Vector3类型的顶点(points变量), 就像我们为THREE.ConvexGeometryTHREE.LatheGeometry所做的那样。 然而,在我们可以使用这些点来创建管道之前,我们首先需要将这些点转换为THREE.Curve。 换句话说,我们需要通过我们定义的点定义一个平滑的曲线。 我们可以通过将顶点数组传递给THREE.CatmullRomCurve3的构造函数, 或者通过Three.js提供的其他Curve实现之一来实现这一点。 有了这个曲线和本节中将要解释的其他参数,我们就可以创建管道并将其添加到场景中。

在此示例中,我们使用了THREE.CatmullRomCurve3。 Three.js还提供了其他一些曲线,您也可以使用它们,它们需要稍微不同的参数, 但它们可以用于创建不同的曲线实现。 Three.js开箱即用提供了以下曲线: ArcCurveCatmullRomCurve3CubicBezierCurveCubicBezierCurve3EllipseCurveLineCurveLineCurve3QuadraticBezierCurveQuadraticBezierCurve3SplineCurve

除了曲线之外,THREE.TubeGeometry还接受一些其他参数。以下是THREE.TubeGeometry的所有参数列表:

  • path: 这是THREE.SplineCurve3,用于描述此管道应沿着的路径。
  • tubularSegments: 这些是用于构建管道的段。默认值为64。路径越长,您应该指定的段数就越多。
  • radius: 这是管道的半径。默认值为1
  • radiusSegments: 这是沿管道长度使用的段数。默认值为8。您使用的越多,管道就会显得越圆。
  • closed: 如果将其设置为true,则管道的起始点和终点将连接起来。默认值为false

本章中我们将展示的最后一个挤压示例并不真正是一种不同类型的几何体, 而是使用THREE.ExtrudeGeometry从SVG图像创建挤压。

从SVG元素挤出3D形状

当我们在第5章讨论THREE.ShapeGeometry时, 我们提到SVG在绘制形状方面基本上采用了相同的方法。 在本节中,我们将看看如何使用SVG图像以及THREE.SVGLoader来挤出SVG图像。 我们将使用蝙蝠侠标志作为示例:

首先,让我们看看原始的SVG代码是什么样的(您还可以在查看assets/svg/batman.svg文件的源代码时自行查看):

<svg version="1.0" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="1152px" height="1152px" xml:space="preserve">
<g>
<path id="batman-path" style="fill:rgb(0,0,0);" d="M261.135 114.535 C 254.906 116.662 247.491 118.825 244.659 119.344 C 229.433 122.131 177.907 142.565 151.973 156.101 C 111.417 177.269 78.9808 203.399 49.2992 238.815 C 41.0479 248.66 26.5057 277.248 21.0148 294.418 C 14.873 313.624 15.3588 357.341 21.9304 376.806 C 29.244 398.469 39.6107 416.935 52.0865 430.524 C 58.2431 437.23 63.3085 443.321 63.3431 444.06 ... 261.135 114.535 "/>
</g>
</svg>

除非您是SVG专家,否则这可能对您来说意义不大。 但基本上,您在这里看到的是一组绘图指令。 例如,C 277.987 119.348 279.673 116.786 279.673 115.867告诉浏览器绘制一个三次贝塞尔曲线, 而L 489.242 111.787告诉我们应该画一条线到那个特定的位置。 然而,幸运的是,我们不必自己编写解释这些代码的代码,可以使用THREE.SVGLoader,如下面的代码所示:

// 返回一个Promise
const batmanShapesPromise = new SVGLoader().loadAsync('/assets/svg/batman.svg');
// 当Promise解析后,svg将包含形状
batmanShapes.then((svg) => {
const shapes = SVGLoader.createShapes(svg.paths[0]);
// 基于形状,我们可以像之前看到的那样创建挤出几何体
const geometry = new THREE.ExtrudeGeometry(shapes, {
curveSegments,
steps,
depth,
bevelEnabled,
bevelThickness,
bevelSize,
bevelOffset,
bevelSegments,
amount
});
// ...
});

在这个代码片段中,您可以看到我们使用SVGLoader加载SVG文件。 我们在这里使用loadAsync,它将返回一个JavaScript Promise。 当该Promise解析时,我们可以访问加载的svg数据。 此数据可能包含路径元素列表,每个元素代表原始SVG的路径元素。 在我们的示例中,我们只有一个路径元素,因此我们使用svg.paths[0] 并将其传递给SVGLoader.createShapes以将其转换为THREE.Shape对象数组。

现在我们有了这些形状,我们可以使用我们之前在挤出我们自定义创建的2D几何体时使用的相同方法, 并使用THREE.ExtrudeGeometry从2D加载的SVG形状创建3D模型。

当您在浏览器中打开extrude-svg.html示例时,可以看到最终结果。

在本节中,我们将讨论的最后一个几何体是THREE.ParametricGeometry。 使用此几何体,您可以指定一些用于以编程方式创建几何体的函数。

THREE.ParametricGeometry

使用THREE.ParametricGeometry,您可以基于方程创建几何体。 在我们深入研究自己的示例之前,首先看一看Three.js已经提供的示例是个好主意。 当您下载Three.js分发版时,会获得examples/js/ParametricGeometries.js文件。 在此文件中,您可以找到可以与THREE.ParametricGeometry一起使用的一些方程的示例。 最基本的示例是创建平面的函数:

plane: function (width, height) {
return function (u, v, target) {
const x = u * width;
const y = 0;
const z = v * height;
target.set(x, y, z);
};
},

此函数由THREE.ParametricGeometry调用。 uv值将从01变化,并且将被调用大量次,对于从01的所有值。 在此示例中,使用u值确定矢量的x坐标,而使用v值确定z坐标。 运行时,您将获得一个宽度为width,深度为depth的基本平面。 在我们的示例中,我们做了类似的事情。 但是,我们不是创建一个平面,而是创建了一种波浪状的图案,如parametric-geometry.html示例中所示。 以下截图显示了此示例:

要创建此形状,我们将以下函数传递给THREE.ParametricGeometry

const radialWave = (u, v, optionalTarget) => {
var result = optionalTarget || new THREE.Vector3();
var r = 20;
var x = Math.sin(u) * r;
var z = Math.sin(v / 2) * 2 * r + -10;
var y = Math.sin(u * 4 * Math.PI) + Math.cos(v * 2 * Math.PI);
return result.set(x, y, z);
};

const geom = new THREE.ParametricGeometry(radialWave, 120, 120);

正如您在此示例中所看到的,通过几行代码,我们可以创建一些非常有趣的几何形状。 在此示例中,您还可以看到我们可以传递给THREE.ParametricGeometry的参数:

  • function:定义基于提供的u和v值的每个顶点位置的函数。
  • slices:定义应将u值划分为多少部分。
  • stacks:定义应将v值划分为多少部分。

通过更改函数,我们可以轻松使用完全相同的方法渲染完全不同的对象:

在转到本章的下一部分之前,让我们简要说明如何使用slicesstacks属性。 我们提到uv属性被传递到提供的函数参数中, 这两个属性的值范围从01。使用slicesstacks属性,我们可以定义传入函数调用的频率。 例如,如果我们将slices设置为5并将stacks设置为4,函数将使用以下值调用:

u:0/5, v:0/4
u:1/5, v:0/4
u:2/5, v:0/4
u:3/5, v:0/4
u:4/5, v:0/4
u:5/5, v:0/4
u:0/5, v:1/4
u:1/5, v:1/4
...
u:5/5, v:3/4
u:5/5, v:4/4

因此,这些值越高,您就越能够指定顶点,并且所创建的几何体将越平滑。 您可以在parametric-geometry.html示例右侧的菜单中查看此效果。

要了解更多示例,您可以查看Three.js分发版中的examples/js/ParametricGeometries.js文件。 此文件包含用于创建以下几何体的函数:

  • Klein瓶
  • 平面
  • 平面莫比乌斯带
  • 3D莫比乌斯带
  • 管道
  • 环状结
  • 球体
  • 平面

有时,您需要查看几何体的更多详细信息,并且对于材质以及网格将如何渲染并不太关心。 如果您想查看顶点和面,甚至只是轮廓,除了启用用于网格的材质的wireframe属性之外, Three.js还提供了一些几何体可以帮助您完成这些操作。 我们将在接下来的部分中探讨这些内容。

用于调试的几何体

Three.js默认提供了两种助手几何体,使查看几何体的详细信息或仅轮廓变得更容易:

  • THREE.EdgesGeometry:提供仅渲染几何体边缘的几何体。
  • THREE.WireframeGeometry:仅渲染几何体而不显示任何面。

首先,让我们看一看THREE.EdgesGeometry

THREE.EdgesGeometry

使用THREE.EdgesGeometry,您可以包装现有几何体,然后通过仅显示边缘而不显示单独的顶点和面来渲染它。 在edges-geometry.html示例中展示了一个示例:

在上述截图中,您可以看到RoundedBoxGeometry的轮廓,我们只看到了边缘。 由于RoundedBoxGeometry具有平滑的角,因此在使用THREE.EdgesGeometry时会显示这些平滑的角。 要使用此几何体,只需包装现有几何体,如下所示:

const baseGeometry = new RoundedBoxGeometry(3, 3, 3, 10, 0.4);
const edgesGeometry = new THREE.EdgesGeometry(baseGeometry, 1.5);

THREE.EdgesGeometry唯一接受的属性是thresholdAngle。 通过此属性,您可以确定几何体在何时绘制边缘。 在edges-geometry.html中,您可以控制此属性以查看效果。 如果您有现有几何体并希望查看线框,可以配置一个材质以显示此线框:

const material = new THREE.MeshBasicMaterial({ color: 0xffff00, wireframe: true });

Three.js还提供了一种使用THREE.WireframeGeometry的不同方式。

THREE.WireframeGeometry

此几何体模拟了将材质的wireframe属性设置为true时看到的行为:

const baseGeometry = new THREE.TorusKnotBufferGeometry(3, 1, 100, 20, 6, 9);
const wireframeGeometry = new THREE.WireframeGeometry(baseGeometry);

此几何体不接受任何额外的属性。

本章的最后一部分涉及创建3D文本对象。 我们将向您展示两种不同的方法,一种使用THREE.Text对象,另一种使用外部库。

创建3D文本网格

在这一节中,我们将快速了解如何创建3D文本。 首先,我们将看看如何使用Three.js提供的字体渲染文本,以及如何使用自己的字体。 然后,我们将展示使用一个名为Troika的外部库(https://github.com/protectwise/troika) 的快速示例,该库使得创建标签和2D文本元素并将其添加到场景中变得非常容易。

渲染文本

在Three.js中渲染文本非常简单。 您只需定义要使用的字体并使用我们在讨论THREE.ExtrudeGeometry时看到的相同的挤压属性。 以下是在Three.js中渲染文本的text-geometry.html示例的截图:

创建这个3D文本所需的代码如下:

import { FontLoader } from 'three/examples/jsm/loaders/FontLoader'
import { TextGeometry } from 'three/examples/jsm/geometries/TextGeometry'

new FontLoader()
.loadAsync('/assets/fonts/helvetiker_regular.typeface.json')
.then((font) => {
const textGeom = new TextGeometry('Some Text', {
font,
size,
height,
curveSegments,
bevelEnabled,
bevelThickness,
bevelSize,
bevelOffset,
bevelSegments,
amount
})
// ...
})

在此代码片段中,可以看到我们首先必须加载字体。 为此,Three.js提供了FontLoader(),我们提供要加载的字体的名称, 就像我们在使用SVGLoader时所做的那样,我们得到一个JavaScript Promise。 一旦该Promise解析,我们使用加载的字体创建TextGeometry

我们可以传递给THREE.TextGeometry的选项与我们可以传递给THREE.ExtrudeGeometry的选项相匹配:

  • font:要用于文本的加载字体。
  • size:文本的大小,默认值为100
  • height:挤压的长度(深度),默认值为50
  • curveSegments:在挤压形状的曲线时使用的段数。段数越多,曲线看起来越平滑。默认值为4
  • bevelEnabled:如果设置为true,将添加斜角。默认值为false
  • bevelThickness:斜角的深度。斜角是前后面和挤压之间的圆角。默认值为10
  • bevelSize:斜角的高度。默认值为8
  • bevelSegments:斜角使用的段数。段数越多,斜角看起来越平滑。默认值为3
  • bevelOffset:斜角开始的形状轮廓的距离。默认值为0

由于THREE.TextGeometry也是THREE.ExtrudeGeometry, 如果要为正面和侧面使用不同的材质,可以采用相同的方法。 如果在创建THREE.Mesh时传递一个包含两种材质的数组, Three.js将将第一个材质应用于文本的前后面,将第二个材质应用于侧面。

也可以使用其他字体与此几何体一起使用,但首先需要将它们转换为JSON - 如何执行此操作将在下一节中介绍。

添加自定义字体

Three.js提供了一些可以在场景中使用的字体。 这些字体基于TypeFace.js库提供的字体。 TypeFace.js是一个可以将TrueType和OpenType字体转换为JavaScript的库。 生成的JavaScript文件或JSON文件可以包含在您的页面中,然后可以在Three.js中使用该字体。 在较早的版本中,使用JavaScript文件,但在后来的Three.js版本中,Three.js切换到使用JSON文件。

要转换现有的OpenType或TrueType字体, 可以使用网页https://gero3.github.io/facetype.js/: 在这个页面上,您可以上传一个字体,它将为您将其转换为JSON。 请注意,这对于所有类型的字体可能效果不佳。 字体越简单(更直线),在Three.js中使用时渲染正确的机会就越大。 生成的文件如下所示,其中描述了每个字符(或字形):

{"glyphs":{"¦":{"x_min":359,"x_max":474,"ha":836,"o":"m 474 971 l 474 457 l 359 457 l 359 971 l 474 971 m 474 277 l 474 -237 l 359 -237 l 359 277 l 474 277 "},"Ž":{"x_min":106,"x_max":793,"ha":836,"o":"m 121 1013 l 778 1013 l 778 908 l 249 115 l 793 115 l 793 0 l 106 0 l 106 104 l 620 898 l 121 898 l 121 1013 m 353 1109 l 211 1289 l 305 1289 l 417 1168 l 530 1289 l 625 1289 l 482 1109 l 353 1109 "},"Á":{"x_min":25,"x_max":811,"ha":836,"o":"m 417 892 l 27 ....

一旦获得了JSON文件,就可以使用FontLoader(如前面“渲染文本”部分所示) 加载此字体并将其分配给您可以传递到TextGeometry的选项的font属性。

本章的最后一个示例将介绍使用Three.js创建文本的不同方法。

使用Troika库创建文本

如果要为场景中的某些部分创建标签或2D文本标记, 那么使用THREE.Text几何体的替代选项是一个名为Troika的外部库:https://github.com/protectwise/troika

这是一个相当庞大的库,提供了许多功能,可用于向您的场景添加交互性。 在本例中,我们将仅查看该库的文本模块。我们将在troika-text.html示例中展示我们将要创建的内容:

要使用此库,我们首先必须安装它(如果您按照第1章“使用Three.js创建您的第一个3D场景”的说明进行了操作,则可以使用此库): $ yarn add troika-three-text。 一旦安装完成,我们就可以像使用Three.js提供的其他模块一样导入并使用它:

import { Text } from 'troika-three-text'
const troikaText = new Text()
troikaText.text = 'Text rendering with Troika!\nGreat for 2D labels'
troikaText.fontSize = 2
troikaText.position.x = -3
troikaText.color = 0xff00ff
troikaText.sync()
scene.add(troikaText)

在上面的代码片段中,我们展示了如何使用Troika创建一个简单的文本元素。 您只需要调用Text()构造函数并设置属性。 但要注意的一点是,每当更改Text()对象中的属性时,都必须调用troikaText.sync()。 这将确保更改也应用于屏幕上呈现的模型。

总结

在本章中我们学到了很多。 我们介绍了一些高级几何体,并向您展示了如何使用Three.js创建和渲染文本元素。 我们向您展示了如何使用高级几何体(如 THREE.ConvexGeometryTHREE.TubeGeometryTHREE.LatheGeometry)创建非常漂亮的形状, 以及如何通过实验这些几何体来获得您想要的结果。 一个非常好的特性是我们还可以使用THREE.ExtrudeGeometry将现有的SVG路径转换为Three.js。

我们还快速查看了一些对调试非常有用的几何体。 THREE.EdgesGeometry只显示另一个几何体的边缘, 而THREE.WireframeGeometry可用于显示另一个几何体的线框。

最后,如果要创建3D文本,Three.js提供了TextGeometry,其中可以传递要使用的字体。 Three.js附带了一些字体,但您也可以创建自己的字体。 但请记住,复杂的字体通常无法正确转换。 使用TextGeometry的替代方法是使用Troika库, 它使得创建2D文本标签并将其放置在场景中变得非常容易。

到目前为止,我们看到的是实心(或线框)几何体,其中顶点彼此连接以形成面。 在接下来的章节中,我们将介绍使用一种称为粒子或点的东西来可视化几何体的替代方法。 使用粒子,我们不渲染完整的几何体 - 我们只渲染空间中的单个顶点。 这使您能够创建外观出色的3D效果,并具有良好的性能。