
Three.js ShapeGeometry 详解从二维 Shape 到可三角化的多边形平面几何【免费下载链接】three.jsJavaScript 3D Library.项目地址: https://gitcode.com/GitHub_Trending/th/three.jsThree.js 的ShapeGeometry是一个专门用于将二维Shape路径轮廓转换为平面多边形网格的内置几何体类它继承自BufferGeometry常用于绘制带圆角/贝塞尔曲线的贴片、平面文字底板、地图轮廓、SVG 导入图形等场景。本文基于 官方 API 文档 与 源码实现 展开讲清其构造参数、默认行为、多材质分组、UV 与世界坐标的关系以及toJSON/fromJSON序列化机制帮助你在项目中正确地由Shape路径生成并渲染平面几何。核心概念从 Shape 到三角化的平面网格ShapeGeometry的定位是由一个或多个路径形状path shapes创建单面多边形几何体Creates an one-sided polygonal geometry from one or more path shapes。与ExtrudeGeometry共享同一套Shape输入但ShapeGeometry不做挤出只在z 0平面上把轮廓填充为三角形。官方文档给出的标准示例源自 文档页面与 源码 JSDoc 完全一致const arcShape new THREE.Shape() .moveTo( 5, 1 ) .absarc( 1, 1, 4, 0, Math.PI * 2, false ); const geometry new THREE.ShapeGeometry( arcShape ); const material new THREE.MeshBasicMaterial( { color: 0x00ff00, side: THREE.DoubleSide } ); const mesh new THREE.Mesh( geometry, material ) ; scene.add( mesh );从 实现源码 可以看到生成流程对每个Shape调用shape.extractPoints( curveSegments )把路径采样为顶点数组含孔洞holes检查并纠正轮廓的绕向winding order调用ShapeUtils.triangulateShape完成三角剖分写入position、normal、uv三组属性与索引缓冲。绕向Clockwise/Counterclockwise的自动纠正源码中最容易被忽视的细节是绕向处理ShapeGeometry.js#L101-L119外轮廓必须是顺时针CWif ( ShapeUtils.isClockWise( shapeVertices ) false ) shapeVertices shapeVertices.reverse();孔洞必须是逆时针CCW孔洞若为顺时针则会被反转。判断依据是 ShapeUtils.isClockWise其内部用鞋带公式计算有向面积ShapeUtils.area( pts ) 0即为顺时针。这意味着你手写moveTo/lineTo/bezierCurveTo时不必刻意控制顶点顺序几何体构造器会自动纠正保证 Earcut 三角剖分输入一致。Shape类src/extras/core/Shape.js继承自Path其holes属性文档明确要求孔洞定义必须与外轮廓使用相反的绕向CW/CCW——ShapeGeometry在构造时兜底纠正了这一点。三角剖分基于 Earcut三角化由 ShapeUtils.triangulateShape 完成先把外轮廓与各孔洞顶点拼成扁平坐标数组并记录孔洞起始索引然后交给内置的 Earcut 实现import { Earcut } from ./Earcut.js见 ShapeUtils.js#L1。Earcut 是业界常用的多边形含洞三角剖分算法因此ShapeGeometry可以正确处理带任意数量孔洞的形状例如const shape new THREE.Shape(); shape.moveTo( 0, 0 ).lineTo( 10, 0 ).lineTo( 10, 10 ).lineTo( 0, 10 ); const hole new THREE.Path(); hole.moveTo( 2, 2 ).lineTo( 8, 2 ).lineTo( 8, 8 ).lineTo( 2, 8 ); shape.holes.push( hole ); const geometry new THREE.ShapeGeometry( shape ); // 自动生成方框构造函数与参数new ShapeGeometry( shapes, curveSegments )构造签名与文档一致两个参数均可选参数类型默认值说明shapesShape \| ArrayShape一个内置三角形单个形状或形状数组curveSegmentsnumber12每条曲线弧线、贝塞尔曲线等的采样段数源码中的默认形状是一个小三角形ShapeGeometry.js#L32new Shape( [ new Vector2( 0, 0.5 ), new Vector2( - 0.5, - 0.5 ), new Vector2( 0.5, - 0.5 ) ] )因此new THREE.ShapeGeometry()不传参时也会得到一个可渲染的默认三角形单元测试 中即以此验证实例化与type ShapeGeometry。curveSegments控制曲线细分精度示例中的absarc半径 4 的圆弧会被采样为curveSegments个线段。值越大圆弧越平滑顶点数也线性增长对纯直线lineTo构成的形状该参数不产生影响。多形状与多材质分组传入Shape数组时源码会按索引为每个形状建立渲染分组ShapeGeometry.js#L64-L81if ( Array.isArray( shapes ) false ) { addShape( shapes ); } else { for ( let i 0; i shapes.length; i ) { addShape( shapes[ i ] ); this.addGroup( groupStart, groupCount, i ); // enables MultiMaterial support groupStart groupCount; groupCount 0; } }addGroup( start, count, materialIndex )让数组中第i个形状对应材质数组的第i个材质因此可以这样为每个形状上不同颜色const geometry new THREE.ShapeGeometry( [ shapeA, shapeB, shapeC ] ); const mesh new THREE.Mesh( geometry, [ matA, matB, matC ] );这是文档shapes : Array. 参数背后实际的工程价值所在。属性 .parametersthis.parameters { shapes: shapes, curveSegments: curveSegments };parameters保存构造时使用的参数ShapeGeometry.js#L45-L48文档明确说明实例化之后修改parameters不会改变已生成的几何体——它只用于序列化记录。需要重新生成几何时应创建新的ShapeGeometry实例而不是改参数。copy()方法会通过Object.assign( {}, source.parameters )浅拷贝该属性ShapeGeometry.js#L163-L171。生成的缓冲属性position / normal / uvaddShape内部ShapeGeometry.js#L132-L157对每个顶点写入vertices.push( vertex.x, vertex.y, 0 ); // positionz 恒为 0 normals.push( 0, 0, 1 ); // normal恒指向 Z uvs.push( vertex.x, vertex.y ); // uv直接使用 2D 世界坐标三条实践结论几何体始终落在z 0平面法线全部为(0, 0, 1)因此从背面看网格时材质必须设置side: THREE.DoubleSide官方示例即如此UV 就是顶点的 x/y 世界坐标并非归一化到 0–1。这意味着直接贴纹理时纹理会按世界单位重复。官方示例 examples/webgl_geometry_shapes.html 中有明确注释和配套写法// note: default UVs generated by THREE.ShapeGeometry are simply // the x- and y-coordinates of the vertices texture.wrapS texture.wrapT THREE.RepeatWrapping; texture.repeat.set( 0.008, 0.008 );即用RepeatWrapping 缩小repeat相当于对 UV 做缩放才能得到正确的纹理密度法线固定为 Z意味着该几何体只适合平面光照模型若需要更真实的法线例如配合MeshPhongMaterial的平滑效果通常要自行computeVertexNormals()或改用ExtrudeGeometry挤出薄层。序列化toJSON 与静态方法 fromJSONtoJSON()的定制部分ShapeGeometry.js#L173-L231把parameters.shapes转换为shape 的 uuid 数组写入data.shapes而不是内联形状数据data.shapes []; if ( Array.isArray( shapes ) ) { for ( let i 0, l shapes.length; i l; i ) { data.shapes.push( shapes[ i ].uuid ); } } else { data.shapes.push( shapes.uuid ); }对应地文档中的静态工厂方法签名是ShapeGeometry.fromJSON( data : Object, shapes : Array. ) : ShapeGeometrydata序列化后的 JSON 对象其中data.shapes是一个形状索引/uuid 序列data.curveSegments记录段数shapes外部提供的形状数组因为 JSON 里只有 uuid 指针真正的Shape实例必须由调用方按序传入Returns新的ShapeGeometry实例。实现逻辑ShapeGeometry.js#L191-L205static fromJSON( data, shapes ) { const geometryShapes []; for ( let j 0, jl data.shapes.length; j jl; j ) { const shape shapes[ data.shapes[ j ] ]; geometryShapes.push( shape ); } return new ShapeGeometry( geometryShapes, data.curveSegments ); }这解释了为什么fromJSON需要两个参数几何体的 JSON 只保存形状引用形状本体的数据属于Shape序列化体系Shape.toJSON 会序列化为uuid、holes与路径数据由加载器如 JSONLoader 生态负责先把形状数组还原出来再传给fromJSON重建几何。与 Shape / Path 生态的配合ShapeGeometry的上游是 Shape继承自Path其extractPoints( divisions )方法返回{ shape, holes }两组 2D 点数组Shape.js#L97-L106这正是curveSegments参数的作用点——它作为divisions传入采样过程决定每条曲线上取多少关键点。在实际项目中Shape对象除了手写路径外还常由解析器批量产出。仓库examples/jsm目录下有现成的来源SVGLoader把 SVG 路径解析为Shape[]可直接作为ShapeGeometry的数组参数渲染平面矢量图FontLoader字体字形轮廓同样解析为Shape可先用ShapeGeometry生成平面字面再按需替换为ExtrudeGeometry立体字。官方演示 examples/webgl_geometry_shapes.html 同时展示了同一组Shape分别经ShapeGeometry平面、ExtrudeGeometry挤出和Line轮廓线三种渲染方式是理解同一 Shape、不同几何产物的最佳参考。单元测试验证test/unit/src/geometries/ShapeGeometry.tests.js 对核心契约做了回归验证object instanceof BufferGeometry为真确认继承链EventDispatcher → BufferGeometry → ShapeGeometry与文档页头部 Inheritance 标注一致可以无参实例化验证默认三角形参数type属性恒为字符串ShapeGeometry。测试还展示了典型的三角形路径写法const triangleShape new Shape(); triangleShape.moveTo( 0, - 1 ); triangleShape.lineTo( 1, 1 ); triangleShape.lineTo( - 1, 1 ); new ShapeGeometry( triangleShape );使用要点小结ShapeGeometry生成的是z0单面平面背面观察必须side: THREE.DoubleSidecurveSegments默认12仅影响曲线采样纯直线形状无需调整外轮廓 CW、孔洞 CCW 的绕向由构造器自动纠正手写路径无需纠结顶点顺序传Shape数组即自动建立多材质 group每个形状对应material数组中的同下标材质UV 为顶点的 2D 世界坐标而非 0–1 归一化贴图时配合RepeatWrapping与repeat缩放参考 webgl_geometry_shapes.html序列化时data.shapes只存 uuid 指针反序列化必须通过ShapeGeometry.fromJSON( data, shapes )并外部提供形状数组修改geometry.parameters不会重建几何重新生成请新建实例。主要参考文件API 文档、核心实现、Shape 类、ShapeUtils绕向与 Earcut 三角化、单元测试、官方示例。【免费下载链接】three.jsJavaScript 3D Library.项目地址: https://gitcode.com/GitHub_Trending/th/three.js创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考