Three.js 有很多图元。图元就是一些 3D 的形状,在运行时根据大量参数生成。

使用图元是种很常见的做法,像使用球体作为地球,或者使用大量盒子来绘制 3D 图形。 尤其是用来试验或者刚开始学习 3D。 对大多数 3D 应用来说,更常见的做法是让美术在 3D 建模软件中创建 3D 模型, 像 Blender,Maya 或者 Cinema 4D。 之后在这个系列中,我们会涵盖到创建和加载来自 3D 建模软件的模型。 现在,让我们仅使用可以获得的图元。

基于 BufferGeometry 的图元是面向性能的类型。 几何体的顶点是直接生成为一个高效的类型数组形式,可以被上传到 GPU 进行渲染。 这意味着它们能更快的启动,占用更少的内存。但如果想修改数据,就需要复杂的编程。

基于 Geometry 的图元更灵活、更易修改。 它们根据 JavaScript 的类而来,像 Vector3 是 3D 的点,Face3 是三角形。 它们需要更多的内存,在能够被渲染前,Three.js 会将它们转换成相应的 BufferGeometry 表现形式。

如果你知道你不会操作图元,或者你擅长使用数学来操作它们,那么最好使用基于 BufferGeometry 的图元。 但如果你想在渲染前修改一些东西,那么 Geometry 的图元会更好操作。

举个简单的例子,BufferGeometry 不能轻松的添加新的顶点。 使用顶点的数量在创建时就定好了,相应的创建存储,填充顶点数据。 但用 Geometry 你就能随时添加顶点。

下面的很多图元都有默认的部分或者全部参数,所以可以根据你的需要选择使用。如果下面的形状不符合你的使用需求,你可以从 .obj 文件 或 .gltf 文件 加载几何体。 你也可以创建 自定义 Geometry。

BoxGeometry

盒子几何体,BoxGeometry是四边形的原始几何类,它通常使用构造函数所提供的“width”、“height”、“depth”参数来创建立方体或者不规则四边形。

img

const geometry = new THREE.BoxGeometry( 1, 1, 1 );
const material = new THREE.MeshBasicMaterial( {color: 0x00ff00} );
const cube = new THREE.Mesh( geometry, material );
scene.add( cube );

球缓冲几何体(SphereGeometry)

一个用于生成球体的类。

img

const geometry = new THREE.SphereGeometry( 15, 32, 16 );
const material = new THREE.MeshBasicMaterial( { color: 0xffff00 } );
const sphere = new THREE.Mesh( geometry, material );
scene.add( sphere );

还有一个重要的东西,就是所有形状都有多个设置来设置它们的细化程度。 一个很好的例子就是球形几何体。它可以这些参数:一圈组成的片数、从上到下的数量等。例如:

img

第一个球体一圈有 5 分片,高度为 3,一共 15 片,或者 30 个三角形。 第二个球体一圈有 24 分片,高度为 10,一共 240 片,或者 480 个三角形。 第三个球体一圈有 50 分片,高度为 50,一共 2500 片,或者 5000 个三角形。

由你决定需要细分成多少。看起来你可能需要较多数量的分片,但去除线,设置平面着色,我们就得到了:

img

现在并不明显是否右边有 5000 个三角形的比中间只有 480 个三角形的好更多。 如果你只是绘制少量球体,比如一个地球地图的球体,那么单个 10000 个三角形的球体就是个不错的选择。 但如果你要画 1000 个球体,那么 1000 个球体 x 10000 个三角形就是一千万个三角形。 想要动画流畅,你需要浏览器每秒绘制 60 帧,那么上面的场景就需要每秒绘制 6 亿个三角形。那是巨大的运算量。

平面缓冲几何体(PlaneGeometry)

一个用于生成平面几何体的类。

img

const geometry = new THREE.PlaneGeometry( 1, 1 );
const material = new THREE.MeshBasicMaterial( {color: 0xffff00, side: THREE.DoubleSide} );
const plane = new THREE.Mesh( geometry, material );
scene.add( plane );

img

左边的平面有 2 个三角形,右边的平面有 200 个三角形。不像球体,在多数平面的应用场景中,并没有什么折中的方法。 你可能只在你想要修改或者在某些方面封装一下的时候才将平面细分。对于盒子也是一样。

所以,选择适合你情况的方案。细分的越少,运行的越流畅,使用的内存也会更少。 你需要根据你的具体情况选择合适的方案。

圆形缓冲几何体(CircleGeometry)

