Unity shader学习笔记(一)―― 渲染流程

匿名 (未验证) 提交于 2019-12-03 00:03:02

渲染流水线

为了渲染一个模型,我们需要知道这个模型的每个顶点信息,有可能还需要知道由几个顶点构成的每个片元的颜色,这个颜色可能是直接指定的顶点颜色然后做的插值,也可能是用一张纹理“贴”上去,然后还要经过顶点变换,裁剪,等等操作,最后才渲染到屏幕上。为了加快这些步骤的执行,我们希望能够将这些步骤拆分成具体的几步,参考CPU指令流水线的思想,用流水线的思想加快执行。
在这些步骤中,可以分成两个部分。一部分在CPU上执行,一部分在GPU上执行。一开始模型的信息都存储在硬盘上,然后CPU要将其读取到内存中,之后设置一些渲染的状态(比如使用哪张纹理,顶点变换规则,颜色计算规则等等),然后将渲染状态和模型的顶点信息发送到显存,再发送一个指向该渲染状态的渲染命令Draw Call,通知GPU读取该渲染状态和模型顶点信息,开启第二部分的步骤绘制模型。而在第二部分中,又可以拆分出来两部分。总共三部分的步骤,分别如下:

应用阶段
几何阶段
光栅化阶段

其中应用阶段在CPU上执行,几何阶段和光栅化阶段在GPU上执行。这三个部分里面的十几个步骤(下文提及)最终构成了整个渲染流水线的元素。
注意上文提到的渲染命令,CPU有可能会发送多个,这些命令都会发送到一个命令缓存中,再由GPU一一读取。

OpenGL 和 DirectX


这张图是《Untiy shader 入门精要》下的一张图,非常清晰地解释了CPU,GPU,OpenGl/DirectX之间的关系。CPU会执行应用程序的代码,里面可能会包含一些渲染指令(即所谓的Draw Call),这些渲染指令其实并不是真正地直接发送到命令缓存中,而是调用了OpenGL或者DirectX提供的接口,由这两个计算机图形处理库去发送真正的渲染命令。渲染命令由显卡驱动去翻译成GPU能够识别的语言,然后GPU读取显存中由渲染命令指定的数据,最后开始渲染,在此之前,CPU已经提前将数据从内存中拷贝到了显存里。
一个游戏引擎,为了能够跨平台开发,跨平台发布,需要同时支持OpenGL和DirectX两个图形处理库。一般来说,我们在游戏引擎中写的代码,就是上图中的应用程序一项。不必直接接触OpenGL和DirectX提供的接口,因为游戏引擎一般都已经提供了调用这两个库的接口,并且做了很好适配和封装。但是有时候,我们仍然需要手动地去为这两个图形处理库的不同做一些处理。

GPU做的事情

第一张图中,有两个部分是GPU做的。一个是几何阶段,一个是光栅化阶段。这其中又细分为好几个步骤,以下是《Unity shader 入门精要》的一张图:

其中,顶点着色器,曲面细分着色器和片元着色器是可编程的。在应用阶段,CPU设置的渲染状态时,可以设置分别使用哪三个着色器。在Unity Shader中,我们通过在Shader里编写着色器代码,然后将Shader脚本附给一个材质,再将材质指定给一个物体,就完成了某个物体的着色器的指定。当然,Shader可不止能做这些。
几何阶段从一开始接收物体的顶点数据,到最后的屏幕映射,输出的是经过坐标变换后的窗口坐标系下的坐标(关于具体的坐标变换,可以参考关键词 MVP坐标变换)。这些新的顶点数据会传给光栅化阶段做下一步的处理。
在光栅化阶段的片元着色器会对每个片元进行颜色填充或者纹理映射,计算片元的输出颜色。

总结

最后,总结一下一个模型是如何被渲染到屏幕上。渲染一个模型需要什么信息?首先是模型的网格,即顶点信息,它决定一个模型具体长什么样,其次,由顶点构成的面元具体如何被绘制?一种可以在顶点信息中包含每个顶点的颜色,然后每个面元由构成它的顶点的颜色插值得到。另一种,指定一张纹理给它,通过纹理映射技术将纹理“贴”在模型表面。那我们定好了形状的物体以什么样的姿态呈现在屏幕上的(比如你可以指定它总是以哈哈镜的效果呈现在屏幕上),然后颜色如何插值,纹理具体怎么“贴”,那就需要自己书写顶点着色器,片元着色器等,然后指定使用你写的着色器。这些东西一并打包发送至显存中,然后发送一条渲染命令,通知GPU渲染模型。
再提一下纹理坐标这个概念,它将一张纹理的每个像素映射到一个坐标系中,这个坐标系的原点(OpenGL)(-1,-1)为纹理的左下角,而纹理的右上角固定为坐标(1,1)。即每个像素的横坐标映射到(-1,1)的某一点,纵坐标同理。不过在DirectX中,原点是在纹理的左上角。然后,纹理映射将纹理的纹理坐标映射到模型的每个点,以此在模型上显示出纹理。由于纹理坐标水平方向上一般叫U坐标,竖直方向上一般叫V坐标,故纹理坐标又称UV坐标。我们可以指定纹理坐标映射在模型表面的方式,比如纹理太小而模型太大时,我们可以选择平铺,也可以选择拉伸来映射纹理坐标。
从效果上来看,平铺将一个纹理坐标映射到多个模型顶点上,而拉伸将一个纹理坐标映射到一个模型顶点上,那些没有分配到纹理坐标的顶点可以通过插值绘制而得。然而实际是如何做的,我并没有考究,仅供思路参考。
欢迎大佬批评指正。

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!