首页
/ three.js 虚线线条材质 LineDashedMaterial:从参数配置到着色器实现原理

three.js 虚线线条材质 LineDashedMaterial:从参数配置到着色器实现原理

2026-09-07 14:28:11作者:房伟宁

导读

LineDashedMaterial 是 three.js 中用于渲染虚线线段的材质,它继承自 LineBasicMaterial,专为 THREE.LineTHREE.LineSegmentsTHREE.LineLoop 这类线条图元提供“实线 + 间隔”的周期化描边效果。阅读本文后,你将掌握 LineDashedMaterial 的完整参数语义(dashSize/gapSize/scale)、前置条件 computeLineDistances() 的正确用法,并能结合着色器源码理解虚线在 GPU 上到底是如何被“切断”出来的。本文以 官方材质文档 为主体骨架,同时对照 源码实现官方示例 进行源码级展开。

LineDashedMaterial 是什么

一句话概括:材质(Material)决定了可渲染 3D 对象的外观,而 LineDashedMaterial 负责让线以虚线段的形式呈现。它的完整继承关系为:

EventDispatcher → Material → LineBasicMaterial → LineDashedMaterial

这一继承链意味着它自动拥有 Material 基类(透明度、深度测试、侧向剔除等)与 LineBasicMaterial(颜色、线宽等)的全部能力,仅在之上叠加了虚线专属的控制参数。

从源码角度确认类结构(src/materials/LineDashedMaterial.js):

  • 构造时先 super() 调用 LineBasicMaterial 初始化公共属性;
  • 随后注册类型标识 this.isLineDashedMaterial = true 与字符串 this.type = 'LineDashedMaterial'
  • 声明三个专属属性的默认值:scale = 1dashSize = 3gapSize = 1
  • 末尾调用 this.setValues( parameters ),将传入的参数对象批量写入材质——这也是所有 three.js 内置材质统一的初始化协议(setValues 定义于 Material 基类)。

补充:isLineDashedMaterial 这类布尔标记被 WebGL 渲染器广泛用于运行期类型分派(例如 WebGLMaterials.js 中先判断 isLineBasicMaterial,再在 isLineDashedMaterial 为真时额外刷新虚线 uniform),因此它是一个只读的、面向引擎内部的类型探测标志,业务代码中一般不需要直接依赖它。

基础用法与最小可运行示例

官方文档给出的构造示例即为最标准的用法(文档原文):

const material = new THREE.LineDashedMaterial( {
	color: 0xffffff,
	scale: 1,
	dashSize: 3,
	gapSize: 1,
} );

但仅创建材质是不够的——虚线要真正显示,还必须在承载它的线条对象上调用 computeLineDistances()。完整的最小场景如下:

import * as THREE from 'three';

// 1. 准备一组顶点,生成非索引 BufferGeometry
const points = [];
points.push( new THREE.Vector3( - 10, 0, 0 ) );
points.push( new THREE.Vector3(   0, 5, 0 ) );
points.push( new THREE.Vector3(  10, 0, 0 ) );

const geometry = new THREE.BufferGeometry().setFromPoints( points );

// 2. 创建虚线材质
const material = new THREE.LineDashedMaterial( {
	color: 0x00ff00,
	dashSize: 3,      // 每段虚线的长度(世界单位)
	gapSize: 1,       // 虚线之间间隔的长度(世界单位)
} );

// 3. 创建 Line 对象并「计算线距」——虚线显示的关键前置步骤
const line = new THREE.Line( geometry, material );
line.computeLineDistances();

scene.add( line );

对照仓库中的官方示例 webgl_lines_dashed.html,可以看到同样的模式被应用在两类对象上:

// 样条曲线采样得到的连续 Line
const line = new THREE.Line( geometrySpline,
	new THREE.LineDashedMaterial( { color: 0xffffff, dashSize: 1, gapSize: 0.5 } ) );
line.computeLineDistances();

// 立方体线框使用的 LineSegments(分段连线)
const lineSegments = new THREE.LineSegments( geometryBox,
	new THREE.LineDashedMaterial( { color: 0xffaa00, dashSize: 3, gapSize: 1 } ) );
lineSegments.computeLineDistances();

两份代码均在构造材质后紧跟着调用 computeLineDistances(),这正是虚线渲染的正确使用范式。

虚线的前置条件:computeLineDistances() 与 lineDistance 属性

为什么必须调用 computeLineDistances()?因为虚线着色器需要知道每个顶点相对线段起点的累计长度,才能均匀地把“实线/间隔”沿整条线铺开。

逐行解读源码算法