CircleGeometry是欧式几何的一个简单形状,它由围绕着一个中心点的三角分段的数量所构造,由给定的半径来延展。 同时它也可以用于创建规则多边形,其分段数量取决于该规则多边形的边数。

img

const geometry = new THREE.CircleGeometry( 5, 32 );
const material = new THREE.MeshBasicMaterial( { color: 0xffff00 } );
const circle = new THREE.Mesh( geometry, material );
scene.add( circle );

圆锥缓冲几何体(ConeGeometry)

一个用于生成圆锥几何体的类。

img

const geometry = new THREE.ConeGeometry( 5, 20, 32 );
const material = new THREE.MeshBasicMaterial( {color: 0xffff00} );
const cone = new THREE.Mesh( geometry, material );
scene.add( cone );

圆柱缓冲几何体(CylinderGeometry)

一个用于生成圆柱几何体的类。

img

const geometry = new THREE.CylinderGeometry( 5, 5, 20, 32 );
const material = new THREE.MeshBasicMaterial( {color: 0xffff00} );
const cylinder = new THREE.Mesh( geometry, material );
scene.add( cylinder );

十二面缓冲几何体(DodecahedronGeometry)

一个用于创建十二面几何体的类。

img

const radius = 7;  // ui: radius
const geometry = new THREE.DodecahedronGeometry(radius);

边缘几何体(EdgesGeometry)

一个工具对象,将一个几何体作为输入,生成面夹角大于某个阈值的那条边。例如,你从顶上看一个盒子,你会看到有一条线穿过这个面,因为每个组成这个盒子的三角形都显示出来了。而如果使用 EdgesGeometryopen in new window 中间的线就会被移除。调整下面的 thresholdAngle,你就会看到夹角小于这个值的边消失了。

img

const geometry = new THREE.BoxGeometry( 100, 100, 100 );
const edges = new THREE.EdgesGeometry( geometry );
const line = new THREE.LineSegments( edges, new THREE.LineBasicMaterial( { color: 0xffffff } ) );
scene.add( line );

挤压缓冲几何体(ExtrudeGeometry)

从一个形状路径中,挤压出一个BufferGeometry。受挤压的 2D 形状,及可选的斜切。 这里我们挤压了一个心型。

img

const shape = new THREE.Shape();
const x = -2.5;
const y = -5;
shape.moveTo(x + 2.5, y + 2.5);
shape.bezierCurveTo(x + 2.5, y + 2.5, x + 2, y, x, y);
shape.bezierCurveTo(x - 3, y, x - 3, y + 3.5, x - 3, y + 3.5);
shape.bezierCurveTo(x - 3, y + 5.5, x - 1.5, y + 7.7, x + 2.5, y + 9.5);
shape.bezierCurveTo(x + 6, y + 7.7, x + 8, y + 4.5, x + 8, y + 3.5);
shape.bezierCurveTo(x + 8, y + 3.5, x + 8, y, x + 5, y);
shape.bezierCurveTo(x + 3.5, y, x + 2.5, y + 2.5, x + 2.5, y + 2.5);

const extrudeSettings = {
  steps: 2,  // ui: steps
  depth: 2,  // ui: depth
  bevelEnabled: true,  // ui: bevelEnabled
  bevelThickness: 1,  // ui: bevelThickness
  bevelSize: 1,  // ui: bevelSize
  bevelSegments: 2,  // ui: bevelSegments
};

const geometry = THREE.ExtrudeGeometry(shape, extrudeSettings);

形状缓冲几何体(ShapeGeometry)

从一个或多个路径形状中创建一个单面多边形几何体。

img

const shape = new THREE.Shape();
const x = -2.5;
const y = -5;
shape.moveTo(x + 2.5, y + 2.5);
shape.bezierCurveTo(x + 2.5, y + 2.5, x + 2, y, x, y);
shape.bezierCurveTo(x - 3, y, x - 3, y + 3.5, x - 3, y + 3.5);
shape.bezierCurveTo(x - 3, y + 5.5, x - 1.5, y + 7.7, x + 2.5, y + 9.5);
shape.bezierCurveTo(x + 6, y + 7.7, x + 8, y + 4.5, x + 8, y + 3.5);
shape.bezierCurveTo(x + 8, y + 3.5, x + 8, y, x + 5, y);
shape.bezierCurveTo(x + 3.5, y, x + 2.5, y + 2.5, x + 2.5, y + 2.5);
const geometry = new THREE.ShapeGeometry(shape);

二十面缓冲几何体(IcosahedronGeometry)

一个用于生成二十面体的类。

img

