一、着色器如何工作
顶点输入。这些顶点信息包括顶点位置position、顶点法线normal、顶点颜色color等。之后顶点着色器会输出包含处理过的顶点信息的数据包,这一步称为顶点输出。此时数据包包括新顶点位置position和其他来自顶点着色器的信息。例如有一个由几个顶点组成的旗帜,现在将这些顶点一一传递给顶点着色器,顶点着色器可以让顶点基于sin函数移动顶点,从而得到一个挥动的旗帜,此时顶点着色器输出来新的位置信息。
顶点着色器输出的数据包加入光栅化器Rasterizer。光栅化器是一个硬件,它将根据特定的规则找到屏幕上被几何图形覆盖的对应像素。光栅化器的第一个任务是找到几何区域中的像素,另一个任务是把顶点着色器给的数据进行插值,使数据得以进入像素着色器。例如,我们要在屏幕上画一个三角形,光栅化器要找到顶点组成的区域中找到那些被几何图元覆盖的像素,另外它还要对屏幕上的每一个片段或像素进行插值。如图提供了两个顶点法线(0,1,0)和(1,0,1),将他们进行插值计算取得两点连线中点的坐标(0.5,0.5,0)。接着,光栅化器会将数据给像素着色器,像素着色器会按照指令对每一个被光栅化器识别的像素进行着色,像素着色器会输出每个像素的颜色color、像素的透明度alpha,帮助像素在屏幕上最终的着色。
光栅化器是如果通过采样找到图元区域上对应的像素的? 每一个像素可以有一个或多个采样 ,如果采样的地方是被几何区域覆盖的,那这个mesh就会被特定的像素绘制上去。但是简单采样出来的三角区域的边缘是锯齿状的。这就有了后来的对像素的多采样,可以是同时采样2个像素、4个像素等,采样后根据是否被图元区域覆盖进行像素颜色计算随着多采样像素增多计算的代价也提高了。
二、Unity中一个着色器的组成成分
unity中写shader时包含两种语言:Shaderlab和Cg语言。unity中写shader的结构如下图,上下两个部分是shaderlab,中间是cg代码。shaderlab包含有为shader属性创建GUI,它设置了着色器的其他属性如对透明和不透明颜色进行混合的混合选项,它可以设置render passes渲染通道,它还可以设置FallBack硬件回退。cg语言是英伟达开发的着色语言,它让我们可以控制图形硬件,它让我们可以编写顶点和像素着色器。在unity中编程cg语言时,我们有两个编写着色器的选择,一个是unity提供的一种快速编写shader的surface shader,这是一种自动生成代码的方法,一个是限制较少的Vertex/Pixel shader的组合。
unity中写shader时包含7个组成部分。
最顶部是shaderlab编写的属性Properties,它在shaderlab中定义,它是着色器的输入或者外部控制面板可任意调节的参数,unity会自动根据shaderlab中定义的不同数据类型生成不同类型的UI界面。
Subshader,它是着色器的子集。一个着色器可以包含多个子着色器,不同的子着色器可以支持不同的硬件功能。而这些硬件功能是指支持某些图形API的硬件,这些图形API有metal、OpenGL ES、OpenGL ES 3、XBOX 360等等。比如要让一个游戏能在多种硬件下运行,比如要在IOS设备上做一些特殊的功能,可以用metal图形API专为IOS设备写一个Subshader,而只有支持metal的设备才会运行这个Subshader。如果一个shader中有几个Subshader,unity会选择能由硬件支持的第一个Subshader来执行顶点和像素着色器;而如果硬件都不支持这几个Subshader,unity会选择作为备用选项的Fallback退回到默认的着色器,这些默认的着色器可以保证在任何硬件上运行。
Pass,一个Subshader可以有多个渲染通道Pass。不同的渲染通道有如occlusion pass遮蔽、lighting pass灯光、beauty pass。这些渲染通道会选择一个着色器就渲染一次到几何Geometry或网格Mesh上,比如着色器应用到一个三角形上,每个pass会渲染一次这个三角形。这就是为什么运行一次pass就是一个draw call,所以要尽量只写在一个pass里。
顶点数据信息的结构体;第五部分是顶点着色器代码;第六部分是输入像素着色器的顶点输出数据信息结构体;第七部分是最后输出颜色和透明度的像素着色器代码。
三、unity中编写一个最基础的shader
HLSL/CG语法参考链接:
https://docs.unity3d.com/Manual/SL-ShaderPrograms.htmldocs.unity3d.com
Semantics - Win32 appsdocs.microsoft.com
unity中最基础的shader,EG:
// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'