Shader(着色器)实际上就是一小段程序,它负责将输入的Mesh(网格)以指定的方式和输入的贴图或者颜色等组合作用,然后输出。绘图单元可以依据这个输出来将图像绘制到屏幕上。输入的贴图或者颜色等,加上对应的Shader,以及对Shader的特定的参数设置,将这些内容(Shader及输入参数)打包存储在一起,得到的就是一个Material(材质)。之后,我们便可以将材质赋予合适的renderer(渲染器)来进行渲染(输出)了。 所以说Shader并没有什么特别神奇的,它只是一段规定好输入(颜色,贴图等)和输出(渲染器能够读懂的点和颜色的对应关系)的程序。而Shader开发者要做的就是根据输入,进行计算变换,产生输出而已。
按照渲染管线的分类,可以把Sharder分成3个类别:
固定功能着色器为固定功能渲染管线的具体表现。
存在于Unity3D中由U3D发扬光大的一门技术。Untiy3D为我们把Shader的复杂性包装起来,降低shader的书写门槛。
GPU上含有两个组件:可编程顶点处理器和可编程片段处理器,顶点和片段处理器被分离成可编程单元,可编程顶点处理器是一个硬件单元,可以运行顶点程序,而可编程片段处理器则是一个可以运行片段程序的单元。
顶点着色程序从GPU前端(寄存器)中提取图元信息(顶点位置、法向量、纹理坐标),并完成顶点坐标空间变换、法向量空间转换、光照计算等操作,最后将计算数据传送到指定寄存器中。
片段程序从上述寄存器中获取需要的数据:纹理坐标与光照信息等,并根据这些信息以及从应用程序传递的纹理信息进行每个片段的颜色计算(纹理查询),最后将处理后的数据传送光栅操作模块。
Material{}以及SetTexture[_MainTex]{}
CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" ENDCG
1.表面着色器使用Unity3D自带光照模型Lambert,也不做顶点处理,只需要一个表面处理函数surf即可。 CGPROGRAM #Pragma surface surf Lambert ENDCF 2.这套表示使用的是自己写的光照模型lsyLightModel,并且使用了顶点处理函数vert CGPROGRAM #pragma surface surf lsyLightModel vertex:vert ENDCG
Unity中Shader整体的框架写法可以用如下的形式来概括:
Shader "name" { [Properties] SubShaders[Fallback] }
Unity中所有着色器都由关键字shader开始,随后的字符表示着色器的名字,这个名字会显示在Inspector检视面板中,所有的代码都应该放在{}里面。
该名字不需要和shader文件名同名,它应该是简单的描述性词语,在name后面加上/能够中Inspector面板中创造出二级菜单(多个/创建多级菜单)。
如上面的整体框架,我们可以画出下面这图:
从这幅图可以看到,Unity中的shader可以分为以下三个模块:
Properties一般定义中着色器的起始部分。属性模块可以定义一些属性,用来指定那个这段代码有哪些输入。在使用shader的时候可以直接中材质面板里编辑这些属性。Properties块内的语法都是单行的。属性的定义格式如下所示:
属性可以划分为以下几类:
name ("display name", Range (min, max)) = number //滑动条调节 name ("display name", Float) = number//数字调节 name ("display name", Int) = number//数字调节
name ("display name", Color) = (number,number,number,number) name ("display name", Vector) = (number,number,number,number)
name ("display name", 2D) = "defaulttexture" {} name ("display name", Cube) = "defaulttexture" {}//立方体贴图 name ("display name", 3D) = "defaulttexture" {}
使用示例
// properties for a water shader
Properties
{
_WaveScale ("Wave scale", Range (0.02,0.15)) = 0.07 // sliders
_ReflDistort ("Reflection distort", Range (0,1.5)) = 0.5
_RefrDistort ("Refraction distort", Range (0,1.5)) = 0.4
_RefrColor ("Refraction color", Color) = (.34, .85, .92, 1) // color
_ReflectionTex ("Environment Reflection", 2D) = "" {} // textures
_RefractionTex ("Environment Refraction", 2D) = "" {}
_Fresnel ("Fresnel (A) ", 2D) = "" {}
_BumpMap ("Bumpmap (RGB) ", 2D) = "" {}
}
可以有一个或者有多个子着色器SubShader(至少有一个),子着色器SubShader中含有一个或者多个通道Pass(也是至少要有一个)。
Color Color 设定对象的纯颜色,可以是括号中的四个值,也可以是被方框包围的颜色属性名称
Material{Material Block} 材质被用于定义对象的材质属性,关于材质块的内容可以看下面的介绍
Lighting On/Off 定义上述材质块的定义是否有效,On时材质块效果有效,Off时颜色通过Color命令直接给出
SeparateSpecular On/Off 开启独立镜面反射,这个命令会添加高光光照到着色器通道的末尾,因此贴图对高光没有影响。只在光照开启时有效。 ColorMaterial AmbientAndDiffuse | Emission 使用每顶点的颜色替代材质中的颜色集。AmbientAndDiffuse 替代材质的阴影光和漫反射值;Emission 替代 材质中的光发射值。
上面已经说了,在Pass中可以书写材质块代码用于定义对象的材质属性,如下的代码可以写在材质块中:
Diffuse Color(R,G,B,A);对象基本颜色,漫反射
Ambient Color(R,G,B,A);环境光,当对象被RenderSettings.中设定的环境色所照射时对象所表现的颜色。
Specular Color(R,G,B,A);对象反射高光的颜色
Emission Color 对象自发光
Shininess Number 取值在0-1之间表示加亮时的光泽度
对象完整光照的最终颜色是: FinalColor=Ambient * RenderSettings ambientsetting + (Light Color * Diffuse + Light Color *Specular) + Emission
即: 最终颜色=环境光反射颜色* 渲染设置环境设置 (灯光颜色漫反射颜色+灯光颜色*镜面反射颜色)+自发光
示例代码:
Shader "我的Shader"
{
Properties
{
_MainColor ("主颜色",Color)=(0,0,0,0)
_AmbientColor("环境光颜色",Color)=(0.8,0.2,0.2,0)
_SpecularColor("放射高光颜色",Color)=(0.8,0.2,0.2,0)
_ShininessNum("光泽度",Range(0,1))= 0.5 //滑动条
}
//---------------------------------【子着色器1】----------------------------------
SubShader
{
//----------------通道---------------
Pass
{
Color(1,0,0,0) //当光照开启时无效。
//----------材质块------------
Material
{
//将漫反射和环境光反射颜色设为相同
Diffuse[_MainColor]
Ambient[_AmbientColor]
Specular[_specularColor]
Shininess[_ShinnessNum]
}
//开启光照 //光照影响了颜色
Lighting On
SeparateSpecular On
}
}
}
效果:
Shader基本框架的最后是指定一个回滚函数Fallback,用来处理所有的子着色器都不能运行时的情况(当目标设备太老时,所有的设备都有其不支持的特性时使用了Fallback),可以认为是一种defult。
Fallback" Diffuse "
关于纹理的载入在下一节讲解。