# ScanlineRasterizer-CPU **Repository Path**: SouthernHermit/scanline-rasterizer-cpu ## Basic Information - **Project Name**: ScanlineRasterizer-CPU - **Description**: 基于扫描线算法的软光栅化渲染器 - **Primary Language**: C++ - **License**: 0BSD - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2022-06-23 - **Last Updated**: 2022-06-24 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 基于扫描线的软光栅渲染器 徐开元 ## 开发测试环境 操作系统:windows 10 编程语言:C++ 编译器:MSVC v142(Visual Studio 2019) CPU: 移动端 AMD Ryzen 4800U@8核16线程 内存: LPDDR4@4166Mhz 16GB ## 使用说明 在`bin`目录下运行`renderer.exe`,运行命令为 ```bash renderer.exe scene.json ``` 其中`scene.json`是json格式的场景描述文件。配置项示例如下: ```json { "display": // 显示分辨率 { "width": 640, // 宽度 "height": 480 // 高度 }, "camera": // 相机 { "position": [0.0,30.0,100.0], // 相机世界坐标 "fovy": 60.0, // 纵向视角,单位为角度° "zNear": 0.1, // 最近z值(正数) "zFar": 1000.0 // 最远z值(正数) }, "light": // 光源 { "position": [0,100,100] // 点光源世界坐标 }, "objects": // 场景物体 [ { "name": "dragon", // 物体名称 "path": "../Resource/dragon2.obj", // 模型文件路径 "color": "Yellow", // 物体颜色 "scale": [1.0,1.0,1.0], // x、y、z轴缩放比例 "rotate": [0,0,0], // 绕x、y、z轴旋转角度,单位为角度° "translate": [0,0,0] // x、y、z轴平移量 }, { "name": "teapot", "path": "../Resource/teapot2.ply", "color": "Green", "scale": [3.0,3.0,3.0], "rotate": [0,0,0], "translate": [0,0,3.33] } ] } ``` 按以上配置文件运行程序,界面如下图 ![image-20220115233413845](image1.png) 本界面采用FPS相机,使用W、A、S、D键位可以控制相机位置移动;按住左键拖拽鼠标,可以控制相机视角左右上下移动。 ## 算法与数据结构 本程序是一个在CPU端实现扫描线遮挡剔除算法的软光栅渲染器。具体实现上,使用CPU粗略模拟了光栅化渲染管线的各阶段,包括顶点处理、视锥体裁剪、屏幕空间映射、光栅化及深度测试、像素着色。计算完成的framebuffer通过OpenGL输出到GPU的帧缓冲上显示出来。 算法核心代码位于`src/OcclusionCulling/Scanline/parallel.h`.模拟光栅化渲染管线的代码为 ```c++ void runPipeline() { assert(pMesh); assert(framebuffer); processVertex(); processPixel(); postprocess(); } ``` 数据结构定义位于`src/OcclusionCulling/Scanline/base.h`.主要数据结构见下 ```c++ /* 分类多边形表项 */ struct PolygonInfo { Plane plane; // 平面 ax+by+cz+d=0 int prim_id; // polygon id int edge_id; // 边索引 }; /* 分类边表项 */ struct EdgeInfo { occFloat upper_x; // 边的上端点x坐标 occFloat dx; // 相邻扫描线的delta x int ymax; int dy; // 边穿过的扫描数量 int id; // polygon id }; /* 活化边表项 */ struct ActiveEdgeInfo { occFloat xl, xr; occFloat dxl, dxr; int dyl, dyr; occFloat zl, dzx, dzy; int id; // polygon id int next_edge; }; ``` 本程序实现了简单的像素着色器功能,伪着色器代码位于`src/OcclusionCulling/Graphics/VirtualShader`目录。上文程序截图展示的是一个diffuse shader的渲染结果。 ## 优化 1.省去了活化多边形表。由于本算法只处理多边形为三角形的情况,因此可以通过活化边表的`next_edge`追踪三角形的剩下一条边。通过省去多边形表节省了存储和运算开销。 2.并行加速。使用OpenMP对顶点坐标变换和扫描线深度测试做了并行化加速。在AMD Ryzen 4800U处理器上使用16线程并行处理,达到每帧70ms的渲染性能,相比单核实现的100~110ms提升了35%左右。由于大量线程的创建和合并,以及增加的内存访问带来额外开销,性能的提升不算很大,还有进一步优化的空间。 ## 实验结果 ### 正确性 根据上文场景描述文件的配置,使用MeshLab加载模型: ![image-20220115235725279](image2.png) 与本程序运行结果相同。可见深度关系处理正确。 ### 性能 | 模型 | 面数 | 每帧渲染耗时(ms) | | ---------- | ------- | ------------------ | | 茶壶(小) | 6320 | 6 | | 茶壶(大) | 60760 | 18 | | 龙 | 209227 | 52 | | 茶壶+龙 | 269987 | 70 | | 弥勒佛 | 1087716 | 183 | 渲染耗时大致与场景面片数成正比。