首页
/ three.js LineMaterial 深度指南:任意宽度的线框风格线条材质

three.js LineMaterial 深度指南:任意宽度的线框风格线条材质

2026-09-07 16:37:26作者:幸俭卉

LineMaterial 是 three.js 中用于绘制“加粗”线框风格几何体的专用材质。与受限于 GPU 固定管线、宽度恒为 1 像素的 LineBasicMaterial 不同,它支持任意线宽,并能切换为以世界单位定义尺寸。本指南将以 examples/jsm/lines/LineMaterial.js 为主线,完整讲解其继承关系、构造参数、全部属性语义,并结合 Line2LineSegments2 与官方示例剖析它的着色器实现与使用注意点,读完后你可以在 WebGL 渲染器中独立搭建可变线宽的线条场景(含虚线、顶点色、MSAA 抗锯齿)。

一、LineMaterial 是什么

LineMaterial 是绘制线框风格几何体(wireframe-style geometries)的材质。它的官方定义位于 LineMaterial.js

  • 继承链EventDispatcher → Material → ShaderMaterial → LineMaterial,本质上是一个自定义着色器的 ShaderMaterial
  • 不可替代的能力:与 LineBasicMaterial 不同,它支持任意线宽(arbitrary line widths),并允许用世界单位(world units)而不是屏幕空间像素单位来指定线宽。
  • 配套对象:它必须配合扩展几何体 LineSegments2(成对顶点线段集合)与 Line2(由 LineSegments2 扩展而来的连续折线)使用。

从源码可见其构造方式非常直接——复用内置 ShaderLib['line'] 着色器并开启裁剪支持:

// examples/jsm/lines/LineMaterial.js
class LineMaterial extends ShaderMaterial {
	constructor( parameters ) {
		super( {
			type: 'LineMaterial',
			uniforms: UniformsUtils.clone( ShaderLib[ 'line' ].uniforms ),
			vertexShader: ShaderLib[ 'line' ].vertexShader,
			fragmentShader: ShaderLib[ 'line' ].fragmentShader,
			clipping: true // required for clipping support
		} );
		this.isLineMaterial = true;
		this.setValues( parameters );
	}
}

渲染后端限制(WebGL / WebGPU)

LineMaterial 只能配合 WebGLRenderer 使用。在使用 WebGPURenderer 时,应改用基于节点系统(TSL)实现的 Line2NodeMaterial(源码位于 src/materials/nodes/Line2NodeMaterial.js)。仓库在 examples/jsm/lines/webgpu/ 目录下提供了 WebGPU 版的 Line2.jsLineSegments2.jsWireframe.js,导入路径相应变为 three/addons/lines/webgpu/...

二、导入方式

LineMaterial 属于 addon(附加模块),不在 three.js 核心构建中,必须显式导入(安装时通过 three/addons/ 别名或直接指向 examples/jsm/ 目录):

import { LineMaterial } from 'three/addons/lines/LineMaterial.js';

配套使用:

import { Line2 } from 'three/addons/lines/Line2.js';
import { LineGeometry } from 'three/addons/lines/LineGeometry.js';

也可以参考官方示例 examples/webgl_lines_fat.html 顶部的 importmap 配置方式:将 "three/addons/" 映射到 "./jsm/"

三、与 LineSegments2 / Line2 的协作关系

要理解 LineMaterial,必须先理解它消费的数据。它不与普通 BufferGeometry 直接配合,而是面向实例化线段几何体工作:

  • LineSegmentsGeometry 继承自 InstancedBufferGeometry。每个线段由一对 instanceStart / instanceEnd(以及可选的实例级颜色、距离)描述,材质内部把每条线段绘制成一块带“端帽扩展”的实例化四边形(quad)再进行计算。
  • 该几何体每段顶点数必须是 6 的倍数(每组 xyz xyz 定义一条线段的起点与终点)。
  • LineSegments2 继承自 Mesh,用于持有上述几何体与 LineMaterialLine2 再扩展 LineSegments2,把独立的线段首尾相接组成折线(多段线)。LineSegments2.computeLineDistances() 会为虚线渲染计算累计长度属性 instanceDistanceStart/End

一个最简组合:

const geometry = new LineGeometry();
geometry.setPositions( positions );   // 折线点列
geometry.setColors( colors );         // 可选顶点色