该方法定义在 src/objects/Line.js,核心逻辑非常清晰:

  1. 只处理**非索引(non-indexed)**几何体:若 geometry.index !== null,直接调用 warn() 提示“仅支持非索引 BufferGeometry”并返回,因为索引几何体的顶点共享方式会破坏逐点累计距离的正确性。
  2. 新建数组 lineDistances,首顶点到自身距离记为 0
  3. 从第 2 个顶点起逐个遍历 position 属性,累加前一个顶点到当前顶点的直线距离:
lineDistances[ 0 ] = 0;
// 对 i = 1..count-1:
//   _vStart = position[i-1]; _vEnd = position[i];
//   lineDistances[i] = lineDistances[i-1] + _vStart.distanceTo( _vEnd );
  1. 最终把累计距离数组包装为 Float32BufferAttribute,写入几何体的名为 lineDistance 的顶点属性:
geometry.setAttribute( 'lineDistance', new Float32BufferAttribute( lineDistances, 1 ) );

也就是说,computeLineDistances() 的本质是:为几何体新增一个逐顶点的 lineDistance 属性,其值为“当前顶点沿折线到起点的折线长度”。随后虚线着色器读取该属性完成周期切分。若创建了虚线材质却不调用 computeLineDistances()lineDistance 属性缺失或恒为 0,着色器将无法形成预期的虚实交替,虚线便不会正确显示——这是初学者最容易踩的坑。

使用限制小结

  • 只适用于 Line 及其子类(LineSegmentsLineLoop),它们共享 Line.js 中的该方法;普通 Mesh 不可用。
  • 几何体必须是非索引的 BufferGeometry
  • 因为距离在 CPU 侧预先算好,当几何体顶点位置发生动态修改时,需要重新调用 computeLineDistances() 以刷新 lineDistance 属性。

专属属性详解:dashSize、gapSize 与 scale

文档页面为 LineDashedMaterial 定义了三个专属属性(文档原文),其默认值与含义归纳如下:

属性 类型 默认值 含义
dashSize number 3 虚线中“实线段”的长度,与间隔一起构成一个完整周期。
gapSize number 1 虚线中“间隔段”的长度。
scale number 1 对虚线周期整体的缩放因子,值越大,单位长度内重复的虚实周期越少、视觉效果越“稀疏”。

三个属性的底层语义

三者并非简单的“样式描述”,它们的实际作用在 WebGL 渲染管线中被严格实现:

顶点着色器阶段linedashed.glsl.js):

uniform float scale;
attribute float lineDistance;
varying float vLineDistance;
// ...
vLineDistance = scale * lineDistance;

即每个顶点的线距先被 scale 放大,作为变化量传入片元阶段。

片元着色器阶段(同文件 L33-L75):

uniform float dashSize;
uniform float totalSize;
varying float vLineDistance;
// ...
if ( mod( vLineDistance, totalSize ) > dashSize ) {
	discard;   // 丢弃像素 → 形成“间隔”空洞
}

Uniform 同步阶段WebGLMaterials.js):

function refreshUniformsDash( uniforms, material ) {
	uniforms.dashSize.value = material.dashSize;
	uniforms.totalSize.value = material.dashSize + material.gapSize;  // 周期总长 = 实线 + 间隔
	uniforms.scale.value = material.scale;
}

综合三段代码可以得到完整的公式化理解:

  • 周期总长 totalSize = dashSize + gapSize
  • 每个像素所在的线距位置对 totalSize 取模,落在 (dashSize, totalSize] 区间内的像素一律 discard(被丢弃),于是视觉上呈现为间隔;
  • 落在 [0, dashSize] 区间内的像素正常着色,即为实线段;
  • scale 作用于 lineDistance,等价于把整条线上“厘米刻度”的间距拉伸,从而整体拉长虚实周期在真实世界坐标中的尺寸。

另外,渲染器在 ShaderLib.js 中为 dashed 程序预设了默认 uniforms:scale: 1dashSize: 1totalSize: 2src/renderers/shaders/ShaderLib.js),正式绘制时会由上面的 refreshUniformsDash 依据材质实例刷新覆盖。

直观配置技巧

  • 想让虚线更密集:把 dashSizegapSize 整体调小;想让虚线更稀疏:调大两者,或把 scale 调大。
  • scaledashSize/gapSize 的区别:前者是渲染阶段的整体缩放系数,后两者是“周期内部”的虚实比例与绝对尺寸。若只想改变疏密而不改变虚实占比,调整 scale 即可;若想改变“实线与间隔的比值”,则分别调整 dashSizegapSize

文档措辞的小提醒

文档在 dashSize 一词下写有 “This is both the gap with the stroke.”(文档原文),这一表述沿袭了源码注释里的措辞,实际语义应理解为:dashSize 是与间隔 gapSize 配套、共同构成“一实一虚”一个周期的实线段长度——上文的着色器 mod() 逻辑即为最终裁决依据。

