聊聊我是怎么开发软渲染器的


3楼猫 发布时间:2022-12-30 18:02:07 作者:煤问题 Language

本文首次发表于CG世界,内容经过重新编辑

介绍

大概是从去年8月份,我开始一边自学图形学一边开发软渲染器foolrenderer。除了模型加载用了开源代码以外,其他所有功能都是自己开发的。目前实现了基本的光栅化渲染,还添加了可编程渲染流水线、基于物理的材质等功能。
foolrenderer演示了GPU的大致工作原理和一些实时渲染技术,可以作为游戏开发者和图形爱好者了解渲染基础知识的学习性项目。大伙可以在Github上找到源代码
使用自制渲染器得到的一些渲染结果
1 / 8
截取自视频中的一些渲染结果
首先解释一下什么是软渲染器?
软渲染器(Software Renderer)是一类完全依靠软件驱动,不使用图形硬件加速的渲染器的统称。原本集成在显卡里的功能需要全部由开发者自己实现,软渲染的速度也远远比不上利用了硬件加速的渲染器。虽然软渲染器缺点很多,但也有一些实际应用场景:例如软渲染器有很好的兼容性,一些系统上的图形编程接口有软渲染层作为硬件不兼容时的后备渲染方案;可以用软渲染器实现一些显卡不支持的功能,早期的光线追踪就是纯软件实现的。

动机

虽然我之前一直在开发游戏,但主要集中在玩法制作上,图形方面的知识其实了解的不多,这就导致我在制作游戏的时候遇到不少问题。比如,色彩贴图一般要用sRGB空间,为啥金属度、法线这类贴图不用;PBR材质里常说的分布函数是个啥,什么是GGX。诸如此类,看了网上的解释还是云里雾里。还有个问题是不会写着色器,很多视觉效果需要自定义的着色器实现,但我一直不熟悉渲染流水线,对着教程也写不出来。
所以我慢慢觉得有必要了解一些计算机图形的概念,于是乎开始找相关资料学习。经过了解之后发现,图形学的基础原理并没有想象中那么复杂,用学到的知识完全可以自己写一个软渲染器。我觉得边学边做个小软件挺有意思的,连带这样学稍微更有动力一点,所以就决定开发一个基于光栅化算法的软渲染器,也就是现在的foolrenderer项目。
foolrenderer的开发过程(模型作者Suushimi)

foolrenderer的开发过程(模型作者Suushimi)

开发过程

因为是边学边做,所以foolrenderer的开发是按照自学进度走的,我学到哪里就给程序添加相应的功能。
首先就是为foolrenderer实现三角形光栅化功能,用人话说就是把三角形绘制到屏幕上。原理很简单:检查屏幕上的每一个像素,如果像素刚好落在三角形里面,就给这个像素设置对应颜色。当然了,每画一个三角形都要检查屏幕上的所有像素显然没有必要,最简单的优化方法是只检查三角形所在的矩形范围内的像素。
有了三角形光栅化,我接着添加了插值功能。接触过三维软件的老哥都知道,模型是由一个个点构造的三角形组成(多边形可以分解成若干个三角形)。这些点就叫顶点,顶点除了包括这个点的位置信息外还可以存储颜色、UV坐标、法线等等其他数据。插值说的就是把三角形三个顶点的数据均匀的过渡到三角形表面上。举个例子,如果三角形顶点都是红色,插值会得到一个红色三角形;如果三角形顶点分别是红、绿、蓝,经过差值得到的三角形呈现渐变色,越接近绿色顶点的地方颜色也越接近绿色。
三角形光栅化以及顶点颜色差值

三角形光栅化以及顶点颜色差值

到这里就已经可以渲染一个简单的模型了,只需要读取模型文件里的数据,然后一个个的把三角形画出来就行——当然事情也没有这么简单,得到的结果还有很多问题:比如,程序没有考虑三角形的前后位置关系,模型背面的三角形可能挡住前面的三角,那就用深度缓冲记录每个像素的前后位置,不画靠后的面;渲染器肯定要有调整模型位置大小的功能,还要模拟人眼近大远小的透视观察效果。这就需要利用线性代数提供的数学工具修改顶点位置;还应该给foolrenderer开发一个可编程渲染流水线,这样才方便给模型添加贴图和光照效果。
从这儿就可以看出来,图形学的各种技术就是在讨论现有方案存在的问题时自然而然发展起来的,非常符合直觉。这里我要特别推荐《Tiny renderer》这个教程,内容就是在教你怎么开发一个最基本的软渲染器,非常适合没有基础的菜鸟学习,这个教程也算是一个让我开始写软渲染器的契机。
《Tiny renderer》可以手把手教你渲染个人头

《Tiny renderer》可以手把手教你渲染个人头

上面提到,渲染模型时要对所有顶点做位置变换,这需要提供相应的线性代数函数。网上现成的代码有很多,但本着尽量自己做的原则我还是自己写了一套。《线性代数的本质》是我找到的一个不错的入门视频,如果还有高中数学的那点底子,看这个视频就不成问题。一共只有十几集,每集平均10分钟,作者讲解的非常清楚,看完以后应付图形方面的问题足够了。
一番折腾foolrenderer逐渐像模像样起来,能真正能称得上是个渲染器了。在这个基础上可以开始添加一些常见的实时渲染技术,像什么法线贴图、阴影、基于物理的渲染等等。当然难度也开始慢慢变高了。实际开发这些功能后才发现,我们经常在软件中使用的技术,其实远比看起来的要复杂。
使用PBR材质渲染得到的小提琴(模型作者Virtual Museums of Małopolska)

使用PBR材质渲染得到的小提琴(模型作者Virtual Museums of Małopolska)

印象比较深的是计算顶点的切线向量,用切线空间的法线贴图时就要用到切线。乍一看计算切线很简单,按照推倒的公式算就行。但琢磨一下会发现不对劲的地方——一个顶点可能会被多个三角形共用,怎么计算平均切线就有很多方法了。如果建模软件用的方法和游戏引擎中的不一样,通过法线贴图计算得到的最终法线就会有区别,渲染结果就会有差异。为了避免差异,有人甚至提出计算切线的标准,MikkTSpace就是一种算法,实现方式非常复杂。
常用的法线贴图技术其实比看起来的要复杂(图片来自Tiny renderer)

常用的法线贴图技术其实比看起来的要复杂(图片来自Tiny renderer)

总之制作一个软渲染器算是不错的学习经历。开发的时候有个体会:找到合适的资料也挺费功夫的,同一内容的类似资料能在网上找到很多,只能都看看,横向比较一下才能知道好坏。我把找到的资料整理成了一个清单,希望对那些想要充电的朋友多少有点帮助。
开发foolrenderer时收集到的资料

开发foolrenderer时收集到的资料

后续打算

foolrenderer算是完成了吗?emmm从功能上来说恐怕还有很长一段距离,目前这个软渲染器只能说还很基础,甚至一些技术已经在现有的生产力工具里淘汰了。
随便举个例子,光是纹理这项功能到目前还只能用“残疾”来形容,最基本的双线性差值和Mipmap都没有,所以能在渲染的动画里看到明显的走样。更别提其他一些常见优化手段,更高级的全局照明,都还没来得及加进去。
同样的,图像方面的学习也才刚刚开始,继续练级呗还能咋整啊?

© 2022 3楼猫 下载APP 站点地图 广告合作:asmrly666@gmail.com