const material = new LineMaterial( { linewidth: 5, vertexColors: true } );
const line = new Line2( geometry, material );
line.computeLineDistances();          // 若使用虚线,必须调用
scene.add( line );

四、构造函数

new LineMaterial( parameters : Object )

构造一个新的线段材质。

parameters:一个对象,可包含一个或多个用于定义材质外观的属性。材质的任何属性(包括继承自 ShaderMaterial / Material 的属性)都可以在此传入;颜色值可传任何 Color#set 接受的类型(如十六进制数、'#ff0000'、CSS 颜色名或 THREE.Color 实例)。

const matLine = new LineMaterial( {
	color: 0xffffff,
	linewidth: 5,            // worldUnits=false 时单位为像素
	vertexColors: true,
	dashed: false,
	alphaToCoverage: true,
} );

构造函数内部通过 this.setValues( parameters ) 应用参数,因此上述大多数属性最终会映射到材质内部自定义 uniforms 上(见下表)。

五、属性详解

以下所有属性均有源码中的对应 getter/setter。需要特别指出:很多属性并非单纯的数值存储,而是直接作用于着色器编译期的 defines,切换它们会触发 needsUpdate = true,从而要求重新编译着色器。

.alphaToCoverage : boolean

是否启用 alphaToCoverage。启用后,在开启 MSAA 多重采样的情况下可以显著改善线条边缘的抗锯齿质量。

  • 默认:false
  • 覆盖(Overrides):ShaderMaterial#alphaToCoverage

源码中它对应 defines.USE_ALPHA_TO_COVERAGE(见 LineMaterial.js)。片段着色器里开启后会改用 smoothstep( 0.5 - fwidth(...), 0.5 + fwidth(...), ... ) 的软边缘计算,并配合 fwidth 让覆盖采样产生渐变。注意该特性在 GPU 上依赖 MSAA,需要在创建 WebGLRenderer 时设置 antialias: true(官方示例正是如此),硬件不支持时会退回普通(硬边 discard)路径。屏幕空间(非 worldUnits)模式下源码注释还提到:在条件分支内做导数(derivative)会在部分硬件上产生瑕疵,因此做了规避处理。

.color : Color

材质颜色。默认 (1,1,1) 即白色。它实际是内部 uniform diffuse 的访问入口:

get color() { return this.uniforms.diffuse.value; }
set color( value ) { this.uniforms.diffuse.value = value; }

.dashed : boolean

线条是虚线还是实线。默认 false

  • 默认:false

它对应 defines.USE_DASH(见 LineMaterial.js)。置为 true 会触发材质重编译,片段着色器中按“累计线长 + dashOffset 对 (dashSize+gapSize) 取模”来决定片段是绘制还是 discard(丢弃),同时端帽会被丢弃。注意:使用虚线前必须对几何体调用 computeLineDistances(),否则缺少 instanceDistanceStart/End 属性,虚线将无法正确排布(见第五节)。

.dashSize : number

单个短划线的长度(尺寸)。默认 1

.gapSize : number

短划线之间的空隙长度。默认 0(即实线),设置虚线时需要大于 0。

.dashScale : number

虚线与空隙的整体缩放系数。默认 1。它实际上用于在顶点着色器把累计距离放大/缩小,从而不改变几何体本身而整体缩放虚线疏密。

.dashOffset : number

虚线循环(dash cycle)从何处开始,即整条虚线的相位偏移。默认 0。改变它可以让虚线的“段落起点”沿线条前后移动(常用于模拟流动的动画线)。片段着色器中通过 mod( vLineDistance + dashOffset, dashSize + gapSize ) > dashSize 丢弃空隙片段。

.linewidth : number

控制线条粗细:

  • worldUnits = false(默认)时,单位为 CSS 像素(屏幕空间);
  • worldUnits = true 时,单位为 世界单位(随距离衰减)。

默认 1。覆盖 ShaderMaterial#linewidth。对应 uniform linewidth,源码在修改时会对 this.uniforms.linewidth 做存在性检查后写值(LineMaterial.js)。

.opacity : number

整体透明度。默认 1。覆盖 ShaderMaterial#opacity。实际映射 uniform opacity,并作为最终 alpha 输出。若想半透明线条获得正确混合,请结合父类 Materialtransparent 属性使用(LineMaterial 未自动开启透明)。

