基于Three.js实现顶面可倾斜的任意多边形棱柱(含凹凸多边形)
实现支持任意凹凸多边形的可倾斜棱柱模型
问题分析
需要构建一个棱柱模型,核心需求包括:
- 底面为任意凹凸多边形
- 顶面可自由倾斜(后续需支持底面独立设置倾斜角度)
- 两种尝试的方案存在缺陷:
- 拉伸
ShapeGeometry后通过法向量识别顶面修改顶点:法向量识别易受凹凸多边形拓扑影响,修改顶点易破坏几何体拓扑,导致渲染异常 - 复制底面平移旋转生成顶面+拼接侧面:手动合并几何体易出现顶点索引冲突、法向量错误,且简单平移旋转无法精准匹配任意倾斜平面的需求
- 拉伸
最优实现思路:自定义BufferGeometry构建
直接手动生成几何体的顶点、面索引,完全控制拓扑结构,适配任意凹凸多边形和倾斜平面需求,步骤如下:
核心步骤
- 定义基础顶点:输入底面的二维顶点数组,转换为三维坐标(初始可设z=0,后续扩展底面倾斜时可替换为平面投影)
- 计算顶面顶点:通过平面方程(
THREE.Plane)计算每个底面顶点在目标倾斜顶面上的投影点,确保顶面严格贴合倾斜平面 - 三角化多边形:用
THREE.ShapeUtils.triangulateShape对凹凸多边形进行三角化,生成底面和顶面的三角面索引 - 生成侧面:遍历相邻顶点对,连接底面与顶面的对应顶点,将四边形侧面拆分为两个三角面
- 整合几何体:将顶点、索引存入
BufferGeometry,自动计算法向量保证光照正常
代码示例(Three.js)
import * as THREE from 'three'; import { BufferGeometryUtils } from 'three/addons/utils/BufferGeometryUtils.js'; function createTiltedPrism(baseVertices, topPlane, basePlane = new THREE.Plane(new THREE.Vector3(0,0,1), 0)) { // baseVertices: 二维数组,示例:[[0,0], [2,0], [3,1], [1,2], [0,1]] // topPlane: THREE.Plane实例,定义顶面倾斜状态 // basePlane: THREE.Plane实例,定义底面倾斜状态(默认z=0平面) const vertices = []; const indices = []; const vertexCount = baseVertices.length; // 生成底面顶点:投影到底面平面 const base3D = baseVertices.map(([x,y]) => { const ray = new THREE.Ray(new THREE.Vector3(x, y, 0), basePlane.normal); const point = new THREE.Vector3(); ray.intersectPlane(basePlane, point); return point; }); base3D.forEach(v => vertices.push(v.x, v.y, v.z)); // 生成顶面顶点:投影到顶面平面 const top3D = base3D.map(v => { const ray = new THREE.Ray(v, topPlane.normal); const point = new THREE.Vector3(); ray.intersectPlane(topPlane, point); return point; }); top3D.forEach(v => vertices.push(v.x, v.y, v.z)); // 三角化底面,生成面索引 const baseTriangles = THREE.ShapeUtils.triangulateShape(baseVertices, []); baseTriangles.forEach(tri => indices.push(tri[0], tri[1], tri[2])); // 三角化顶面,索引偏移底面顶点数 const topIndexOffset = vertexCount; baseTriangles.forEach(tri => { indices.push( topIndexOffset + tri[0], topIndexOffset + tri[1], topIndexOffset + tri[2] ); }); // 生成侧面三角面 for (let i = 0; i < vertexCount; i++) { const nextI = (i + 1) % vertexCount; // 四边形拆分为两个三角形 indices.push( i, nextI, topIndexOffset + nextI, i, topIndexOffset + nextI, topIndexOffset + i ); } // 创建BufferGeometry并计算法向量 const geometry = new THREE.BufferGeometry(); geometry.setAttribute('position', new THREE.Float32BufferAttribute(vertices, 3)); geometry.setIndex(indices); geometry.computeVertexNormals(); return geometry; } // 使用示例 const baseVertices = [[0,0], [2,0], [3,1], [1,2], [0,1]]; // 凹凸多边形 // 顶面:法向量(0.2, 0.3, 1),平面方程0.2x + 0.3y + z = 2 const topPlane = new THREE.Plane(new THREE.Vector3(0.2, 0.3, 1).normalize(), -2); // 底面:默认z=0平面,可自定义倾斜,比如new THREE.Plane(new THREE.Vector3(0.1, 0, 1).normalize(), 0) const prismGeometry = createTiltedPrism(baseVertices, topPlane); const material = new THREE.MeshStandardMaterial({ color: 0x42a5f5 }); const prism = new THREE.Mesh(prismGeometry, material); scene.add(prism);
方案优势
- 完全适配任意凹凸多边形:依赖Three.js内置的多边形三角化工具,无需额外处理凹凸逻辑
- 精准控制倾斜平面:通过
THREE.Plane定义顶面/底面,支持任意方向的倾斜,避免平移旋转的局限性 - 拓扑结构稳定:手动生成索引,不会出现法向量识别错误或顶点冲突问题,渲染效果可靠
- 扩展性强:通过
basePlane参数直接支持底面独立倾斜,无需大幅修改核心逻辑
内容的提问来源于stack exchange,提问作者Sumadithya
相关产品推荐
相关产品推荐

