程序化生成glTF文件

本文介绍了如何使用gltf-transform.jsJavaScript库以编程方式在代码中创建glTF3D模型,通过示例展示了如何生成顶点数据、索引和创建Mesh,最后展示了如何将模型保存为glTF文件,以及该库在优化现有模型上的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

更多精彩内容尽在数字孪生平台

虽然在Blender等软件中创建 3D 模型更为常见,但有时我们希望在代码中创建模型。例如,从一种 3D 文件格式转换为另一种 3D 文件格式时,或者使用程序生成创建几何体时。
本文主要解释如何使用 glTF Transform ( gltf-transform.dev ) JavaScript 库以编程方式创建 glTF 3D 模型。该库压缩后的重量约为 20kb,比 Three.js 或 Babylon.js 等完整 3D 引擎小得多。
首先,我们需要一些顶点数据。glTF 支持点、线和三角网。Three.js 具有许多常见 3D 形状的开源实现,我复制了THREE.TorusGeometry下面的相关部分,删除了对 Three.js 本身的依赖。

// MIT License. Copyright © 2010-2023 three.js authors.

function createTorus(radius = 1, tube = 0.4, radialSegments = 12,tubularSegments = 48, arc = Math.PI * 2) {

  const indicesArray = [];
  const positionArray = [];
  const uvArray = [];

  const vertex = [0, 0, 0];

  // 生成 positions 和 uvs
  for (let j = 0; j <= radialSegments; j++) {
    for (let i = 0; i <= tubularSegments; i++) {
      const u = (i / tubularSegments) * arc;
      const v = (j / radialSegments) * Math.PI * 2;

      // position
      vertex[0] = (radius + tube * Math.cos(v)) * Math.cos(u);
      vertex[1] = (radius + tube * Math.cos(v)) * Math.sin(u);
      vertex[2] = tube * Math.sin(v);
      positionArray.push(vertex[0], vertex[1], vertex[2]);

      // uv
      uvArray.push(i / tubularSegments);
      uvArray.push(j / radialSegments);
    }
  }

  // 生成索引
  for (let j = 1; j <= radialSegments; j++) {
    for (let i = 1; i <= tubularSegments; i++) {
      // indices

      const a = (tubularSegments + 1) * j + i - 1;
      const b = (tubularSegments + 1) * (j - 1) + i - 1;
      const c = (tubularSegments + 1) * (j - 1) + i;
      const d = (tubularSegments + 1) * j + i;

      // faces

      indicesArray.push(a, b, d);
      indicesArray.push(b, c, d);
    }
  }

  return { indicesArray, positionArray, uvArray };
}

上面的函数createTorus()提供了顶点位置和 UV 的数组,以及连接这些顶点的三角形的索引。这里可以替换为其他的几何图形。
接下来我们需要将顶点数据组装成 glTF 2.0 文件。首先,我们创建一个新的 document 对象和一个用于存储数据的Buffer

import { Document } from '@gltf-transform/core';

const document = new Document();
const buffer = document.createBuffer();

接下来,我们获取上面生成的顶点数据,并使用它来创建Mesh

const { indicesArray, positionArray, uvArray } = createTorus();

// indices and vertex attributes
const indices = document
  .createAccessor()
  .setArray(new Uint16Array(indicesArray))
  .setType('SCALAR')
  .setBuffer(buffer);
const position = document
  .createAccessor()
  .setArray(new Float32Array(positionArray))
  .setType('VEC3')
  .setBuffer(buffer);
const texcoord = document
  .createAccessor()
  .setArray(new Float32Array(texcoordArray))
  .setType('VEC2')
  .setBuffer(buffer);

// material
const material = document.createMaterial()
  .setBaseColorHex(0xD96459)
  .setRoughnessFactor(1)
  .setMetallicFactor(0);

// primitive and mesh
const prim = document
  .createPrimitive()
  .setMaterial(material)
  .setIndices(indices)
  .setAttribute('POSITION', position)
  .setAttribute('TEXCOORD_0', texcoord);
const mesh = document.createMesh('MyMesh')
  .addPrimitive(prim);

虽然 glTF可用于单独存储Mesh,但更常见的是将Mesh放置在默认场景中。这可确保一切内容在各种 3D 查看器中按预期显示。我们将Mesh添加到节点,并将该节点放入场景中。如果我们有多个Mesh,我们会为每个节点分配不同的位置/旋转/缩放。

const node = document.createNode('MyNode')
  .setMesh(mesh)
  .setTranslation([0, 0, 0]);

const scene = document.createScene('MyScene')
  .addChild(node);

最后,我们将结果保存为新的 glTF 文件。I/O 取决于我们运行代码的环境,因此请选择下面适当的选项。
Node.js

import { NodeIO } from '@gltf-transform/core';

const io = new NodeIO();
await io.write('./torus.glb', document);

Deno

import { DenoIO } from '@gltf-transform/core';

const io = new DenoIO();
await io.write('./torus.glb', document);

Web

import { WebIO } from '@gltf-transform/core';

const io = new WebIO();
const bytes = await io.writeBinary(document); // → Uint8Array

就这样,我们就生成了一个新 glTF 2.0 文件。

gltf-transform库可以做的不仅仅是创建新文件,它更常用于优化现有的 glTF 文件,我们可以通过在模型中添加 Draco 压缩来执行相同的操作。

评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

搞GIS图形的sky.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值