打造高性能二维图纸渲染引擎系列(二):创建结构化和可扩展的渲染场景

在我上一篇文章【高性能 Web CAD Viewer:Batched Geometry 助你轻松渲染百万实体】中,我分享了 CAD-Viewer (Gitlab or Github) 如何通过将 CAD 图纸中的 Entity 进行 Geometry 合并在浏览器中实现快速渲染。这篇文章主要关注"如何高效绘制",减少 Draw Call 用并利用 GPU 友好的数据布局。

然而,Batched Geometry 只是故事的一部分。要真正有意义地渲染 CAD 图纸,我们还需要一个结构良好的渲染场景结构来统一管理所有内容。

这就是 CAD-ViewerAcTrScene 类的作用。它充当 CAD 的逻辑数据模型(Layout、Layer、Entity)与渲染引擎的 GPU 友好表示(批处理几何)之间的桥梁。本文将深入介绍 AcTrScene 如何组织 CAD 图纸的可视内容,并通过具体实例进行详细解释。

读完后,你不仅会了解如何快速绘制,还会理解如何保持渲染场景清晰、结构化和可扩展------即使图纸非常复杂。

注意:

1. 场景结构

首先,让我们了解场景的基本结构:

bash 复制代码
Scene (AcTrScene)
 └── Layout (AcTrLayout)       # 纸张空间或模型空间
     └── Layer (AcTrLayer)     # 绘图图层
         └── Batched Group (AcTrBatchedGroup)
             ├── Line Batches  # 线条、圆弧、样条、射线...
             ├── Mesh Batches  # 填充、网格字体等
             └── Point Batches # 点

Scene (AcTrScene)

  • 整个图纸的根管理器
  • 保存所有 Layout 以及 Layer 的信息(存储 isOnisFrozen、颜色等属性)

