three.js Wireframe 深度指南基于粗线条Wide Line实现的网格线框类【免费下载链接】three.jsJavaScript 3D Library.项目地址: https://gitcode.com/GitHub_Trending/th/three.js本指南围绕 three.js 官方扩展中的Wireframe类展开讲解如何使用加粗线条而非传统gl.LINE来渲染网格线框覆盖其在 WebGL 与 WebGPU 渲染器下的导入差异、构造与使用流程以及computeLineDistances()虚线渲染所依赖的距离计算的源码实现。读完本文你将掌握一套可独立编写「任意线宽 虚线 双端渐变着色」线框的完整方案并能读懂 fat lines 系列扩展的底层数据组织方式。什么是 Wireframe基于宽线的线框渲染在 three.js 中传统网格线框渲染是借助核心类WireframeGeometry与LineSegments/LineBasicMaterial组合由 GPU 以gl.LINE方式绘制其线宽受 WebGL 限制大多数平台固定为 1 像素且无法逐段着色。而本文介绍的Wireframe是 three.js 官方 addonfat lines 扩展族的一员它基于宽线wide lines来构建线框每条边都是一段有宽度的独立线段从而支持可自由设置的像素级线宽如linewidth: 5直线与虚线dash/gap风格起点/终点独立的逐实例颜色instanceColorStart/instanceColorEnd与逐实例距离属性为着色器提供自由度。因此它常用于 CAD 编辑器的网格叠加显示、可视化工具的边界强调、以及需要精细但醒目的结构线框展示等场景。文档中给出它的继承链为EventDispatcher→Object3D→Mesh→Wireframe也就是说Wireframe本质上是一个以LineSegmentsGeometry为几何体、LineMaterial为材质的Mesh真正渲染时由着色器把每条实例化线段扩展成带宽度的四边形quad。这一点可以从源码中得到印证examples/jsm/lines/Wireframe.js 中类声明即为class Wireframe extends Mesh。渲染器限定与导入方式Wireframe属于 three.js 的 addon附加组件不会被包含在three核心包中必须显式导入。同时需要特别注意渲染器的差异这也是官方文档明确强调的一点使用WebGLRenderer时从lines/Wireframe.js导入使用WebGPURenderer时请改为从lines/webgpu/Wireframe.js导入对应实现。两类对象都实现了相同的 API 语义同样是extends Mesh、isWireframe true、拥有computeLineDistances()差别在于维度WebGL 版WebGPU 版导入路径three/addons/lines/Wireframe.jsthree/addons/lines/webgpu/Wireframe.js默认材质LineMaterialLine2NodeMaterial额外行为在onBeforeRender()中同步resolutionuniform详见后文无WebGL 版导入示例需配合 import map将three/addons/指向./jsm/import { Wireframe } from three/addons/lines/Wireframe.js; import { WireframeGeometry2 } from three/addons/lines/WireframeGeometry2.js;WebGPU 版对应导入import { Wireframe } from three/addons/lines/webgpu/Wireframe.js; import { WireframeGeometry2 } from three/addons/lines/WireframeGeometry2.js;快速上手完整可运行的代码骨架将官方文档的最小示例展开为一个可直接替换进渲染循环的完整脚本参考 examples/webgl_lines_fat_wireframe.html 的写法import * as THREE from three; import { OrbitControls } from three/addons/controls/OrbitControls.js; import { LineMaterial } from three/addons/lines/LineMaterial.js; import { Wireframe } from three/addons/lines/Wireframe.js; import { WireframeGeometry2 } from three/addons/lines/WireframeGeometry2.js; // 1. 任意 BufferGeometry 派生几何体 const geo new THREE.IcosahedronGeometry( 20, 1 ); // 2. 从网格几何体生成“线段化”的宽线几何体 const wireframeGeometry new WireframeGeometry2( geo ); // 3. 宽线材质像素线宽 可选虚线 const material new LineMaterial( { color: 0x4080ff, linewidth: 5, // 单位为像素 dashed: false } ); // 4. 组装为 Wireframe 并加入场景 const wireframe new Wireframe( wireframeGeometry, material ); // 5. 虚线渲染必需为每个顶点累积“距线段起点的长度” wireframe.computeLineDistances(); scene.add( wireframe ); // 6. 材质是 ShaderMaterial需要把分辨率传给着色器 // WebGL 版 Wireframe 的 onBeforeRender 已自动处理见下文构造器详解new Wireframe( geometry : LineSegmentsGeometry, material : LineMaterial )geometry线段几何体。文档规定类型为LineSegmentsGeometry。在实际使用中最自然的做法是传入由网格几何体包装而来的WireframeGeometry2也可以手动传入LineSegmentsGeometry实例配合setPositions()等方法填充数据。material线条材质。文档规定为LineMaterialWebGPU 路径下为Line2NodeMaterial。两个参数在源码中均带默认值examples/jsm/lines/Wireframe.jsconstructor( geometry new LineSegmentsGeometry(), material new LineMaterial( { color: Math.random() * 0xffffff } ) ) { super( geometry, material ); this.isWireframe true; this.type Wireframe; }也就是说即使你直接new Wireframe()也能得到一个可渲染的对象材质颜色会随机生成。但为了可控的视觉效果显式传入自己的几何体与材质是推荐做法。构造完成后Wireframe继承自Mesh的一切能力position、rotation、scale、visible、图层管理等均可直接使用例如示例代码中的wireframe.scale.set( 1, 1, 1 )。属性isWireframe只读类型标记.isWireframe : booleanreadonly默认值为true。该标志用于类型测试type testing与 three.js 核心类上普遍存在的isLineSegments、isMesh等标志是同一设计风格。可以通过它判断一个对象是否是Wireframeobject.isWireframe true; // 用于分支判断或通用处理函数方法computeLineDistances() 及其源码原理.computeLineDistances() : Wireframe用途为渲染虚线dashed lines预先计算一组距离值。对于几何体中的每一个顶点该方法都会计算从当前点到整条线最起始点的累积长度渲染虚线时着色器需要用这些值来决定该处落在 dash 段还是 gap 段。返回当前实例的引用因此可以链式调用。下面我们逐行剖析 examples/jsm/lines/Wireframe.js 中该方法的实现理解它做了什么computeLineDistances() { const geometry this.geometry; const instanceStart geometry.attributes.instanceStart; const instanceEnd geometry.attributes.instanceEnd; const lineDistances new Float32Array( 2 * instanceStart.count ); for ( let i 0, j 0, l instanceStart.count; i l; i , j 2 ) { _start.fromBufferAttribute( instanceStart, i ); _end.fromBufferAttribute( instanceEnd, i ); lineDistances[ j ] ( j 0 ) ? 0 : lineDistances[ j - 1 ]; lineDistances[ j 1 ] lineDistances[ j ] _start.distanceTo( _end ); } const instanceDistanceBuffer new InstancedInterleavedBuffer( lineDistances, 2, 1 ); // d0, d1 geometry.setAttribute( instanceDistanceStart, new InterleavedBufferAttribute( instanceDistanceBuffer, 1, 0 ) ); // d0 geometry.setAttribute( instanceDistanceEnd, new InterleavedBufferAttribute( instanceDistanceBuffer, 1, 1 ) ); // d1 return this; }实现要点可归纳为宽线几何体的每条线段被表示为一对起点/终点实例属性instanceStart与instanceEnd每个实例对应两个Vector3方法分配2 * 实例数长度的Float32Array对第j个线段写入两个值d0该段起点的累积线长与d1该段终点即起点加上本段长度start.distanceTo(end)最后把这些距离值打包成一个InstancedInterleavedBuffer步长 2并分别挂载为instanceDistanceStart与instanceDistanceEnd两个交错interleaved属性。之后LineMaterial的着色器会通过USE_DASH宏与instanceDistanceStart/instanceDistanceEnd声明见 examples/jsm/lines/LineMaterial.js在片元阶段做 dash/gap 判断。LineMaterial中与虚线相关的 uniform 定义同样位于该文件的UniformsLib.line包括uniform默认值含义linewidth1线宽像素或世界单位取决于worldUnitsworldUnits1线宽是否以世界单位计resolutionVector2当前视口分辨率用于像素线宽换算dashScale1虚线整体缩放比例dashSize1单个 dash 段的长度gapSize1dash 段之间的间隙长度dashOffset0虚线起始偏移一个值得注意的细节虚线模式是通过defines.USE_DASH编译期宏而非 uniform 来启用的因此在运行时切换虚线时需要手动维护宏例如官方示例中的做法if ( val ) matLine.defines.USE_DASH ; else delete matLine.defines.USE_DASH; matLine.needsUpdate true; // 触发重编译从网格几何体到宽线几何体WireframeGeometry2严格来说Wireframe类的搭档是LineSegmentsGeometry真正把普通网格几何体转换成可被宽线渲染的线段数据的是配套类WireframeGeometry2const geometry new THREE.IcosahedronGeometry(); const wireframeGeometry new WireframeGeometry2( geometry ); // 注意不是传 Wireframe查看其源码 examples/jsm/lines/WireframeGeometry2.js它的构造逻辑非常精简class WireframeGeometry2 extends LineSegmentsGeometry { constructor( geometry ) { super(); this.isWireframeGeometry2 true; this.type WireframeGeometry2; this.fromWireframeGeometry( new WireframeGeometry( geometry ) ); } }即先用 three 核心的WireframeGeometry把任意BufferGeometry的三角形边去重提取为线段集合再通过fromWireframeGeometry()把位置数据灌入LineSegmentsGeometry内部。而LineSegmentsGeometry.fromWireframeGeometry()examples/jsm/lines/LineSegmentsGeometry.js实际调用的是setPositions( geometry.attributes.position.array )。在 examples/jsm/lines/LineSegmentsGeometry.js 的setPositions()中可以看到宽线数据的核心组织方式const instanceBuffer new InstancedInterleavedBuffer( lineSegments, 6, 1 ); // xyz, xyz this.setAttribute( instanceStart, new InterleavedBufferAttribute( instanceBuffer, 3, 0 ) ); // xyz this.setAttribute( instanceEnd, new InterleavedBufferAttribute( instanceBuffer, 3, 3 ) ); // xyz this.instanceCount this.attributes.instanceStart.count;也就是说position数组中每6 个浮点数描述一条线段起点 xyz 终点 xyz它们被打包进同一个交错缓冲并分别暴露为instanceStart、instanceEnd两个实例属性。调用后几何体还会自动重算包围盒与包围球。LineSegmentsGeometry还提供了一组对应的数据入口均可按需组合使用setPositions( array )—— 手动指定线段端点长度须为 6 的倍数setColors( array )—— 设置每段起点/终点颜色instanceColorStart/instanceColorEnd长度须为 6 的倍数fromWireframeGeometry( geometry )—— 从WireframeGeometry取数fromEdgesGeometry( geometry )—— 从EdgesGeometry取数fromMesh( mesh )—— 直接从网格的几何体生成线框数据fromLineSegments( lineSegments )—— 从普通LineSegments要求无索引取数。Wireframe文档正文中注释掉的 set colors, maybe 说明逐实例颜色通道已预留可自行扩展着色逻辑。WebGL 版独有的 onBeforeRender 细节官方文档只列了构造器、属性与方法三项但其实 WebGL 版Wireframe还覆写了onBeforeRender()examples/jsm/lines/Wireframe.js它在每次渲染前自动把当前视口尺寸写入材质的resolutionuniformonBeforeRender( renderer ) { const uniforms this.material.uniforms; if ( uniforms uniforms.resolution ) { renderer.getViewport( _viewport ); this.material.uniforms.resolution.value.set( _viewport.z, _viewport.w ); } }LineMaterial基于ShaderMaterial构建其顶点着色器需要用resolution将像素线宽换算到屏幕空间从而保证不同窗口大小下线条宽度表现一致。由于Wireframe已自动处理这一步使用WireframeLineMaterial时你通常不需要手动设置resolution但在某些自定义材质或离屏渲染多 viewport/scissor场景下应意识到这一机制的存在。WebGPU 版本对照WebGPU 版 examples/jsm/lines/webgpu/Wireframe.js 与 WebGL 版在语义上完全平行差异点集中在材质体系上默认材质从LineMaterial换成Line2NodeMaterial从three/webgpu导入的节点式材质computeLineDistances()的实现与 WebGL 版逐字一致同样生成instanceDistanceStart/instanceDistanceEnd由于Line2NodeMaterial的内部 uniform 由渲染器管理不再需要 WebGL 版那种手动同步resolution的onBeforeRender()钩子。配套的完整示例可对照 examples/webgpu_lines_fat_wireframe.html 与 WebGL 示例 examples/webgl_lines_fat_wireframe.html 阅读两者结构几乎一一对应。实际使用建议与注意事项务必调用computeLineDistances()只有当你使用虚线dashed: true时才真正需要它但即便使用实线额外调用一次也几乎无成本官方示例webgl_lines_fat_wireframe.html中也是无条件调用可避免后续切换到虚线时漏算。dashed是编译期宏如前面示例所示运行时切换虚线需要同步增删defines.USE_DASH并置needsUpdate true否则看不到效果。Wireframe会渲染网格的全部三角边因此复杂网格会产生大量线段实例如需只保留轮廓/边界可先基于EdgesGeometry精简数据再配合LineSegmentsGeometry.fromEdgesGeometry()使用。选择正确的渲染器入口WebGLRenderer 下误用 webgpu 目录中的类会因Line2NodeMaterial依赖three/webgpu而产生不兼容反之亦然。这是 fat lines 家族Line2、LineSegments2、Wireframe共通的约定。与内联的对照示例结合学习仓库自带的 examples/webgl_lines_fat_wireframe.html 同时渲染了宽线线框与基于THREE.WireframeGeometry LineBasicMaterial的gl.LINE线框并用 GUI 提供了线宽、虚线比例2:1、1:1、1:2的实时对比开关是理解两类方案差异的最佳活教材。相关扩展阅读类实现examples/jsm/lines/Wireframe.js、WebGPU 版 examples/jsm/lines/webgpu/Wireframe.js配套几何体WireframeGeometry2examples/jsm/lines/WireframeGeometry2.js与其基类LineSegmentsGeometryexamples/jsm/lines/LineSegmentsGeometry.js宽线材质LineMaterialexamples/jsm/lines/LineMaterial.jsLineMaterial对应的worldUnits、linewidth、dashSize/gapSize/dashScale/dashOffset等全部参数均可在此确认同族的独立线段类LineSegments2与连续折线类Line2见 examples/jsm/lines 目录它们是理解Wireframe底层四边形拉伸渲染算法的最好旁证官方示例webgl_lines_fat_wireframe.html 与 webgpu_lines_fat_wireframe.html【免费下载链接】three.jsJavaScript 3D Library.项目地址: https://gitcode.com/GitHub_Trending/th/three.js创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考