Shader编程学习笔记(四)—— Unity Shader的组织形式(ShaderLab)
Unity Shader的组织形式
Unity Shader的形态
Unity官方手册上讲Unity Shader有三种不同的编写方案,这三种编写方案分别是surface shaders、vertex and fragment shaders和fixed function shaders。 从前面几篇笔记中可以了解到,可编程图形管线中能够编写shader的主要是两个部分:vertex shader和fragment shader,但Unity还有surface shaders和fixed function shaders。
对于fixed function shaders,从表面意思来理解就是固定管线着色器,在可编程管线硬件出现之前,很多的光照流水计算都会放在硬件里进行处理,我们把这样的固定管线功能也看作是对应于固定管线硬件的操作,这种shader的功能是很保守的,比如说启用简单的光照,进行简单的纹理采样等等,对于现在绝大多数硬件都能得到很好的支持。
surface shaders是Unity中被推荐和鼓励使用的着色器,当你在Unity(Unity5之前)中创建一个shader时,默认的代码就是使用surface shaders。那么为什么会有surface shaders这样的着色器呢?它和vertex and fragment shaders又有什么关系呢?我们可以这样理解,可编程图形管线能够识别就是两种shader程序:vertex shader和fragment shader,至于surface shader是在vertex shader和fragment shader上面的一种包装,Unity引擎最终会把surface shaders代码编译成能够被硬件识别和调用的vertex and fragment shaders。
Unity官方推荐在学习shader时先去阅读ShaderLab syntax的基本概念,然后再去阅读surface shaders或者vertex and fragment shaders的相关内容。fixed function shaders只能够使用ShaderLab语法进行编写,而surface shaders和vertex and fragment shaders不限于ShderLab语法,是可以使用Cg/HLSL,甚至可用GLSL去编写,它是镶嵌在ShaderLab中的一种代码片段。
ShaderLab基本结构
ShaderLab是Unity定制的专门编写Shader的一种方案。使用ShaderLab可以把上文提到的三种shader用同一种格式来编写,这样就不会导致编写不同的shader需要不同的语法。前文中对shader、材质和贴图等它们之间的关系做了一个形象的比喻:材质是最终要使用的对象,shader是关于如何加工和处理的一种方案,是一种程序片段,至于贴图、颜色等它们是属于这个加工方案中的材料。那么当要建立一个Shader,要把它用于在材质上去使用的时候,既需要有算法,也需要添加一些原材料,首先来了解一下ShaderLab的主要结构,以下是ShaderLab的基本结构:
shader "name"{ [Properties] SubShaders [FallBack] }
Properties的作用是能够允许在Unity材质(material)检视面板中为材质去定义一些需要的参数,如颜色、贴图、参数等,这些都属于原材料,被shader程序使用。
SubShader的作用就是专门为GPU渲染所编写的shader程序片段,在一个shader当中有并且至少需要一个SubShader,也可以有多个SubShader,但是在执行时只能选择其中的一个SubShader。那为什么允许多个SubShader同时存在呢?原因是shader中的某一种算法或者某一种指令可能不支持当前的硬件,比如说一个shader程序有多个SubShader,当这个shader程序被执行的时候,首先会去检测当前硬件能不能完好地支持第一个SubShader,如果第一个SubShader可以被当前硬件良好支持,那么就会使用当前SubShader,如果不能,那么就去检测下一个SubShader。在编写shader时,假如不能确定当前的SubShader在某些老旧的硬件上得到良好的支持,可以编写第二个SubShader,用来适配这些老旧的、稍差的图形硬件,当然也可以继续编写第三个、第四个、第五个等等,编写的SubShader功能要依次简化、运算指令依次简单,用来满足更多的硬件支持。如果编写的SubShader都不能支持当前的硬件时,就需要用到ShaderLab的“FallBack”功能了。
FallBack的意思就是回滚。如果当前硬件无法支持所有的SubShader时,Unity会将当前的着色回滚,回滚到FallBack所指定的shader,因此FallBack指令后跟随的一般都是系统自带比较简单的,能够被绝大多数硬件执行的shader。以下列举了几个Unity官方的内建的shader,更加具体的信息可以查阅Unity官方手册。
Unity Build-In Shader
- Unlit. This is just a texture,not affected by any lighting.(不发光。这只是一个纹理,不被任何光照影响)
这是Unity中最为简单的shader,执行效率非常快,该shader经常被用于UI系统。
- VertexLit.(顶点光照)
该shader拥有在顶点上渲染光照的能力。
- Diffuse.(漫反射)
漫反射也是一种光照形态,不过它不仅仅在顶点上进行光照计算,在片段程序中也要进行光照计算。
- Normal Mapped.This is a bit more expensive than Diffuse:it adds more texture(normal map),and a couple of shader instructions.(法线贴图,比漫反射更昂贵:增加了一个或更多纹理(法线贴图)和几个着色器结构)
法线贴图技术是一种比较传统的图形渲染技术,通过一张贴图进行采样计算,但这张贴图不是一张普通的贴图,而是一张存储法向量的贴图,把这张图采样出来的数据当做法向量,然后再进行光照计算,主要目的就是当几个模型面片顶点数量不多,构成的面片细节不多的时候,为了表达丰富的细节,可以使用法线贴图去弥补这样的细节,以假乱真,因此法线贴图shader的使用率很高。
- Specular.This adds specular highlight calculation.(高光。增加了特殊的高光计算)
Specular指的是镜面高光反射,主要用于模拟光滑物体,比如金属、玻璃等。
- Normal Mapped Specular.Again,this is a bit more expensive than Specular.(高光法线贴图。比高光更昂贵一点)
该shader结合了以上两点,既有法线贴图,又有高光显示。
- Parallax Normal mapped.This adds parallax normal-mapping calculation.(视差法线贴图。增加了视差法线贴图计算)
- Parallax Normal Mapped Specular.This adds both poarallax normal-mapping and specluar highlight calculation.(视差高光法线贴图。增加了视差法线贴图和镜面高光计算)
视差即视觉的差异,比较形象的解释就是当人的左眼和右眼分别去看同一个物体时,两只眼睛看到的结果是不一样的,正是由于人的左眼和右眼有三厘米左右的距离,人看到的这个世界是三维的、立体的。
那么视差法线贴图的作用是什么呢?原因就是当法线贴图进行贴图映射的时候,是为了更好地表现物体的细节,法线数据存储在纹理当中,但是该纹理是不可变的,当物体渲染出来后,会得到一个比较好的结果,表现了物体凹凸的细节,所以法线贴图在有些教科书当中也叫作凹凸贴图,但是当把有法线贴图的物体进行旋转,照理说有些地方由于视角的偏差是看不到它的凹凸的形态或者说看到凹凸感觉会更强烈,由于法线贴图是一个固定的贴图,就会达不到这样的效果,而视差法线贴图的作用就弥补这样的缺陷。