.resolution : Vector2

视口尺寸(屏幕像素)。必须保持为最新,屏幕空间渲染才能精确。LineSegments2.onBeforeRender 回调会对可见对象自动执行该更新:

// examples/jsm/lines/LineSegments2.js
onBeforeRender( renderer ) {
	const uniforms = this.material.uniforms;
	if ( uniforms && uniforms.resolution ) {
		renderer.getViewport( _viewport );
		this.material.uniforms.resolution.value.set( _viewport.z, _viewport.w );
	}
}

因此常规渲染场景下你通常无需手动维护;但如果线条在首次渲染之前就被用于射线拾取(Raycaster),或未走正常渲染流程,就需要自行设置,否则拾取会因 resolution 为 0 而提前返回(见 LineSegments2.js)。注意该分辨率取的是当前 viewport(非整张画布),示例中的画中画(inset viewport)渲染场景也能自动适配。

.worldUnits : boolean

材质尺寸(线宽、虚线的间距)是否使用世界单位。默认 false(屏幕空间 / 像素)。

  • 默认:false

它对应 defines.WORLD_UNITSLineMaterial.js),切换会导致着色器重编译。两条渲染路径差异较大:

  • 屏幕空间(默认):在 NDC 空间把线段法线方向按 linewidth / resolution.y 偏移,得到恒定像素宽度的线条,其着色器在顶点处对穿过相机近平面的线段做“裁剪修剪”,再结合端帽展开形成四边形。
  • 世界单位:在观察空间根据 linewidth 与世界方向构建 worldUp / worldFwd 正交基并偏移顶点,得到真实世界粗细的线(存在近大远小的透视衰减)。同时片段着色器改用“视线射线与线段最近距离”判定覆盖率,几何开销更高但物理感更强。

.isLineMaterial : boolean(只读)

类型测试标志,默认 true。用于在运行时快速区分该材质是否为 LineMaterial(相当于 instanceof 的轻量替代),源码中在构造器里直接硬编码为 true

继承自 ShaderMaterial 的可复用特性

因为继承自 ShaderMaterial,顶点色、雾、裁剪平面、对数深度缓冲等能力默认已被内置着色器支持:

  • 顶点色:设 vertexColors: true 即可按实例给线段两端分别着色,对应源码中的 instanceColorStart / instanceColorEnd 属性;
  • 裁剪平面:构造器中显式设置了 clipping: true
  • :uniform 合并了 UniformsLib.commonUniformsLib.fogUniformsLib.line(见 LineMaterial.js),所以场景雾效对线条生效。

六、内置着色器与 uniforms

LineMaterial 的着色器通过注册进全局 ShaderLib['line'] 的 GLSL 字符串定义。uniform 默认值集中在 UniformsLib.lineLineMaterial.js):

UniformsLib.line = {
	worldUnits: { value: 1 },
	linewidth: { value: 1 },
	resolution: { value: new Vector2() },
	dashOffset: { value: 0 },
	dashScale: { value: 1 },
	dashSize: { value: 1 },
	gapSize: { value: 1 } // todo FIX - maybe change to totalSize
};

顶点着色器大致流程:将实例起止点变换到相机空间 → 对透视投影下跨越近平面(z<0 一侧)的线段做裁剪(trimSegmentAlpha 计算插值系数,见 LineMaterial.js)→ 投影到 NDC → 屏幕空间路径按线段垂直方向构建像素偏移、世界单位路径按世界正交基构建偏移 → 输出 gl_Position 并追加雾与裁剪处理。片段着色器负责端帽圆形化(vUv 上的圆形 discard)、虚线取模 discard 以及 worldUnits 下的“点到线段距离”软裁剪。理解这些有助于排查两类典型问题:

  1. 相机穿入线段内部或线段横跨近裁剪面时出现贯穿闪烁——这是 NDC 空间三角形被近平面撕裂导致的,源码中专门做了线段裁剪与深度重叠规避(将 clip.z 重设为原线段 NDC 深度以免两个端帽深度排序冲突);
  2. 开启 alphaToCoverage 后仍有锯齿——通常是因为渲染器未开启 MSAA(antialias 需在创建 renderer 时指定)。

七、典型应用示例

