使用threejs实现3D坐标轴

科技一点鑫得 2024-03-08 03:20:53

threejs可以通过添加AxesHelper到场景中来显示辅助的坐标轴,但是效果一般,特别是场景中又添加了GridHelper网格辅助的话,X轴和Z轴由于和辅助线重合导致若隐若现,就像下面这样。接下来我们使用threejs来实现一个自己的3D坐标轴。本文需要你掌握threejs基础知识,了解平移、旋转等基本操作,推荐阅读在线书籍《探索three.js》进行入门。

我们使用一个细长的圆柱体和一个圆锥体组合表示一条坐标轴,圆锥体用来模拟箭头表示坐标轴的方向,通过创建三组这样的坐标轴,通过适当的旋转变换就可以生成XYZ坐标轴了。threejs中的CylinderGeometry几何体根据参数的不同既可以生成圆柱体、也可以生成圆锥体,查看官方文档关于CylinderGeometry的参数可知,当顶面和底面圆半径相同时表示的就是圆柱体,其中一个面的半径为0时就是圆锥体。创建CylinderGeometry几何体一般需要设置的参数有radiusTop、radiusBottom、height、radialSegments,分别表示顶面半径、底面半径、高度、圆面的分片数量,其中分片数量越大就越像圆。

生成Y轴

这里约定RGB颜色对应XYZ,默认创建的圆柱体轴线与y轴平行且圆柱体的中心点在原点,所以我们先来生成y轴,因为它不需要做旋转。创建一个Axes类来封装坐标轴的代码,这样使用时只需要把new Axes的对象添加到场景中就可以显示了,使用者不需要知道其中的细节。

import { SphereGeometry, CylinderGeometry, Group, Mesh, MeshStandardMaterial } from "three";class Axes extends Group { constructor() { super(); // 坐标轴:圆柱体和圆锥组成一条轴 const cylinder = new CylinderGeometry(0.03, 0.03, 2, 16); // 表示坐标轴线 const arrow = new CylinderGeometry(0, 0.06, 0.2); // 表示箭头 // 材质:RGB对应XYZ const red = new MeshStandardMaterial( {color: 0xff0000} ); const green = new MeshStandardMaterial( {color: 0x00ff00} ); const blue = new MeshStandardMaterial( {color: 0x0000ff} ); // y轴:一个轴包含了一个圆柱体和一个圆锥体,使用Group组合它们 const axes_y = new Group(); const y_line = new Mesh(cylinder, green); const y_arrow = new Mesh(arrow, green); y_arrow.position.y += 1.04; // 沿y轴向上平移箭头使其固定在圆柱体的顶端 axes_y.add(y_line, y_arrow); // 一个圆柱体和一个圆锥体添加到一个组中 axes_y.position.y += 1; // 沿y轴向上平移圆柱体的一半距离,使圆柱体的末端在原点(0,0,0)处 this.add(axes_y); }}export {Axes};

生成y轴效果如下

生成X轴

生成x轴唯一的不同就是还需要一个旋转的步骤,显然在上面y轴的基础上沿z轴顺时针旋转90°得到的就是x轴了。这里需要注意的是,Group的目的是用来组织可见的网格实体,默认的中心点也是原点,组变换会使其组内的所有网格都会发生变换,但是组内的网格自身也可以进行变换且不会影响组的中心点。

// x轴const axes_x = new Group(); // group默认的中心点是(0,0,0)const x_line = new Mesh(cylinder, red); const x_arrow = new Mesh(arrow, red);// 这里只是箭头自身的平移,不会改变group的中心点,所以group的中心点还是(0,0,0)x_arrow.position.y += 1.04;axes_x.add(x_line, x_arrow);// 沿z轴顺时针旋转90°axes_x.rotation.z = -Math.PI / 2;// 向x轴正向平移圆柱体一半的距离,使其末端在原点处axes_x.position.x += 1;this.add(axes_y, axes_x);

生成Z轴

类似地,沿x轴逆时针旋转90°可以得到z轴。

// z轴const axes_z = new Group();const z_line = new Mesh(cylinder, blue); const z_arrow = new Mesh(arrow, blue);z_arrow.position.y += 1.04;axes_z.add(z_line, z_arrow);axes_z.position.z += 1;axes_z.rotation.x = Math.PI / 2;this.add(axes_y, axes_x, axes_z);

生成原点

通过OrbitControls插件控制摄像头放大原点发现连接处有缺口,最后通过在原点处添加一个圆球来弥补一下。

const sphere = new SphereGeometry(0.06);const gold = new MeshStandardMaterial( {color: "gold"} );const origin = new Mesh(sphere, gold);this.add(axes_y, axes_x, axes_z, origin);

最后,生成的3D坐标轴效果如下:

附上完整的Axes类代码

import { SphereGeometry, CylinderGeometry, Group, Mesh, MeshStandardMaterial } from "three";class Axes extends Group { constructor() { super(); // 坐标轴:圆柱体和圆锥组成一条轴 const cylinder = new CylinderGeometry(0.03, 0.03, 2, 16); const arrow = new CylinderGeometry(0, 0.06, 0.2); const sphere = new SphereGeometry(0.06); // 材质:RGB对应XYZ const red = new MeshStandardMaterial( {color: 0xff0000} ); const green = new MeshStandardMaterial( {color: 0x00ff00} ); const blue = new MeshStandardMaterial( {color: 0x0000ff} ); const gold = new MeshStandardMaterial( {color: "gold"} ); // y轴 const axes_y = new Group(); const y_line = new Mesh(cylinder, green); const y_arrow = new Mesh(arrow, green); y_arrow.position.y += 1.04; axes_y.add(y_line, y_arrow); axes_y.position.y += 1; // x轴 const axes_x = new Group(); // group默认的中心点是(0,0,0) const x_line = new Mesh(cylinder, red); const x_arrow = new Mesh(arrow, red); // 这里只是箭头自身的平移,不会改变group的中心点,所以group的中心点还是(0,0,0) x_arrow.position.y += 1.04; axes_x.add(x_line, x_arrow); axes_x.position.x += 1; axes_x.rotation.z = -Math.PI / 2; // z轴 const axes_z = new Group(); const z_line = new Mesh(cylinder, blue); const z_arrow = new Mesh(arrow, blue); z_arrow.position.y += 1.04; axes_z.add(z_line, z_arrow); axes_z.position.z += 1; axes_z.rotation.x = Math.PI / 2; // 原点 const origin = new Mesh(sphere, gold); this.add( axes_x, axes_y, axes_z, origin ); }}export {Axes};参考文献

[1]. https://threejs.org/docs/index.html?q=CylinderGeometry#api/en/geometries/CylinderGeometry

0 阅读:0

科技一点鑫得

简介:感谢大家的关注