Web编程技术营地
研究、演示、创新

Geometries

撰写时间:2023-09-18

修订时间:2026-05-19

Geometries类负责处理与顶点相关的数据,故有较多的方法,特此作明确的分类。

该类的各个方法的实现,均来源于一线日常实践所需,因此具有非常高的实用性。得益于该类,我们可以很优雅的方式来实现、扩展各种各样的Mesh类。

文件内容框架

import {vec3, mat4, glMatrix} from '/js/esm/gl-matrix/index.js'; const VERTEX_COMP_SIZE = 3; function Point(x, y, z) { return {x, y, z}; } class Geometries { ... } export default Geometries;

Point返回一个顶点对象。

下面开始Geometries的定义。

弧度转换

这两组方法用于弧度与角度值的互换。

Geometries的核心职责

确定顶点数组中顶点的数量

WebGL中,顶点数组是扁平的一维数组。

顶点数组共有12个元素,每3个元素构成一个顶点,共有3个顶点。

上面代码通过对齐、断行、注释,因此各个概念十分清晰。

但实际上,计算机在内部所看到的,却是:

问:上面顶点数组中共有多少个顶点?

很明显,我们需指定多少个元素构成一个顶点。

上面以3D立体坐标系指定了顶点数组。之前我们曾接触过2D平面坐标系的顶点数组:

后面我们还要与齐次坐标系 (homogeneous coordinates) 的顶点数组打交道:

鉴于我们目前使用最多的是3D立体坐标系,因此当前的Geometries类声明了以下常量:

以明确在顶点数组中,每3个元素构成一个顶点的坐标值。

自动生成IBO对象中的索引值

回顾之前内容,要渲染一个四边形,需编写以下代码:

我们必须在indices中,按逆时针方向依序指定两个三角形的顶点索引值,才能正确地渲染出来。

上面手工指定索引值的方式,不仅麻烦,且易出错。

Geometries可帮助我们自动生成这些索引数组。给定一个按逆时针方向指定的任意顶点数组,下面代码可直接达到目的:

因此,在应用框架中的众多Mesh类的构造方法中,如果客户端不手工指定这些索引值数组,则这些Mesh类将自动创建。由此,客户端使用起来非常方便。

在控制台格式化输出信息

为弥补console的不足,Geometries提供了一些格式化输出相关数据的方法。

PrintVertices方法在控制台格式化输出顶点数组信息。

PrintMatrix方法在控制台格式化输出矩阵信息。

顶点的矩阵变换

可对顶点数组的各个顶点统一左乘一个矩阵,以便将所有顶点都进行统一的顶点位置变换

生成几何图元的顶点

对于稍微复杂的几何体,从头构建其顶点数组无疑是个噩梦。Geometries可用于生成正多边形的顶点,以及诸如立方体、圆锥体等常见3D物体的顶点数组及各个面的索引数组。

获取顶点

GetVertexFromIndex

GetVertexFromIndex从一个由每3个元素代表一个顶点所构成的顶点数组中返回指定索引值的顶点。

源代码:

static GetVertexFromIndex(vertices, index) { index *= VERTEX_COMP_SIZE; return vertices.slice(index, index + VERTEX_COMP_SIZE); }

客户端代码:

GeoPC.js模块中的extend函数,将一个标准的PageConsole实例,扩展为支持打印顶点信息等功能的对象。

通过这种方式,在不影响原有对象功能的基础上,我们可以在特定领域内,自由、充分、独立地扩展任意对象的额外功能。

上面代码,从vertices中取出第1个顶点。

目前的实现是固定为顶点数组中每3个元素代表一个顶点。

GetVerticesFromIndices

GetVerticesFromIndices根据一个指定的索引值数组,从一个由每3个元素代表一个顶点所构成的顶点数组中返回一个包含多个顶点的顶点数组。

源代码:

GetVerticesFromIndices在内部调用上一节中的GetVertexFromIndex方法,取出各相应的顶点以组成一个新的顶点数组,并返回该数组。

客户端代码:

vertices是一个有5个顶点的数组,faceIndices将顶点数组索引值为[0, 3, 4]3个顶点定义为一个面,GetVerticesFromIndices方法据此从顶点数组中提取相应的顶点,并返回一个新的数组。

我们可以使用更直观的方式来打印两者的引用关系:

pclogRefVerticesFromIndices方法内部调用了GeoGetVerticesFromIndices方法来求出最终的顶点数组,所以其参数只需传入所引用的顶点数组及顶点索引数组即可。

上一章中的FaceMesh类的构造方法中就使用了GetVerticesFromIndices方法用以方便地构建各个面。

获取顶点索引

GetIndicesFromVertices

GetIndicesFromVertices从一个顶点数组返回由每个顶点索引值所构成的数组。

源代码:

上面代码也可改用被屏蔽的那行语句返回数组。依序为:构造函数、迭代器、解构为个体、数组打包。

实际代码使用了迭代类数组对象 (array-like object)并仅返回其索引值的方式。

客户端代码:

GetIndicesFromVertices的返回结果可作为参数调用其他方法,如GetCCWTriangleIndicesGetLinesIndices等等。

GetCCWTriangleIndices

GetCCWTriangleIndices从一个顶点索引数组中返回一个按逆时针方向指定的各个独立三角形的索引值数组。

源代码:

下面是客户端代码。分为两种情况。

第一种情况

对于顶点数组变量vertices,变量indices存储了顶点数组中各顶点的索引值。Geo.GetCCWTriangleIndices方法从这些索引值中,按逆时针方向生成各个三角形的索引值。每个三角形都包含第0个顶点的索引值。因此,这是一个共点的算法。