const radius = 7;  // ui: radius
const geometry = new THREE.IcosahedronGeometry(radius);

车削缓冲几何体(LatheGeometry)

创建具有轴对称性的网格,比如花瓶。车削绕着Y轴来进行旋转。

img

const points = [];
for ( let i = 0; i < 10; i ++ ) {
	points.push( new THREE.Vector2( Math.sin( i * 0.2 ) * 10 + 5, ( i - 5 ) * 2 ) );
}
const geometry = new THREE.LatheGeometry( points );
const material = new THREE.MeshBasicMaterial( { color: 0xffff00 } );
const lathe = new THREE.Mesh( geometry, material );
scene.add( lathe );

八面缓冲几何体(OctahedronGeometry)

一个用于创建八面体的类。

img

const radius = 7;  // ui: radius
const geometry = new THREE.OctahedronGeometry(radius);

多面缓冲几何体(PolyhedronGeometry)

多面体在三维空间中具有一些平面的立体图形。这个类将一个顶点数组投射到一个球面上,之后将它们细分为所需的细节级别。 这个类由DodecahedronGeometry、IcosahedronGeometry、OctahedronGeometry和TetrahedronGeometry 所使用,以生成它们各自的几何结构。将一些环绕着中心点的三角形投影到球体上。

img

const verticesOfCube = [
    -1,-1,-1,    1,-1,-1,    1, 1,-1,    -1, 1,-1,
    -1,-1, 1,    1,-1, 1,    1, 1, 1,    -1, 1, 1,
];

const indicesOfFaces = [
    2,1,0,    0,3,2,
    0,4,7,    7,3,0,
    0,1,5,    5,4,0,
    1,2,6,    6,5,1,
    2,3,7,    7,6,2,
    4,5,6,    6,7,4
];

const geometry = new THREE.PolyhedronGeometry( verticesOfCube, indicesOfFaces, 6, 2 );

圆环缓冲几何体(RingGeometry)

一个用于生成二维圆环几何体的类。中间有洞的 2D 圆盘

img

const geometry = new THREE.RingGeometry( 1, 5, 32 );
const material = new THREE.MeshBasicMaterial( { color: 0xffff00, side: THREE.DoubleSide } );
const mesh = new THREE.Mesh( geometry, material );
scene.add( mesh );

四面缓冲几何体(TetrahedronGeometry)

一个用于生成四面几何体的类。

img

const radius = 7;  // ui: radius
const geometry = new THREE.TetrahedronGeometry(radius);

圆环缓冲几何体(TorusGeometry)

一个用于生成圆环几何体的类。

img

const geometry = new THREE.TorusGeometry( 10, 3, 16, 100 );
const material = new THREE.MeshBasicMaterial( { color: 0xffff00 } );
const torus = new THREE.Mesh( geometry, material );
scene.add( torus );

圆环缓冲扭结几何体(TorusKnotGeometry)

创建一个圆环扭结,其特殊形状由一对互质的整数,p和q所定义。如果p和q不互质,创建出来的几何体将是一个环面链接。

img

const geometry = new THREE.TorusKnotGeometry( 10, 3, 100, 16 );
const material = new THREE.MeshBasicMaterial( { color: 0xffff00 } );
const torusKnot = new THREE.Mesh( geometry, material );
scene.add( torusKnot );

管道缓冲几何体(TubeGeometry)

创建一个沿着三维曲线延伸的管道。

img

class CustomSinCurve extends THREE.Curve {

	constructor( scale = 1 ) {

		super();

		this.scale = scale;

	}

	getPoint( t, optionalTarget = new THREE.Vector3() ) {

		const tx = t * 3 - 1.5;
		const ty = Math.sin( 2 * Math.PI * t );
		const tz = 0;

		return optionalTarget.set( tx, ty, tz ).multiplyScalar( this.scale );

	}

}

const path = new CustomSinCurve( 10 );
const geometry = new THREE.TubeGeometry( path, 20, 2, 8, false );
const material = new THREE.MeshBasicMaterial( { color: 0x00ff00 } );
const mesh = new THREE.Mesh( geometry, material );
scene.add( mesh );

网格几何体(WireframeGeometry)

这个类可以被用作一个辅助物体,来对一个geometry以线框的形式进行查看。

img

const geometry = new THREE.SphereGeometry( 100, 100, 100 );

const wireframe = new THREE.WireframeGeometry( geometry );

const line = new THREE.LineSegments( wireframe );
line.material.depthTest = false;
line.material.opacity = 0.25;
line.material.transparent = true;

scene.add( line );