103 lines
8.4 KiB
Markdown
103 lines
8.4 KiB
Markdown
# 古建筑彩绘计算机辅助设计系统
|
||
|
||
本软件实现一种设计矢量纹案的软件工具,尤其适用于建筑彩绘,矢量纹理采用高效的数据结构存储,支持图案**复用**,支持将矢量图作为模型的纹理,用户可以利用软件中的图元引用、变换以及样式等功能方便地完成**矢量纹理的设计**;完成矢量纹理的设计后,可以将其应用在**三维模型**上,方便展示**模拟纹案的渲染后结果**,用户可以自由调整摄像机位置和缩放视野,模型中的矢量纹理**不会在缩放过程中失真**。
|
||
|
||
![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,但需要额外考虑线条的宽度以及预计算连续曲线中每一段三次贝塞尔曲线的长度,以支持宽度随长度变化的描边样式,长度计算采用数值积分中的龙贝格求积算法。
|
||
|
||
复杂的矢量纹理中存在大量图元,故**使用BVH(Bounding Volume Hierarchies,层次包围盒)结构组织图元**,以加速对于图元的搜素,同时可避免重复存储相似的图元。
|
||
|
||
划分示例如下图(绿框为BVH,红框为四叉树):
|
||
|
||
![ssbo](README.assets/bvh_and_quadtree.png)
|
||
|
||
经过上述方法处理的矢量纹理数据以SSBO(Shader 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(百分比渐近过滤)实现阴影反走样。
|