第二种情况

上下两个面均为顶点数量相等的多边形(此为三角形),则可自动生成侧边的6个三角形索引值(每个侧面由2个三角形构成)。

这种算法可为圆柱体、圆锥体、球体等网络物体生成侧边的三角形索引值。

在调用GeoGetCCWTriangleIndices方法时,第一个参数为底部索引数组,第二个参数为顶部索引数组,可确保所生成的侧边三角形索引值为逆时针方向。

GetLinesIndices

GetLinesIndices从一个顶点索引数组中返回一个由各个独立线段的两点的索引值构成的数组。

源代码:

客户端代码:

GetLinesIndices的参数isLoop用于控制是否需要生成从末尾顶点连向起始顶点的闭合线段的顶点索引值。

可为带有圆心的顶点数组生成从圆心连向各个顶点的框线索引值:

GetLinesIndices的最后一个参数orgIndex用于指定原点的索引值。

GenTriangleIndicesInCircle

GenTriangleIndicesInCircle从一个带有圆心的顶点索引数组中返回一个按逆时针方向指定的各个独立三角形的索引值数组。

/* * * @param {Array} indices * the frist element is the origin index; * and the rest elements are the indices of vertices on the cirle * * @returns {Array|Geometries.GenTriangleIndicesInCircle.result} * such as [0, 1, 2, 0, 2, 3, ...] */ static GenTriangleIndicesInCircle(indices) { let orgIndex = indices.shift(); indices.push(indices[0]); let result = []; while(indices.length >= 2) { let index1 = indices.shift(); let index2 = indices[0]; result.push(orgIndex, index1, index2); } return result; }

客户端代码:

转换为二维数组

To2DArrays将一个一维数组转换为二维数组。

源代码:

static To2DArrays(arr, compSize = 3) { let result = []; for (let index = 0; index < arr.length; index += compSize) { result.push(arr.slice(index, index + compSize)); } return result; }

客户端代码:

生成顶点

GenRegularPolygonPoints

GenRegularPolygonPoints生成正多边形的顶点,返回一个元素类型为Point对象的数组。

源代码:

客户端代码:

各个点位于XY平面上,即图像在观察者面前竖起来。若有圆心,则圆心排在最前面。

GenRegularPolygonVertices

GenRegularPolygonVertices生成正多边形的顶点,返回一个顶点数组。

源代码:

其代码内部调用GenRegularPolygonPoints,将结果转换为经常使用的顶点数组并返回。

客户端代码:

GenSquareVertices

GenSquareVertices根据指定的边长,生成一个正方形的顶点,返回一个顶点数组。

源代码:

客户端代码:

在之前的代码中,我们经常如下指定一个正方形的各个顶点:

现在,我们可以解放双手了。

打印信息

PrintVertices

PrintVertices方法在控制台格式化输出顶点数组信息。

源代码:

static PrintVertices(vertices) { function appendSpace(str) { if (!str.startsWith('-')) { return ' ' + str; } return str; } console.log('==================== Vertices Info ===================='); let coordIndex = 0; for (let i = 0; i < vertices.length; i += VERTEX_COMP_SIZE) { console.log(`${coordIndex}: (${appendSpace(vertices[i].toFixed(2))}, ${appendSpace(vertices[i+1].toFixed(2))}, ${appendSpace(vertices[i+2].toFixed(2))})`); coordIndex++; } console.log('------------------------------------------'); console.log(`Total ${coordIndex} vertices, in forms of (x, y, z)`); console.log('\n'); }

客户端代码:

控制台输出结果:

PrintMatrix

PrintMatrix方法在控制台格式化输出矩阵信息。

源代码:

客户端代码:

控制台输出结果:

GetMatrixRows

GetMatrixRows方法从一个列主序矩阵中获取各行数据。

客户端代码:

矩阵与向量变换

Mat4MulVec3

Mat4MulVec3方法对一个顶点应用矩阵变换,返回只含有一个顶点的顶点数组。

源代码:

客户端代码:

ApplyMatrixToVertices

ApplyMatrixToVertices方法对一个顶点数组应用矩阵变换,返回顶点数组。

源代码:

客户端代码:

RotateVerticesAroundAxis

RotateVerticesAroundAxis方法围绕指定的坐标轴旋转顶点数组,返回顶点数组。

源代码:

客户端代码:

生成几何体

GenCube

GenCube方法为立方体生成顶点数组及6个面的顶点索引数组,返回一个封装对象。

源代码:

客户端代码:

CubeMesh的构造方法直接使用了其封装的对象:

GenCuboid

GenCuboid方法为长方体生成顶点数组及6个面的顶点索引数组,返回一个封装对象。

源代码:

客户端代码:

所对应的Mesh类为CuboidMesh类:

GenBox

GenBox方法为一个开口的盒子生成顶点数组及各个面的顶点索引数组,返回一个封装对象。

源代码:

客户端代码:

所对应的Mesh类为BoxMesh类:

GenCone

GenCone方法为圆锥体生成顶点数组及各个面的顶点索引数组,返回一个封装对象。

源代码:

客户端代码:

所对应的Mesh类为ConeMesh类:

GenConeFrustum

GenConeFrustum方法为圆锥台生成顶点数组及各个面的顶点索引数组,返回一个封装对象。

源代码:

客户端代码:

所对应的Mesh类为ConeFrustumMesh类:

GenCylinder

GenCylinder方法为圆柱体生成顶点数组及各个面的顶点索引数组,返回一个封装对象。

源代码:

客户端代码:

所对应的Mesh类为CylinderMesh类:

参考资源

  1. WebGL 2.0 Specification
  2. glMatrix.net