构造参数与继承属性

构造函数

new LineDashedMaterial( parameters : Object )

parameters 是可选的单个对象,可容纳该材质及其所有继承链材质上的任意属性。文档特别强调:颜色类属性(如 color)可接受任何 Color#set 支持的值类型,例如十六进制数 0xffffff、CSS 颜色字符串 'red''#ff0000''rgb(255,0,0)' 乃至 THREE.Color 实例。

除前面已详述的三个专属属性外,从 LineBasicMaterial 继承的常用属性还包括:

属性 默认值 说明
color 0xffffff(白色) 线条颜色,通过 LineBasicMaterialsetValues 写入
opacity 1 不透明度,需配合 transparent = true 生效
transparent false 是否启用透明混合
linewidth 1 线宽。注意:多数 WebGL 平台对线宽有硬件限制(通常恒为 1px),视觉放大宽度需借助 fat lines 方案(见下文进阶小节)
depthTest / depthWrite —— Material 基类继承的渲染状态

LineBasicMaterial.js 源码可见,LineBasicMaterial 构造时同样以 setValues( parameters ) 收尾,且其 copy() 会逐项复制 color 等属性;而 LineDashedMaterial 的 copy() 在调用 super.copy() 之后补抄 scaledashSizegapSize,保证 material.clone() / copy() 时虚线参数不丢失。

isLineDashedMaterial : boolean(只读)

类型探测标识,默认 true。前面已说明其引擎用途;在业务代码中它也可以作为一种安全的鸭子类型判断:if ( material.isLineDashedMaterial ) { ... }

虚线渲染的完整调用链(源码级串联)

把本文涉及到的部件按一次实际绘制串联起来:

  1. 用户侧new THREE.LineDashedMaterial({...}) + line.computeLineDistances(),后者为几何体写入 lineDistance 顶点属性(Line.js)。
  2. 程序选择:WebGL 渲染器检测到材质携带 isLineDashedMaterial 标记后,选用 ShaderLib 中名为 dashed 的着色程序(ShaderLib.js),其顶点/片元源码即 linedashed.glsl.js
  3. Uniform 刷新:每帧通过 refreshUniformsDash()dashSizetotalSize = dashSize + gapSizescale 上传 GPU(WebGLMaterials.js)。
  4. GPU 切分:顶点着色器执行 vLineDistance = scale * lineDistance,片元着色器对 vLineDistancetotalSize 取模,模值大于 dashSize 的片元直接 discard,剩余片元按常规颜色管线(颜色、雾、色彩空间、色调映射等)输出,最终形成“实线—间隔—实线”的周期条纹。

值得留意的是,虚线模式本质上是在片元阶段“丢弃像素”实现的,因此它不改变几何的拓扑结构,光线投射(Raycaster)、包围球等计算与普通 Line 完全一致,只是外观呈现周期性空洞。

应用场景、注意事项与更进一步的方案

典型应用场景

  • 参考线、网格辅助线中的“虚线标注”,指示视觉上“不可穿越”或“待定”的区域;
  • 图纸/工程类可视化中的隐藏线、剖切线、中心线(在 webgl_lines_dashed.html 示例中,立方体线框即用 LineSegments + 虚线材质表达辅助信息);
  • 样条曲线/轨迹的“规划路径 vs 已行驶路径”区分,配合 dashSize: 1, gapSize: 0.5 这类细密参数使用;
  • 交互中的选中态描边、飞行路径动画等。

注意事项汇总

  1. 必须 computeLineDistances():漏掉它,虚线不会正确渲染,这是最高频的误用。
  2. 仅限非索引几何体:索引化 BufferGeometry 会触发引擎警告并跳过计算(Line.js)。
  3. 顶点动态更新后需重算lineDistance 是静态预计算的顶点属性。
  4. 线宽受限:若业务需要任意像素宽度的虚线,内置 LineBasicMaterial/LineDashedMaterial 不适用。此时可参考仓库中的 fat lines 方案:examples/jsm/lines/Line2 + LineMaterial)与对应演示 webgl_lines_fat_wireframe.htmlwebgpu_lines_fat.html 展示了粗细可控的虚线实现(其 LineMaterial 同样支持 dasheddashScale 等参数)。
  5. WebGPU 路径:若渲染器切换为 WebGPU(THREE.WebGPURenderer),应使用 TSL 节点材质体系的 LineDashedNodeMaterialsrc/materials/nodes/LineDashedNodeMaterial.js)而非内置的 GLSL 材质,以获得一致的虚线表现。

继续深入

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

项目优选

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