官方示例 examples/webgl_lines_fat.html 是对 LineMaterial 能力的完整演示:它通过 GeometryUtils.hilbert3D 生成希尔伯特曲线采样点,再用 CatmullRomCurve3 插值得到光滑折线,配合逐点 HSL 渐变的顶点色构造 LineGeometry

const geometry = new LineGeometry();
geometry.setPositions( positions );
geometry.setColors( colors );

matLine = new LineMaterial( {
	color: 0xffffff,
	linewidth: 5,              // worldUnits=false 时即像素宽度
	vertexColors: true,
	dashed: false,
	alphaToCoverage: true,
} );

line = new Line2( geometry, matLine );
line.computeLineDistances();
scene.add( line );

同时该示例还渲染了一条使用 LineBasicMaterialgl.LINE_STRIP 固定管线)的普通细线作为对照:LineBasicMaterial 在多数平台上无法控制线宽、始终为 1px 且抗锯齿效果差,而 LineMaterial 支持像素级宽度与 MSAA 软边。其 GUI 面板演示了三个核心参数的运行时调节方式:

// 世界单位 / 像素单位切换:切换后需要重新编译(源码会自动置 needsUpdate)
matLine.worldUnits = val;
matLine.needsUpdate = true;

// 线宽调节(像素模式 1~10,世界模式 0.1~0.5)
matLine.linewidth = val;

// 虚线与整体疏密、占空比
matLine.dashed = true;
matLine.dashScale = val;        // 疏密
matLine.dashSize = 2;           // 例如 2:1
matLine.gapSize = 1;

使用虚线时的完整最小步骤

结合源码(LineSegments2.computeLineDistancesLineSegments2.js 中实现累计长度并写回 instanceDistanceStart/End)与示例,要点如下:

  1. 构建 LineGeometry/LineSegmentsGeometrysetPositions
  2. 创建 LineMaterial 时设置 dashed: truedashSize/gapSize/dashScale/dashOffset
  3. 创建 Line2/LineSegments2调用 computeLineDistances()
  4. 放入场景正常渲染。

八、拾取(Raycaster)注意事项

LineSegments2.raycastLineSegments2.js)对两种模式分别实现了精确的“射线—线段”求交:

  • 会依据材质 resolution 计算世界空间包围球/包围盒的扩展边距,以包含屏幕空间线宽,避免误剔除;
  • 屏幕空间模式下 Raycaster.camera 必须被设置,否则控制台会打印错误;同时若材质尚未渲染(resolution 为 0)会直接返回,因此先渲染一帧或用屏幕空间模式拾取前务必保证 resolution 有效;
  • 世界单位模式不依赖 camera,拾取边距直接取 linewidth * 0.5

若线条常与鼠标交互,建议直接观看官方示例 examples/webgl_lines_fat_raycasting.html(该页面同时演示了 LineSegments2 实例级拾取与 Line2 折线拾取)。

九、性能与使用建议小结

  • LineMaterial 基于实例化绘制,每段线段为一个实例化四边形,绘制大量短线段时比逐段生成 Mesh 更省状态切换;但每条线段的片元都在做额外的圆形端帽/距离计算,超大线宽 + 超长线段会放大片元开销。
  • 需要“恒定像素宽”的标注线、边界线用默认屏幕空间模式即可;需要随透视缩小(如模拟实体金属丝、头发丝)用 worldUnits: true
  • MSAA 场景开启 alphaToCoverage: true 可显著柔化边缘;在关闭 MSAA 的后处理链(如 SSAO、SSR 之后)中线条抗锯齿需另寻方案。
  • WebGPURenderer 用户应改用 Line2NodeMaterialsrc/materials/nodes/Line2NodeMaterial.js)与 examples/jsm/lines/webgpu/ 下的对象,两者 API 大体对齐但着色实现基于节点系统,渲染后端为 WebGPU。

如需深入了解配套几何体与 WebGPU 替代品,可继续阅读仓库中的 docs/pages/Line2NodeMaterial.html.mdexamples/jsm/lines/Line2.jsexamples/jsm/lines/LineSegmentsGeometry.js 与源码 examples/jsm/lines/LineMaterial.js

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.14 K
2.74 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
857
1.35 K
docsdocs
暂无描述
Markdown
897
5.81 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
531
595
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
920
1.84 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.63 K
1.02 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.36 K
1.46 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
1.02 K
518
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
547
389