Skip to content
Menu
CFC Studio
  • 实验室主页
  • CFC 招新简章
  • 友情链接
  • RSS订阅
CFC Studio

如何开始写一个软件渲染器

Posted on 2018年2月23日2020年8月9日 by tandandan

经常有人说浪漫这个词。在实用主义的人们看来,浪漫基本跟没用是一个意思。在计算机的世界里,同样存在很多看似“浪漫”的事物,比如标题中的软件渲染器。

什么是软件渲染器?

图形处理器

我们经常提到 GPU (显卡),性能优良的 GPU 是流畅运行游戏的基础。GPU 和 CPU 常被拿来比较。如果说 CPU 是为通用计算而设计,那么 GPU 就是专为游戏而生。CPU 只有几个内核,而 GPU 有数百或数千个内核,经过优化可以并行运行大量计算。正因如此,虽然 GPU 为游戏而生,但它对运行分析深度学习和机器学习算法尤其有用 (比如说现在都有了个 OpenCL) 。

渲染

所谓渲染,就是用软件从模型生成图像的过程。模型是用数据结构进行严格定义的三维物体或虚拟场景的描述,它包括几何位置、纹理坐标、光照等信息。图像是数字图像或者位图图像。

渲染一般分为预渲染和实时渲染,都非常慢。预渲染用于电影制作,计算强度大,需要大量的服务器运算完成。实时渲染则常用于 3D 游戏,通常由 GPU 完成计算过程。

渲染管线

渲染管线是一条流水线,它里面流淌的是用于渲染的数据。对于 3D 渲染来说,一个图元 (通常是三角形) 需要经过渲染管线各个阶段才能被显示在屏幕上。

硬件加速

在 GPU 面世之前,我们用 CPU 来进行绘图显示的。CPU 计算能力強,但并不能并行处理海量数据。而 GPU 是单一指令多数据流 (SIMD) 架构,能在单一时钟循环下,多条管线平行处理指令。直接在 GPU 中完成渲染管线的许多阶段 (比如顶点变换和光照) ,能够达到使用硬件加速渲染的效果。另外,原本 GPU 的硬件加速是为了高性能 3D 图像而设。到后来,也将对影像和 2D GUI 的加速功能集成在了一起。

OpenGL 和 DirectX

随着 GPU 的蓬勃发展,OpenGL 出现了。OpenGL 是一个用于渲染的跨平台库,向下直接跟 GPU 硬件驱动打交道,抽象了渲染管线的各个阶段,以 API 的形式提供出来。而 DirectX 则是微软版本的 OpenGL,游戏配置需求表就经常出现 “DirectX: Version 11” 。

软件渲染器

现在的渲染过程已经从固定管线变成了可变管线,GPU 支持的高级功能也越来越多,但这并不防碍我们去理解每一个像素是如何被渲染到屏幕上的。OpenGL 虽然大部分是为了使用硬件加速而设,但是理论上完全可以通过软件的形式实现。用软件程序的形式来模拟实现图形渲染管线,这就是软件渲染器。在没有 GPU 或者 GPU 不能用的情况下,OpenGL / DirectX 会使用内置的 “高速” 软件渲染器来完成渲染过程。

所以为什么说软件渲染器是浪漫 (没用),一是因为大家一般都使用硬件加速,没有用武之地;二是最好的实现在 OpenGL / DirectX 里,写软件渲染器并不是一种 “创造”。

写软件渲染器所需要的知识

一门语言 + 3D 光栅化渲染算法 + 输出环境

语言没有啥特别的要求,反正我看到过 c,c++,c#,python,java,javascript… 输出环境的最低要求是能提供一张 bitmap 来操纵和显示,程序往 bitmap 的每个单元写颜色值,输出环境把这张 bitmap 展示到屏幕上。我用的是 c++ 和 windows api / gdi (win32 application),c# 可以用 winform (更省一点直接用 unity), js 可以用 canvas…

