Unity3D Shader之路 写Shader前必须要知道的事情2 表面着色器的理解

来源:互联网 发布:一线城市 知乎 编辑:程序博客网 时间:2024/06/02 06:02

版本:unity 5.4.1  语言:Unity Shader

 

总起:

最近这边Shader的研究暂时有所搁置,主要项目需要研究Animator,而那个又是一个深坑。

 

不过今天我们来讲讲Unity中的表面着色器,可能看了上一篇文章大家有所疑惑,有一些Shader的结构并不像顶点/片元Shader一样在Pass中编写vert和frag函数,而是直接在SubShader中写CGPROGRAM和ENDCG,并在其中会有一个surf的函数。

 

这是一个表面着色器,是Unity原创的一种Shader书写方式。具体的来说,顶点/片元Shader是跟GPU的渲染流水线有关的,也就是GPU拥有这样的一个操作方式,所以我们这么写;而表面着色器是顶点/片元Shader的再上一层抽象,面对的是编写Shader的程序员,它将Shader抽象成三个方面:表面着色器、光照模型和光照着色器,方便理解和编写。

 

再次强调一遍,表面着色器实际上也是顶点/片元Shader,Unity会根据编写的代码自动生成顶点/片元Shader,与普通的Shader本质上并没有区别。在Inspector中随便查看一个表面着色器:


重点关注一下Surface shader这一行,如果是顶点/片元Shader这里会显示“no”,也就是不是一个表面着色器,而这边显示了一个“Show generated code”,说明该Shader就是一个表面着色器,点击看看,里面的代码就是Unity自动根据表面着色器生成的顶点/片元着色器。

 

当然里面因为各种平台的判断会十分的复杂,我们可以就看看vert和surf函数中是怎么操作的。

 

一个简单的表面着色器:

下面搞个例子给大家看看表面着色器的结构,会实现一个使用Lambert光照模型的一个处理法线贴图的Shader,让我们来看看用表面着色器来实现是多么的简单:

Shader "Custom/Chapter17/BumpedSpecular" {// 显示在Unity Inspector上的属性,给用户提供控制// 形式:属性名("Inspector显示的标签", 属性类型) = 初始化数据Properties{// 这是一个Color,初始值为白色,也就是(1,1,1,1)_Color("Main Color", Color) = (1, 1, 1, 1)// 这是一个图片,用作模型的贴图,默认为全白_MainTex("Main Tex", 2D) = "white" {}// 这是一个图片,用作模型的法线贴图,默认为没用任何凹凸效果的"bump",千万别设置成了"white"了_BumpMap("Bump Map", 2D) = "bump" {}}// 本来SubShader下会有各种Pass来控制Shader// 但在表面着色器中,Unity会根据下面的代码自动生成各种Pass,提供给GPU使用SubShader{// 标签RenderType设置为Opaque,指明渲染的物体是个不透明物体Tags { "RenderType" = "Opaque" }// 类似于模型的LOD,设定值大于该数值才会渲染(不过我不太清楚LOD值是怎么算的,有了解的希望能指点一下)LOD 300// CGPROGRAM和ENDCG,中间就是表面着色器的代码了CGPROGRAM// 使用surf作为表面着色器的输入输出函数,并使用Lambert光照模型,没错指明光照模型只需要这么一行#pragma surface surf Lambert// 选择模型目标为3.0,2.0能使用传递的参数比较少,防止shader奔溃所以选择3.0#pragma target 3.0// 申明Properties中的属性,以便在surf函数中调用sampler2D _MainTex;sampler2D _BumpMap;fixed4 _Color;// 输入结构struct Input{// 想要获取图片的uv坐标,固定格式就是图片名称前加uvfloat2 uv_MainTex;float2 uv_BumpMap;};// surf函数,表面着色器的处理函数void surf(Input IN, inout SurfaceOutput o){// 使用tex2D函数对_MainTex的uv_MainTex坐标进行采样,获取到颜色值fixed4 tex = tex2D(_MainTex, IN.uv_MainTex);// 采样值与_Color混合输出到Unity预置输出结构SurfaceOutput的Albedo中o.Albedo = tex.rgb * _Color.rgb;// 透明度操控是没有作用的,因为是不透明物体o.Alpha = tex.a * _Color.a;// 获取法线用到了两个函数// 首先tex2D类似上面的对_MainTex的采样,获取到颜色值// 其次UnpackNormal把获取到颜色值的信息转换成法线值,最后输出到Normal中o.Normal = UnpackNormal(tex2D(_BumpMap, IN.uv_BumpMap));}ENDCG}// 申明备用的Shader,如果以上Pass无法运行的话FallBack "Legacy Shaders/Diffuse"}

总结:

使用表面着色器能很方便的实现各种效果,而我们不用考虑光照模型的具体实现,但使用方便就意味着能实现的效果少,它仅仅算是顶点/片元Shader的一个子集。

 

优势是表面着色器在处理平行光的同时,还能处理点光源和聚光灯,这样Shader的编写可以摆脱那些编写的重复。

 

所以建议使用各种光源时使用表面着色器,而需要实现特定效果时使用顶点/片元Shader。

 

接下来几篇文章我准备还是先介绍一下经典的几个光照模型,有了一些对Shader的认识后,去找一些好玩的效果分析分析,学习效果会好一点。

 

最后给大家一个福利,Unity内置的Shader和Unity提供的Shader库具体的实现(引擎中能找到内置库,但没有内置的Shader,大家可以参考一下):

http://download.csdn.net/detail/u012632851/9818881


0 0
原创粉丝点击