实现个简单的固定渲染管线软渲染器不算复杂,大家都说了那么多了,我来个更短的吧,差不多700行代码就可以搞定了。之所以很多人用 D3D用的很熟,写软渲染却肯坑洼洼,主要是现在大部分讲图形的书,讲到透视投影时就是分析一下透视变换矩阵如何生成,顶点如何计算就跳到其他讲模型或者光照的部分了。
因为今天基本上是直接用 D3D 或者 OGL,真正光栅化的部分不了解也不影响使用,所以大部分教材都直接调过了一大段,摄像机坐标系如何转换?三角形如何生成?CVV边缘如何检测?四维坐标如何裁剪?边缘及步长如何计算?扫描线该如何绘制?透视纹理映射具体代码该怎么写?framebuffer zbuffer 到底该怎么用?z-test 到底是该 test z 还是 w 还是 1/z 还是 1/w ?这些都没讲。
早年培训学生时候,我花两天时间写的一个 DEMO,今天拿出来重新调整注释一下,性能和功能当然比不过高大上的软件渲染器。但一般来讲,工程类项目代码不容易阅读,太多边界情况和太多细节优化容易让初学者迷失,这个 mini3d 的项目不做任何优化,主要目的就是为了突出主干:
源代码: skywind3000/mini3d · GitHub
可执行: http://www.skywind.me/mw/images/c/c8/Mini3d.7z
操作方式:左右键旋转,前后键前进后退,空格键切换模式,ESC退出。
颜色填充
透视纹理映射
线框图
增加背面剔
增加简单光照
提供更多渲染模式
实现二次线性差值的纹理读取
推导并证明程序中用到的所有几何知识
优化顶点计算性能
优化 draw_scanline 性能
从 BMP/TGA 文件加载纹理
载入 BSP 场景并实现漫游
当年还用不了 D3D 和 OGL ,开发游戏,做图形实现软件渲染是必备技能,当年机型差,连浮点数都用不了,要用定点数来计算,矩阵稍不注意就越界了。计算透视纠正还是一个比较昂贵的工作,更多游戏使用仿射纹理绘制,只是把离屏幕近的多边形切割成更小的三角形,让人看起来没有那么明显。即便到了 Quake 年代,计算 1/z 的除法也只是四个点才算一次(经过精确计算CPU周期,绘制四个点时下一个点的 1/z刚好算完),Quake 的四个点内也还是仿射纹理绘制……
那时显卡没普及,光软件渲染器的优化就是一个无底洞,今天有了 OGL/D3D和显卡,人的精力才能充分集中在更高层次的场景组织、层次细节、动态光照等功能上。然而有空的时候,花个一周时间坐下来了解一下这部分的大概原理,推导所用到的数学模型,也能帮助大家更好的理解底层运行机制,写出更加优化的代码来。
PS:光线跟踪版本的软件渲染,考虑光照的话,简单实现起来差不多 500 行,比这个要简单一些。各位有兴趣也可以尝试一下,就是简单渲染个立方体足够了。