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

在我上一篇文章【高性能 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 浏览。

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

相关推荐
L***d6703 小时前
十七:Spring Boot依赖 (2)-- spring-boot-starter-web 依赖详解
前端·spring boot·后端
少云清3 小时前
【功能测试】6_Web端抓包 _Fiddler抓包工具的应用
前端·功能测试·fiddler
豐儀麟阁贵3 小时前
8.5在方法中抛出异常
java·开发语言·前端·算法
zengyuhan5033 小时前
Windows BLE 开发指南(Rust windows-rs)
前端·rust
big男孩3 小时前
OrbitControls 的完整原理
three.js
醉方休3 小时前
Webpack loader 的执行机制
前端·webpack·rust
前端老宋Running4 小时前
一次从“卡顿地狱”到“丝般顺滑”的 React 搜索优化实战
前端·react.js·掘金日报
隔壁的大叔4 小时前
如何自己构建一个Markdown增量渲染器
前端·javascript
用户4445543654264 小时前
Android的自定义View
前端
WILLF4 小时前
HTML iframe 标签
前端·javascript