ArchitectureColoredPainting/README.md

103 lines
8.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# 古建筑彩绘计算机辅助设计系统
本软件实现一种设计矢量纹案的软件工具,尤其适用于建筑彩绘,矢量纹理采用高效的数据结构存储,支持图案**复用**,支持将矢量图作为模型的纹理,用户可以利用软件中的图元引用、变换以及样式等功能方便地完成**矢量纹理的设计**;完成矢量纹理的设计后,可以将其应用在**三维模型**上,方便展示**模拟纹案的渲染后结果**,用户可以自由调整摄像机位置和缩放视野,模型中的矢量纹理**不会在缩放过程中失真**。
![image-20240314162357152](README.assets/editor.png)![image-20240314162357152](README.assets/renderer.png)
## 依赖
- Qt 5.15.2 (MSVC 2019 64-bit)
- [Assimp](https://github.com/assimp/assimp) (x64-windows建议使用vcpkg安装)
## 构建
使用 Visual Studio 2022 打开项目编译运行。需安装Qt Visual Studio Tools扩展并配置Qt版本**项目路径不能含有中文**。
## 实现细节
### 纹理编辑Editor
为了简化图元的存储,支持包括图层的坐标偏移、旋转、缩放、翻转的变换操作,实现图层描边、填充等样式功能,设计图元池与图层树结构。图层的变换和样式将被保留在图层树中,并与图层池相互联系。
**图元池**是一个数组结构用于存储现有的SVG资源或者指向图层节点以便对图层信息的重复利用。其具体实现采用Vector容器并根据存储内容和功能不同分为简单图元引用图层节点的复杂图元。复杂图元会保留源图层的变换和样式无变换信息和样式信息的复杂图元无意义。对于引用复杂图元的图层它的变换行为和样式传递行为与简单图元保持一致只不过它的直接父图层节点是它的源图层再上层的父亲节点才是在图层树中的父节点。
**图层树**是一个树结构用于存储编辑时的层次结构每个节点代表一个图层保存了对当前图层的坐标偏移、旋转、缩放、翻转的变换与图层的描边、填充等样式。其中变换操作是相对于父节点的变换图元的最终变换是从当前节点不断叠加到根节点的变换。而图元最终样式是根据以下逻辑判断当子图层未设定样式时继承父图层样式当子图层存在样式时覆盖父图层对应类型的样式由此实现样式的复用以及统一修改。为了图层树中根据功能分为非叶节点、简单叶子节点和复用叶子节点三种类型。其中非叶节点是一种节点容器它包含若干子节点及非叶节点并且会将自身属性与容器内节点叠加以实现批量修改的目的。简单叶子节点即使用了SVG文件的图层其为传递的终点在此将进行图像数据的绘制与反向传递不再向其他节点传递数据。复用叶子节点使用了图元中的引用节点会将自身属性叠加至目标节点的拷贝以实现复用的效果。
![element_and_layer_manager](README.assets/element_and_layer_manager.png)
### 场景渲染Renderer
#### 矢量纹理的快速随机访问——基于BVH和四叉树的加速结构
将矢量纹理中的单个封闭图形或样式相同的连续的线条称为图元使用类似Ray-tracing的方法逐像素点测试图元。
由于单个图元中可能由大量线条组成,对所有线条逐一测试效率低下,故对**图元内部进行四叉树划分**保证四叉树的每个叶子结点中的线条数量低于一定数量同时记录一些额外信息使得无需获取其他结点的数据就能准确还原出每个叶子结点的图像。对于封闭图元的填充参考论文Massively-Parallel Vector Graphics建立Shortcut Tree数据结构。构造过程采用四叉树的方式以广度优先搜索的方式建立shortcut tree。对于线条描边图元同样构建一个与填充所用的Shortcut Tree类似的四叉树结构其无需计算winding number与shortcut segment但需要额外考虑线条的宽度以及预计算连续曲线中每一段三次贝塞尔曲线的长度以支持宽度随长度变化的描边样式长度计算采用数值积分中的龙贝格求积算法。
复杂的矢量纹理中存在大量图元,故**使用BVHBounding Volume Hierarchies层次包围盒结构组织图元**,以加速对于图元的搜素,同时可避免重复存储相似的图元。
划分示例如下图绿框为BVH红框为四叉树
![ssbo](README.assets/bvh_and_quadtree.png)
经过上述方法处理的矢量纹理数据以SSBOShader Storage Buffer Object形式存储于显存最终在显存中维护的数据结构如下图所示
![ssbo](README.assets/ssbo.png)
对于矢量纹理中任一点的访问过程如下首先搜索BVH得到该像素点所属图元的图元表索引从图元表中得到图元的四叉树根节点再搜索图元的四叉树得到像素点所属的叶子结点包含的线组表索引和样式数据索引由此将相关的线做相交测试或计算距离来判断该像素点是否落在封闭图形内或线条上最后根据样式数据得到该像素点的颜色和材质信息。
##### 已实现的图元样式MaterialStyle
###### 面MaterialStyleFill
纯色
###### 线MaterialStyleStroke
双侧/左侧/右侧
圆头/平头
纯色/渐变/分层
变宽
#### 实时真实感绘制
![render_pipeline](README.assets/render_pipeline.png)
##### 基于延迟渲染的渲染管线
渲染主线程的总体流程基于延迟渲染,每一帧的渲染流程分为以下几个阶段:
• Depth Map Pass从光源视角渲染整个场景得到场景被照亮的区域记录深度缓冲
• Geometry Pass几何处理阶段从摄像机视角渲染模型将基础色、世界坐标、法线、金属度和粗糙度、虚拟纹理分页ID输出到颜色缓冲
• Feedback Downsampling对虚拟纹理分页ID缓存降采样此后回读分页ID缓存至CPU并通知虚拟纹理管理线程更新虚拟纹理
• Lighting Pass光照处理阶段根据光源视角深度缓冲等信息计算阴影并根据基础色、世界坐标、法线、金属度和粗糙度以及预计算的环境光照等颜色缓冲以及光源和相机坐标计算光照
• Skybox Rendering绘制天空盒
• Tone Mapping色调映射使用ACES Tone Mapping
• Final Pass执行FXAA以及Gamma Correction。
##### 虚拟纹理
由于对于矢量纹理的随机访问方案所需的时间成本与矢量纹理的复杂程度有关直接使用该方案可能会导致场景绘制帧率的波动故引入虚拟纹理技术。虚拟纹理Virtual Texture类似虚拟内存即对于一张拥有多级Mipmap的高分辨率纹理不将其全部加载到显存中而是分为若干分页Page在显存中只保留当前视野可见的分页和最低分辨率Mipmap的分页并在视野变化时进行分页加载与卸载由此可以在保证渲染精度的同时避免占用过多显存。
本系统的虚拟纹理实现基于OpenGL扩展ARB_sparse_texture2。虚拟纹理的使用以及随视野加载与卸载需要渲染主线程和虚拟纹理管理线程的共同配合完成。
##### 基于物理的渲染
使用基于物理的渲染Physically Based Rendering其基于微平面表面模型遵循能量守恒并使用Cook-Torrance BRDF着色模型光照计算基于如下反射率方程
$$
L_o(p,\omega_o) = \int\limits_{\Omega} (k_d\frac{c}{\pi} + k_s\frac{DFG}{4(\omega_o \cdot n)(\omega_i \cdot n)})L_i(p,\omega_i) n \cdot \omega_i d\omega_i
$$
环境光计算使用基于图像的光照Image Based Lighting使用立方体贴图渲染环境将其每个像素视为光源漫反射部分使用预计算漫反射辐照度实现镜面反射部分使用分割求和近似法。
##### 级联阴影映射
阴影的绘制使用级联阴影映射Cascaded Shadow Mapping实现其把视锥体分割为多个子视锥体并根据光源和场景为每个子视锥体计算独立的相等大小的阴影映射能够有效避免阴影的透视走样。在渲染阴影时将片元的世界坐标按照光源视角重投影并根据重投影的结果采样合适层级的深度缓冲比较深度信息判断是否处于阴影中使用PCF百分比渐近过滤实现阴影反走样。