什么是Shader?它和Material(材质)的关系
Shader,中文翻译:着色器,是可编程图形管线的算法片段
Shader实际上就是一小段程序,它负责将输入的顶点数据以指定的方式和输入的贴图或者颜色等组合起来,然后输出。绘图单元可以依据这个输出来将图像绘制到屏幕上。输入的贴图或者颜色等,加上对应的Shader,以及对Shader的特定的参数设置,将这些内容打包储存在一起,得到的就是一个Material(材质)。之后,我们便可以将材质赋予三维物体来进行渲染(输出)了。
Material(材质)包含Texture Mapping(贴图),Texture Mapping(贴图)包含纹理 Texture。纹理是最基本的数据输入单位,游戏领域基本上都用的是位图。此外还有程序化生成的纹理 Procedural Texture。贴图的英语 Map 其实包含了另一层含义就是“映射”。其功能就是把纹理通过 UV 坐标映射到3D 物体表面。贴图包含了除了纹理以外其他很多信息,比方说 UV 坐标、贴图输入输出控制等等。
材质是一个数据集,主要功能就是给渲染器提供数据和光照算法。贴图就是其中数据的一部分,根据用途不同,贴图也会被分成不同的类型,比方说 Diffuse Map,Specular Map,Normal Map 和 Gloss Map 等等。另外一个重要部分就是光照模型 Shader ,用以实现不同的渲染效果。
材质好比引擎最终使用的商品,Shader好比是生产这种商品的加工方法,而贴图就是原材料。
渲染流水线
渲染流水线的概念:渲染流程(Shader只是渲染流水线中的一个可编程算法片段)。主要就是将流程中各阶段阶段的输出,输入到下一阶段进行工作并输出到下一阶段。例如,片段着色器会将在顶点着色器内进行了坐标变换的顶点数据输入进来拓展成多边形
渲染流水线的工作任务在于由一个三维场景出发、生成(或者说渲染)一张二维图像。换句话说,计算机需要从一系列的顶点数据、纹理等信息出发,把这些信息最终转换成一张人眼可以看到的图像。
《Real-Time Rendering,Third Edition》书中将整个渲染流程分成3个阶段:应用阶段(Application Stage)、几何阶段(Geometry Stage)、光栅化阶段(Rasterizer Stage)
应用阶段(Application Stage):由应用主导,通常由CPU负责实现
渲染流水线的起始点是CPU,即应用阶段。应用阶段大致可分为下面三个阶段:
1、把数据加载到显存中
2、设置渲染状态
3、调用Draw Call
这一阶段最重要的输出是渲染所需的几何信息,即渲染图元(rendering primitives)
通俗来讲, 渲染图元可以是点、线、三角面等。这些渲染图元将会被传递给下一个阶段——几何阶段。
1、把数据加载到显存中。(场景数据,粗粒度剔除......)
渲染所需的数据都要从硬盘加载到系统内存,然后网格和纹理等数据又被加载到显存。
2、设置渲染状态
定义了场景中的网格是怎么被渲染的 (使用哪个shader ,材质,光源属性等)
3、调用Draw Call
Draw Call就是一个命令。他的发起方是CPU,接收方是GPU。
这个命令仅仅会指向一个需要被渲染的图元列表,而且不会再包含任何材质信息,上个阶段已经完成了。
给定一个Draw Call后GPU根据渲染状态,如材质纹理着色器等和所有输入的顶点数据来进行计算,最终输出成屏幕上显示的像素。
Draw Call这个概念在游戏中可以经常听到,它有很多别名,DP,批次,说的都是它,都是一会事情。在游戏中经常出现批次很多的性能问题。
举个例子:场景中有两个桶,如果是分别导入的,就是两个Draw Call。如果是合并之后导入的,就是一个Draw Call。
批次太多会导致CPU性能开销过大,一般来说有两种解决办法。
(1)合批,就是把能合并的都合并起来,尽量减少Draw Call。(肯定有不能合并的,比如场景中的一个大树和主角,还有好多渲染状态啊透明啊之类的)
(2)instance,GPU硬件算法。使用与大量需要重复绘制的模型,比如草地。但是instance是有上限的,并且只能减少Draw Call,对于增加的面数来说是无解的。
几何阶段(Geometry Stage):这一阶段通常在GPU上进行。
几何阶段处理所有和我们要绘制的几何相关的事情。
例如决定需要绘制的图元是什么,怎样绘制它们,在哪里绘制他们。
几何阶段和每个渲染图元打交道,进行逐顶点、逐多边形的操作。该阶段可进一步被分解为更小的流水线阶段。
几何阶段的另一个重要的任务是把顶点坐标变换道屏幕空间中,再交给光栅器进行处理。
这一阶段会将输出屏幕空间的二维顶点坐标、每个顶点对应的深度值、着色等相关信息传递给下个阶段。
顶点着色——>投影——>裁剪——>屏幕映射
·顶点着色
1、通过坐标转换,从模型空间转换到齐次裁剪空间
首先顶点着色阶段拿到的顶点坐标数据都是位于模型空间的,要经过模型转换将这些坐标数据转换到世界空间。接着为了方便后面的投影和裁剪,需要将坐标数据从世界空间转换到摄像机空间(摄像机位置为原点,x轴指向右方,y指向上方,z轴是背离摄像机朝向的方向),这称为视图转换。
2、确定模型上顶点处材质的光照效果。将模型顶点的位置变换到齐次裁剪坐标系下,进行输出后再由硬件做透视排除法得到归一化(NDC)的设备坐标。
- 投影
主要使用两种投影方法:正交投影、透视投影
为了最大化控制性能消耗,需要将那些不在摄像机范围内或是被遮挡的物体裁剪掉,不被渲染。这一阶段也是将模型从三维空间投影二维空间的过程。
模型空间——>摄像机空间——>归一化坐标——>0-1之外的部分裁减掉
- 屏幕映射
这一阶段最主要是将三维坐标系(x,y,z)下的每个几何图元坐标转换到二维(x,y)屏幕空间。
又一个需要引起注意的地方是,OpenGL和DirectX的屏幕坐标系之间存在着差异,OpenGL把屏幕的左下角当成最小的窗口坐标值,而DirectX则定义了屏幕的左上角位最小的窗口坐标值。
光栅化阶段(Rasterizer Stage):这一阶段在GPU上运行
这一阶段会使用上一阶段传递的数据来产生屏幕上的像素,然后渲染出最终的图像。
光栅化阶段的任务主要是决定每个渲染图元中的那些像素应该被绘制在屏幕上。
他需要对上阶段的到的逐顶点数据进行插值,然后再进行逐像素处理。
与几何阶段类似,光栅化阶段又可以分为更小的流水线阶段:三角形设置、三角形遍历、片元着色器、逐片元操作