Layout (AcTrLayout)

  • 表示 CAD 布局(例如 Model Space Model 或 Paper Space Layout1

Layer (AcTrLayer)

  • 容纳特定 Layout 中该 Layer 上的 Entity
  • 不存储 Layer 属性( Layer 属性保存在场景管理的 Layer 列表中)

Batched Group (AcTrBatchedGroup)

  • 保存该 Layout 中该 Layer 的 Batched Geometry
  • 根据类型(Line/Mesh/Point)和材质 ID 进一步分组

2. 一个简洁的 DWG 示例

为了解释如何将 DWG 图纸的结构转化为相应的渲染场景结构,我们使用以下图纸示例:

图层:

  • 0(CAD 默认图层;在主图纸中为空)
  • L1(主要几何)
  • L2(标注)

模型空间中的 Entity:

  • L1 中的 Polyline(墙轮廓)
  • L1 中的 Hatch(地面图案)
  • L2 中的 Point(测量标记)
  • L1 中的 Insert(块引用):

Insert(块引用)所引用的块包含:

  • 0 图层中的 Circle
  • L2 图层中的 MText

3. 将 AutoCAD Entity 转换为可渲染的 Geometry

渲染引擎(如 Three.js 和 WebGPU)无法直接理解 LINECIRCLEHATCHINSERT 等 CAD Entity。cad-viewer 将所有 CAD Entity 转换为三种基本 Geometry,以便 GPU 高效渲染:

  • Line:用于线状 Entity ,如 Polyline、Arc、Circle(转换为多段线)、其他 Entity 的边
  • Mesh:用于填充类型的 Entity,如 Hatch、使用 TrueType/OpenType 字体的字符
  • Point:用于点类型的 Entity

此转换在批处理之前进行,同时解决块和图层继承等复杂问题。

常见 Entity 的处理方式如下表所示:

3.1 Text 和 MText

AutoCAD 支持两种不同的文本技术:

转换流程:

  1. 从 AutoCAD 读取文本字符串、位置、旋转、样式和字体类型
  2. 如果字符使用 TrueType/OpenType 字体,获取字符轮廓、三角化并存储为 Mesh 到对应的 Mesh Batch
  3. 如果字符使用 SHX 字体,解析每个字符的笔画并存储为线段到 Line Batch

详解

  • SHX 笔画字体避免复杂三角化,渲染 CAD 图纸通常更快
  • CAD 用户通常在大幅技术图纸中使用 SHX 字体,因为缩放后仍清晰且轻量

单个 MTEXT 对象可以包含多种字体。字体混合使用 TrueType/OpenType 和 SHX 字体时:

  • 一个 MTEXT 可以同时为 Line Batches 和 Mesh Batches 贡献几何
  • 因此,它可分布到多个批处理

示例:

  • MTEXT 正文使用 TrueType(如 Arial)→ 存入 Mesh Batch
  • 工程符号使用 SHX(如 simplex.shx)→ 存入 Line Batch

场景结构示意:

scss 复制代码
Scene
 └── Layout (模型空间)
     └── Layer L2
         └── Batched Group
             ├── Line Batches      ← MTEXT 的 SHX 部分
             ├── Mesh Batches      ← MTEXT 的 TrueType 部分

3.2 Table Entity

AutoCAD 的 TABLE Entity 包含了:

  • 表格结构:行列网格
  • 单元格文本:通常为多行文字且包含字体样式
  • 边框与单元格线

转换流程

  1. 解析表格:计算每个单元格角的位置,提取边框线

  2. 转换:

    • 所有边框线 → 存入 Line Primitives
    • 单元格文字 → TrueType 转 Mesh Geometry,SHX 转 Line Geometry
  3. 应用每个单元格的格式(字体大小、对齐)

详解

  • 与单个 Polyline 或 Hatch 不同,TABLE 可扩展为数百到上千个 Geometry
  • 通过按材质和 Geometry 类型创建相应的 Batched Geometry 来处理表格的边框和单元格文字,即使大表格也能高效渲染

3.3 Insert Entity

块参照类似可重用子图纸的实例。它引入图层继承和可见性规则,影响内容的转换。

AutoCAD 行为规则

  • 块 Entity 在 0 图层 → 继承块参照图层
  • 块 Entity 在其他图层 → 保持原图层
  • 关闭块参照图层 → 整个块隐藏
  • 关闭块内使用的图层 → 仅隐藏对应 Entity

转换流程

  1. 解析块定义,根据插入的缩放、旋转、位置转换 Entity

  2. 对块内每个 Entity:

    • 根据继承规则确定有效图层
    • 转换为 Line、Mesh 或 Point Geometry
  3. 将所有转换后的 Geometry 加入该 Layout 中有效图层的 Batched Groups

示意图

scss 复制代码
Layout (模型空间)
 └── Layer L1
     └── Batched Group
         ├── Line Batches ← 块内 SHX 字体、边界线
         ├── Mesh Batches ← 块内 TrueType 字体、填充
         └── Point Batches ← 块内标记点

这确保了即使块跨多个图层或包含混合字体,最终渲染也能保持准确。

3.4 总结

根据这些规则,在 AutoCAD 中看起来"在一起"的 Entity,可能会在场景中被拆分多个图层、多个 Batched Geometry中:

  • 一个单独的 INSERT 可能会生成多个 Line GeometryMesh GeometryPoint Geometry ,分散在 多个图层 的 Batched Geometry 中。
  • 一个 TABLE 会展开成许多 Line GeometryMesh Geometry ,分散在 单个图层 的多个 Batched Geometry 中。
  • 一个 TEXT 对象可能会变成多个 Line Geometry (如果是 SHX 字体)或 Mesh Geometry (如果是 TrueType 字体),分散在 单个图层 的多个 Batched Geometry 中。

正是这种精细化的组织,使 cad-viewer 能够利用 GPU 实例化技术,并在复杂注释和可重用内容丰富的图纸中保持高帧率。

4. 示例图纸的场景结构

以下是示例图纸的场景结构:

bash 复制代码
Scene (AcTrScene)  
├── Layout: Model Space (AcTrLayout)  
│ ├── Layer "0" (AcTrLayer)  
│ │ └── Batched Group  
│ │  ├── Line Batches # (空)
│ │  ├── Mesh Batches # (空)  
│ │  └── Point Batches # (空)  
│ │  
│ ├── Layer "L1" (AcTrLayer)  
│ │ └── Batched Group  
│ │  ├── Line Batches  
│ │  │ └── [Polyline geometry] # 墙体轮廓
│ │  │ └── [Polyline geometry] # 来自块的圆
│ │  ├── Mesh Batches  
│ │  │ └── [Hatch geometry] # 地面图案  
│ │  └── Point Batches # (空)  
│ │  
│ └── Layer "L2" (AcTrLayer)  
│   └── Batched Group  
│    ├── Line Batches # (空)  
│    ├── Mesh Batches  
│    │ └── [Text geometry] # 来自块  
│    └── Point Batches  
│      └── [Point entity] # 测量标记  
│  
└── Layout: Paper Space (AcTrLayout) # 此示例为空  
 ├── Layer "0" (AcTrLayer) # 存在但为空  
 ├── Layer "L1" (AcTrLayer) # 存在但为空  
 └── Layer "L2" (AcTrLayer) # 存在但为空

理解转换后的场景

  • 每个布局(Layout)都有自己的一组 AcTrLayer 对象。
  • 每个 AcTrLayer 包含一个 AcTrBatchedGroup,只包含渲染所需的图元类型。
  • 块引用中的圆进入 L1 的 Line Batches 中。
  • 块中的文本进入 L2 的 Mesh Batches。
  • Point 保持在 L2 的 Point Batches 中。
  • Hatch 和 Polyline 保留在 L1 的 Batches 中。

此结构反映了 CAD 的图层语义,同时经过优化以提升渲染性能:使用相同图层、图元类型和材质的实体可以合并为单个批次绘制,从而最小化绘制调用(draw calls)。

5. 场景中的图层 vs. DWG 文件中的图层

在 DWG 文件中:

  • 图层是全局的:只有一组图层,每个图层都有属性,如 isOnisFrozen、颜色等。

在场景中:

  • 每个布局为每个 DWG 图层创建自己的 AcTrLayer 实例。
  • 因此,两个布局(Model, Paper)和三个图层(0, L1, L2)会生成 6 个 AcTrLayer 对象。
  • 实际的图层属性存储在 AcTrScene 中,而不是每个 AcTrLayer,确保切换可见性或冻结图层时,对所有布局都一致生效。
  • 每个 AcTrLayer 只是特定布局中批次的容器。

这种分离方式让我们可以保持渲染数据的布局友好性,同时维护 CAD 一致的全局图层行为。

6. 处理块引用(INSERT)------棘手部分

块引用(INSERT 实体)是 CAD 渲染中最微妙的部分之一。AutoCAD 的行为说明了原因:

假设块包含:

  • E1 在层 0
  • E2 在层 L1
  • E3 在层 L2

你将该块插入到层 L1(块引用所在的层)。

情况 1:关闭图层 L1

  • 块引用本身在 L1。
  • 关闭 L1 后,整个块引用消失,无论其内容在哪个图层。

结果:整个块隐藏。

情况 2:关闭图层 L2

  • 块引用仍在 L1(L1 保持开启)。
  • 块内:
    • E1(在 0)→ 继承块的图层 L1,可见
    • E2(在 L1)→ 可见
    • E3(在 L2)→ 隐藏

结果:块引用可见,但内容中在 L2 的部分消失。

cad-viewer 的处理方式

渲染块引用时,

  • 插入对象自身的图层影响整个块的可见性。
  • 内部实体根据继承规则确定的有效图层贡献几何到相应的批次中。

这确保渲染遵循 CAD 的可见性语义,同时将几何放置在 GPU 批处理的合适位置。

7. 为什么这种场景结构可行

  • 性能:按布局 → 图层 → 批次类型 → 材质分组,最小化绘制调用和 GPU 状态切换。
  • 可扩展性:场景结构在图纸变大、变复杂时仍能很好地扩展,同时与 CAD 概念紧密对应。
  • 一致性:图层属性统一存储在场景级别,应用一致。
  • 可维护性:开发者可以轻松从 CAD 概念(Layout、Layer、Entity)导航到渲染数据。

8. 结束语

在上一篇文章中,我们重点讨论了如何使用 Batched Geometry 以提升性能。本篇文章探讨了如何将 CAD Entity 转换为可渲染图元,并在场景中组织它们。

  • 转换步骤是 CAD 语义与 GPU 效率之间的桥梁。
  • 理解它后,带有布局、图层和批次的场景结构图会更直观。
  • 转换 + 场景组织 + Batched Geometrycad-viewer 既忠实于 AutoCAD 数据模型,又高度优化用于 Web CAD 浏览。

如果想深入实现,或亲自尝试,可查看我的开源项目源代码:

相关推荐
王木风4 小时前
1分钟理解什么是MySQL的Buffer Pool和LRU 算法?
前端·mysql
Jerry_Rod4 小时前
vue 项目如何使用 mqtt 通信
前端·vue.js
云中雾丽5 小时前
Flutter中路由配置的各种方案
前端
不一样的少年_5 小时前
女朋友炸了:刚打开的网页怎么又没了?我反手甩出一键恢复按钮!
前端·javascript·浏览器
Renounce5 小时前
【Android】让 Android 界面 “动” 起来:动画知识点大起底
前端
Asort5 小时前
JavaScript设计模式(十四)——命令模式:解耦请求发送者与接收者
前端·javascript·设计模式
小茴香3535 小时前
Vue 脚手架(Vue CLI)
前端·javascript·vue.js
午安~婉5 小时前
ESLint
前端·eslint·检查
“抚琴”的人5 小时前
C#中获取程序执行时间
服务器·前端·c#