然后是光栅化的渲染算法。这一套算法看 3d graphics pipeline 就可以了解大概有什么:基本上就是 顶点变换 和 光栅化 。顶点变换很多书都会比较仔细提到,光栅化算法则少见得多。

为什么要写软件渲染器?

写软渲之前,学习 DirectX 的时候,真的是碰到很多术语。尽管网络上,书上都有相当程度的解释,但总是感到不尽然。非得概括这种感觉的话,应该就是“没亲自写过的就不是自己的”吧。但直接使用 DirectX 就已经相当底层,而软渲基本是实现个阉割版 DirectX,就更底层了。花时间去学习理解这些基础的算法和流程有没有必要?我不能下这个结论。所以只能把我认为写软渲所能得到的写下来:

  1. 细致地理解光栅化渲染的流程以及所用到的经典算法、数据结构和经验计算模型
  2. 使相关知识牢靠和启发图形化的调试技巧
  3. 避免手生和打发时间

挨个解释下:
1. 很好解释。如果这些东西不熟悉,软渲是写不出来的。
2. 渲染器的输出是一张图像,这就意味着调试不是看文本化的输出,而是看图形化的输出。渲染管线涉及的步骤并不少,其中任何一个都有可能导致最终的渲染错误。这就需要了解一个步骤的细节,并且当渲染出魔性的图像时,按照它去推断可能出错的地方。
3. 通用的道理。动手实践对于技术人员而言,是深入骨髓的东西,需要不断地练习巩固。

我的软件渲染器

感想

这次写软渲,除了光栅化算法本身,我印象最深的就是总不离不弃的 bug 们。从 void** 转成二维数组错误导致 violent access,最后使用指针数组解决;扫描线填充时因为顶点顺序错误,导致颜色插值错误;三角形编织顺序不对,导致背面剔除错误; float 相等判断的方式引起的深度缓冲和单元测试错误…但正是这些 bug 才是独特的经验。

还有就是渲染器的组织方式,我还想过挺多。分离渲染算法和渲染数据,渲染物体改成渲染场景中的物体,数学库是短小精悍还是大而全,const buffer 放在 scene 里…虽然很多时候都被证明是没必要的想多了,但是多思考总应该不会有什么危害吧…

本来我还想写多点的,但是恰巧读到一个故事:sun 公司的最后一任 CEO 热衷于写博客,然后 sun 公司就完蛋了,堪称博客误国的典范…他被批评说 “无论再好、再多的博客都代替不了好的处理器、软件和销售”。虽然只是调侃吧,但老让我心有余悸…所以以后我写博客还是简洁为上。

另外,果然干什么都有一个亘古不变的硬伤:想的很多,干成的很少。我这个软件渲染器只实现了最基本的功能,其他的包括外部加载数据、各种贴图支持、Shader 结构等都没有加上去,是真的懒…

bug组图

这些 bug 图并不是一开始就有意记录的,所以前期一些非常魔性的 bug 并没有截图下来(实在遗憾)。有时候, bug 也可以很美丽。

图片(组) 2.我也记不清哪里出现的各种 bugs

项目地址

Software Renderer – Github
原文链接

 

发表评论 取消回复

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

分类

  • CFC 周刊 (4)
  • CFC 技术 (29)
  • CFC 日常 (3)
  • 未分类 (15)
  • 活动通知 (3)

标签

ACM Android anime animeloop animeloop-cli APP Apple aria2 Array Blog CFC数据结构与算法训练指南 CoreData CQUT Don't Starve Hexo iBooks JavaScript macOS Matlab moeoverflow OpenCV Programming README RxJS SQLite SQLite3 Steam Swift Theme Web Xcode 主题模板 动漫 博客 反编译 妹子 循环 教程 数据库 游戏 算法 装逼 视频 重庆理工大学 饥荒

登录
©2023 CFC Studio | Powered by WordPress & Superb Themes