软件光栅化如何开始用C++写一个光栅化渲染器?

首先特别推荐从Sokolov的Tiny Renderer教程开始入门。这是个很硬核的课程,Sokolov从一个最简单的画点函数开始,一个小节一个小节地讲解怎么画出一条线,怎么填充一个面,怎么逐步加入背面剔除、深度测试、透视相机、着色器等高级功能。每个小节的课程都有详细的讲解以及完整的实现代码可供参考。这个教程一共有9个小节,只要学到第6个小节我们就已经可以实现一个具有可编程渲染管线的软件渲染器了,而这时候的总代码量只有500行而已,可以说是非常简洁适合入门了。

有了图形界面之后,接下来我们可以考虑加入一个方便好用的相机了。常用的相机有两种:第一种是FPS相机,就像在FPS游戏里面一样,可以用鼠标指针调整相机朝向,用WSAD键进行上下左右移动;第二种是环绕相机,相机有一个目标点,可以对着这个目标点环绕(orbit),可以调整和目标点的距离(dolly),也可以平移目标点(pan)。这里我个人更推荐实现一个环绕相机。因为我们在实现软件渲染器的过程中,大部分时间都是在检查某个模型的渲染表现是不是正确的。我们需要旋转到各个角度、拉近或拉远镜头来检查模型上的每个部分的渲染表现。这时候环绕相机要比FPS相机更加趁手。

有了可编程的渲染管线、可交互的图形界面和方便的旋转相机之后,我们其实可以很轻松地在我们的软件渲染器上实验各种图形技术了。在这个过程中,我们会发现之前的可编程渲染管线其实是存在很多问题的。比如,Tiny Renderer在光栅化过程中对变量的插值并不是透视正确的(perspective correct),这会导致模型的纹理坐标发生扭曲,在类似地板这样的由大面积三角形构成的模型上表现得尤为明显。

Tiny Renderer也

GitHub上可以参考的环绕相机的实现很多,其中我看过做得最好的是Three.js的环绕相机。他们的环绕相机有挺多比较细致的优化,比如在平移目标点时,考虑了目标点与相机的距离,使得物体最后在屏幕空间的移动距离总是鼠标的移动距离保持固定的比例,因此物体可以贴合鼠标一起移动(也就是俗称的跟手的感觉)。

加入图形界面的方法有很多,我们可以使用现成的Qt、SDL、GLFW等图形界面库,也可以通过直接调用系统API实现。因为软件渲染器需要的图形界面功能是非常少的,只需要提供打开窗口、显示渲染好的画面、接收鼠标键盘事件这些功能就可以了,为此专门引入一个第三方库好像有点太重量级了,所以我是直接使用系统API实现了简单的图形界面,每个平台需要的代码量大概在300行左右。可以参考一下platform.h这个通用的接口文件以及platforms这个目录下的各平台的具体代码实现。(用过GLFW的同学可能会觉得这些接口长得和GLFW的接口很像,嗯,其实这些接口是我从GLFW里面扒出来的,可以看作是超精简版的GLFW。)

Tiny Renderer并没有包含图形界面,我们需要把渲染好的画面保存到图片中,然后再用图片查看器打开来查看渲染结果。这其实会对我们实现着色器效果造成很大的不便,因为很多时候我们需要不断调整相机的位置和朝向来检查渲染效果是不是正确的。如果每次都要重复设置相机的位置和朝向、渲染场景并保存为图片、打开图片比较渲染效果这个过程的话,我们的效率会变得很低。所以下一步自然是要给我们的软件渲染器加入可以交互的图形界面,以便实时查看渲染结果了。

下面推荐一下我在学习和实现过程中发现的一些非常有用的资源以及比较建议的推进路线。

软件光栅化如何开始用C++写一个光栅化渲染器?

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注

滚动到顶部