【浅墨Unity3D Shader编程】之二 雪山飞狐篇:Unity的基本Shader框架写法&颜色、光照与材质

发表于2016-09-07
评论0 1.8k浏览
  本篇文章中,我们学习了Unity Shader的基本写法框架,以及学习了Shader中Properties(属性)的详细写法,光照、材质与颜色的具体写法。写了6个Shader作为本文Shader讲解的实战内容,最后创建了一个逼真的暴风雪场景进行了Shader的测试。依旧是国际惯例先上本文配套程序的截图。
  先是一张远眺图:


  浅墨在场景中放置了一个自动旋转的剑阵,瞬间武侠气息爆棚:


来一张近距离:


  看到银白色的世界漫天飞雪,剑阵列为圈旋转,有没有雪山飞狐的即视感呢?
  需要说明的是,由于CSDN的图片上传限制2Mb,这样画质的场景做成GIF上传不了。而静态的图片没有动态的表现力,感受不到风雪吹到自己身上的那种刺骨的感觉,所以在这里贴出的图,表现力已经是大打则扣了,而音效和背景音乐更是听不了,表现力就更是不如亲自运行了,所以浅墨推荐感兴趣的同学可以下载此场景的exe自己运行玩耍,赏玩。且此场景有些庞大,徒步走估计5分钟才能走到场景边界。Please enjoy~
  点击这里
  下载此“雪山飞狐”场景的exe。
  另外提醒,场景unity源文件和源代码在末尾提供下载。
  OK,我们正式开始。

一、一些基本概念认知
  1.1
  Shader和Material的基本概念认知
  先引用一段文字,阐述Shader和Material的基本关系:
  “
  Shader(着色器)实际上就是一小段程序,它负责将输入的Mesh(网格)以指定的方式和输入的贴图或者颜色等组合作用,然后输出。绘图单元可以依据这个输出来将图像绘制到屏幕上。输入的贴图或者颜色等,加上对应的Shader,以及对Shader的特定的参数设置,将这些内容(Shader及输入参数)打包存储在一起,得到的就是一个Material(材质)。之后,我们便可以将材质赋予合适的renderer(渲染器)来进行渲染(输出)了。
  所以说Shader并没有什么特别神奇的,它只是一段规定好输入(颜色,贴图等)和输出(渲染器能够读懂的点和颜色的对应关系)的程序。而Shader开发者要做的就是根据输入,进行计算变换,产生输出而已。
  “
  这段文字出自[《[猫都能学会的Unity3D Shader入门指南(一)》
  是比较好的Unity Shader的入门文章,可惜只写了两篇,后面就没有继续了。浅墨在文章开头懒得写了,就讲这句引用了过来。
  1.2
  背景知识说明
  在这里需要说明,学习Unity中的Shader编程,最好是之前对OpenGL或Direct3D的渲染状态等相关知识有一个基本的了解。如果之前没有太接触过这方面的知识,可以看看浅墨写的DirectX相关的教程。而需要大量恶补提升图形编程功力的童鞋,可以在[NVIDIA]和[AMD]
  开发者网站上可以找一些着色器教程和文档来啃啃。
  对于本期的光照和材质,需要的背景知识可以看浅墨之前写的这篇以DirectX为载体的光照和材质导论式的文章:
  《[【Visual C++】游戏开发笔记四十 浅墨DirectX教程之八 绘制真实质感的三维世界:光照与材质专场
  如果对其中的C++&DirectX的代码不太熟悉的话,没关系。看看概念,了解个大概就可以了。

二、 Unity中Shader的三种基本类型
  我们知道,计算机图形学的中渲染管线一般可以分为两种类型:
1、固定功能渲染管线(fixed-functionrendering pipelines)
2、可编程渲染管线(programmablerendering pipelines)
  按这样的分类思路,在Unity中,Shader便可以分成如下三种基本类型:
1、固定功能着色器(Fixed Function Shader)
2、表面着色器(Surface Shader)
3、顶点着色器&片段着色器 (Vertex Shader & Fragment Shader)
  顾名思义,其中的固定功能着色器便是我们所说的固定功能渲染管线(fixed-functionrendering pipelines)的具体表现,而表面着色器、顶点着色器以及片段着色器便属于可编程渲染管线。下面分别对其进行简单的介绍。
  2.1
  关于固定功能着色器
  这里的固定功能着色器可以说是Unity为Shader的书写自带的一层壳,Unity已经在内部为我们做了大量的工作,我们只要稍微记住一些关键字、一些规范就可以实现出很多不错的效果。固定功能着色器是我们初学Unity Shader的最近几篇文章中的主要学习对象。
而后面的表面着色器、顶点着色器以及片段着色器就是在固定功能着色器的基础上嵌套了CG语言的代码而成的更加复杂的着色器。我们来看看他们的一些基本概念。
  2.2
  关于表面着色器
  表面着色器(Surface Shader)这个概念更多的只是在Unity中听说,可以说是Unity自己发扬光大的一项使Shader的书写门槛降低和更易用的技术。我们会在接下来的学习中逐渐意识到Unity是如何为我们把Shader的复杂性包装起来,使其书写的过程更便捷和易用的。
  2.3
  关于顶点着色器和片段着色器
  研究过Direct3D和OpenGL着色器编程的童鞋们一定对这两者不陌生。我们来简单介绍一下他们的用途。
  顶点着色器:产生纹理坐标,颜色,点大小,雾坐标,然后把它们传递给裁剪阶段。
  片段着色器:进行纹理查找,决定什么时候执行纹理查找,是否进行纹理查找,及把什么作为纹理坐标
  2.4
  如何区分Unity中的Shader类型
  在Unity中想要区分他们很简单。后面熟悉了自然知道。在这里浅墨先剧透一下:
  没有嵌套CG语言,也就是代码段中没有CGPROGARAM和ENDCG关键字的,就是固定功能着色器。
  嵌套了CG语言,代码段中有surf函数的,就是表面着色器。
  嵌套了CG语言,代码段中有#pragma vertex name和 #pragma fragment frag声明的,就是顶点着色器&片段着色器。

三、Unity中将Shader赋给Material的两种方法
  在Unity中将Shader赋给Material使用的两种方法。
  【方法一】
  直接将Shader拖拽到Material之上。这种方法我们上篇文章中已经多次讲到,也就是这样:


  【方法二】
  在Material的Inspector面板中选择。
  Unity中内建的Shader都是通过这种方式来让Material使用的。在Material的Inspector中,其名字下方的Shader栏中选择。可以发现Unity已经为我们准备好了很多种不同的Shader,基本可以满足居家旅行的需求了。


  而对于我们自己新写的Shader,也会在这个菜单栏中显示出来。细心的朋友们看上图的时候,肯定就已经发现了。
  这里选择的菜单取决于我们Shader中定义Shader的第一行代码时紧接着Shader关键字的引号“”里面的书写方式:


四、Unity 中Shader的基本框架
  因为着色器代码可以说专用性非常强,因此Shader的设计者人为地规定了它的基本结构。而Unity中S
  hader整体的框架写法可以用如下的形式来概括:
  Shader "name" { [Properties] SubShaders[Fallback] }
  也就是说,Unity中所有着色器都是由Shader关键字开始,随后的字符串表示着色器的名字。这个名字会显示在Inspector检视面板中。所有用于这个着色器的代码必须放置在之后的大括号中:{ }(称为“块”)。ps:该名字应该是短且描述性的文字。它并不需要和shader文件名相同。而想要把着色器加入到Unity的子菜单里,名字需要用斜线(/)。例如:浅墨Shader编程/TheFirstShader就是一个名叫TheFirstShader的着色器,而这个着色器位于“浅墨Shader编程”的子菜单下。
这样,我们就可以在Shader后面紧跟着的引号中用“/”来构造出子二级甚至多级的子菜单来,方便了后面Shader写多了时候的合理分类,不至于太乱。
  OK,我们继续讲。有图有真相,Shader整体的框架写法用图来说就是这样:


  看图可以知道,首先是一些属性定义,用来指定这段代码将有哪些输入。接下来是一个或者多个的子着色器,在实际运行中,哪一个子着色器被使用是由运行的平台所决定的。子着色器是代码的主体,每一个子着色器中包含一个或者多个的Pass。在计算着色时,平台先选择最优先可以使用的着色器,然后依次运行其中的Pass,然后得到输出的结果。最后指定一个Fallback,可译为“回滚”,俗称备胎,用来处理所有SubShader都不能运行的情况(比如目标设备实在太老,所有SubShader中都有其不支持的特性,于是只好用备胎了,不然就显示不出来)。
  不同的图形卡有不同的性能,这对游戏开发者来说是永恒的问题,而这恰恰就是子着色器为什么可以发光发热的原因。若我们开发出了一种使用了当前业界前沿技术构成的Shader,这种Shader目前只有百分之1的牛逼哄哄的显卡可以支持。
  比较明智的做法是,把这套采用最前沿技术的Shader作为我们众多SubShader的其中的一员,然后还得准备一堆Plan B,应对其他硬件上的运行。也就是说,我们为所期望的采用最新技术的效果编写一个子着色器,然后为之前古老的显卡再编制一些备用的着色器。这些子着色器能选择使用更低层次的方式来实现我们的效果,或者选择放弃实现某些细节,确保无论在什么机器上跑,都能够运行出正确的效果。虽然这些效果会有一些细微的差别,因为使用的SubShader是不一样的,但却保证了我们的Shader在任何机器上都跑得起来。
  PS:在实际进行表面着色器的开发时,我们就是直接在SubShader这个层次上写代码,系统会将把我们的代码编译成若干个合适的Pass。
  用一个实例代码来说明吧。
  我们在Project面板中右键->Create->Shader。新建一个Shader文件,然后双击打开,删掉原先代码,分分钟,我们按照上文的讲解,对照着图示,就可以写出如下框架的Shader代码来:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
{
 
   //-------------------------------【属性】-----------------------------------------
     Shader "浅墨Shader编程/0.Shader框架示例"
{
       //-------------------------------【属性】-----------------------------------------
       Properties
       {
              //纹理
              _MainTex("基本纹理",2D)="White"{TexGen ObjectLinear}    
       }
 
       //---------------------------------【子着色器1】----------------------------------
       SubShader
       {
              //----------------通道---------------
              Pass
              {
                     //设置纹理为属性中选择的纹理
                     SetTexture[_MainTex]{combine texture}
              }
 
       }
 
       //---------------------------------【备胎】----------------------------------------
       //备胎设为Unity自带的普通漫反射
       Fallback" Diffuse "
}
  Babybus-u3d技术交流-【浅墨Unity3D Shader编程】之二 雪山飞狐篇:Unity的基本Shader框架写法&颜色、光照与材质
  解释起来就是:
1、
  着色器通过properties来可选的定义一个可通过材质设定界面来自定义的列表。具体到上述代码中写的Properties,就是定义了一个基本属性,参数名叫做_MainTex,在编辑器中显示的名称叫做“基本纹理”, 且纹理生成模式为ObjectLinear。
2、
  后面紧跟着核心部分子着色器SubShader,里面的一个Pass里面设置了纹理为我们属性中定义的那个_ MainTex。
3、
  添加一句Fallback代码用于应对我们Shader中的SubShader不能正确运行的情况。
  需要注意的是,SubShader在UnityShader的代码段中必须有且至少有一个,而properties和fallback对于追求简单的Shader,是可以不写出来的。而复杂一点的Shader,当然各种properties、fallback什么的肯定都有,甚至有多个SubShader,而每个SubShader中又有多个Pass。
  这个框架程序我们后面写新的Shader的时候就可以直接复制然后粘贴,接着在Properties中添加新的属性,SubShader中填充新的Pass以及开辟新的SubShader就行,就像做填空题一样。

五、Properties 属性相关内容讲解
  下面,我们详细地来看一看作为Shader框架中三大组成部分之一的Properties属性的相关内容。
  properties一般定义在着色器的起始部分,我们可以在Shader书写的时候定义多种多样的属性,而使用Shader的时候可以直接在材质检视面板(Material Inspector)里编辑这些属性,取不同的值或者纹理。这可以说是Unity贴心&可见即所得的又一体现吧。
  以Unity自带的BasicVertex Lighting 基本顶点光照为例,一张很直观的图就是这样:


  需要注意,Properties块内的语法都是单行的。每个属性都是由内部名称开始,后面括号中是显示在检视面板(Inspector)中的名字和该属性的类型。等号后边跟的是默认值。


  5.1
  Properties属性 相关代码写法列举
  这一小节我们列举Unity中Shader的Properties属性相关语法参考,可以在需要时进行查阅:
1
Properties { Property [Property ...] }
  定义属性块,其中可包含多个属性,其定义如下:
1
name ("display name", Range (min, max)) =number
  定义浮点数属性,在检视器中可通过一个标注最大最小值的滑条来修改。 
1
name ("display name", Color) =(number,number,number,number)
  定义颜色属性 
1
name ("display name", 2D) = "name" {options }
  定义2D纹理属性
1
name ("display name", Rect) = "name"{ options }
  定义长方形(非2次方)纹理属性
1
name ("display name", Cube) = "name"{ options }
  定义立方贴图纹理属性
1
name ("display name", Float) = number
  定义浮点数属性
1
name ("display name", Vector) =(number,number,number,number)
  定义一个四元素的容器(相当于Vector4)属性
  5.2
  一些细节说明
  包含在着色器中的每一个属性通过name索引(在Unity中, 通常使用下划线来开始一个着色器属性的名字)。属性会将display name显示在材质检视器中,还可以通过在等符号后为每个属性提供缺省值。
  对于Range和Float类型的属性只能是单精度值。
  对于Color和Vector类型的属性将包含4个由括号围住的数描述。
  对于纹理(2D, Rect, Cube) 缺省值既可以是一个空字符串也可以是某个内置的缺省纹理:"white", "black", "gray" or"bump"
  随后在着色器中,属性值通过[name]来访问。
  接着,让我们看一个示例,了解属性
  Properties的实际用法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Shader "浅墨Shader编程/SimpleWater"
{
       Properties{
              //properties for water shader
              //水着色器的属性
              _WaveScale("Wave scale", Range (0.02,0.15)) = 0.07 // 滑动条
              _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) // 颜色
              _ReflectionTex("Environment Reflection", 2D) = "" {} // 纹理
              _RefractionTex("Environment Refraction", 2D) = "" {}
              _Fresnel("Fresnel (A) ", 2D) = "" {}
              _BumpMap("Bumpmap (RGB) ", 2D) = "" {}
       }
 //后续代码省略
………
 
}
  5.3
  关于纹理属性选项
  纹理属性在本文的第一个示例中就有用到,这里先再贴一遍2D纹理属性的写法:
  name ("display name", 2D) ="name" { options }
  需要注意的是,包含在纹理属性的大括号中的选项Options是可选的。可能的选项有如下:
  TexGen纹理生成类型。
  即
  纹理的自动生成纹理坐标时的模式,可以是ObjectLinear, EyeLinear,SphereMap, CubeReflect, CubeNormal的其中之一;这些模式和OpenGL纹理生成模式相对应。注意如果使用自定义顶点程序,那么纹理生成将被忽略。
  LightmapMode 光照贴图模式
  如果我们给出这个选项,纹理将能被渲染器的光线贴图属性所影响。纹理不能被使用在材质中,而是取自渲染器的设定。这个我们以后会讲到。

六、光照、材质与颜色相关内容讲解
  灯光和材质参数常常被用来控制内置的顶点光照。而Unity中的顶点光照也就是Direct3D/OpenGL标准的按每顶点计算的光照模型—— 光照打开时,光照受材质块,颜色材质和平行高光命令的影响。
  我们来一起看一看光照、材质与颜色具体的语法。
  这里讲到的都是采用固定功能渲染的代码写法,以及一些控制选项。讲得有些细了,不用一次全记住,需要的时候回过头来进行查阅就行了。
  6.1
  用于通道Pass中的代码写法列举
  这些代码一般是写在Pass{ }中的,细节如下:
1
Color Color
  设定对象的纯色。颜色即可以是括号中的四值(RGBA),也可以是被方框包围的颜色属性名。
1
2
Material { Material Block
}
  材质块被用于定义对象的材质属性。
1
Lighting On | Off
  开启光照,也就是定义材质块中的设定是否有效。想要有效的话必须使用Lighting On命令开启光照,而颜色则通过Color命令直接给出。
1
SeparateSpecular On | Off
  开启独立镜面反射。这个命令会添加高光光照到着色器通道的末尾,因此贴图对高光没有影响。只在光照开启时有效。
1
ColorMaterial AmbientAndDiffuse | Emission
  使用每顶点的颜色替代材质中的颜色集。AmbientAndDiffuse 替代材质的阴影光和漫反射值;Emission 替代 材质中的光发射值。
  6.2
  材质块Material Block 中相关代码写法列举
  如下这些代码的使用的地方是在SubShader中的一个Pass{ }中新开一个Material{ }块,在这个Material{ }块中进行这些语句的书写。这些代码包含了包含材质如何和光线产生作用的一些设置。这些属性默认为值都被设定为黑色(也就是说不产生作用)
  也就是说他们一般情况下可以被忽略。当然,还是有很多时候需要使用到他们的。
  Diffuse Color(R,G,B,A)
  漫反射颜色构成。这是对象的基本颜色。
  Ambient Color(R,G,B,A)
  环境色颜色构成.这是当对象被RenderSettings.中设定的环境色所照射时对象所表现的颜色。
  Specular Color(R,G,B,A)
  对象反射高光的颜色。(R,G,B,A)四个分量分别代表红绿蓝和Alpha,取值为0到1之间。
  Shininess Number
  加亮时的光泽度,在0和1之间。0的时候你会发现更大的高亮也看起来像漫反射光照,1的时候你会获得一个细微的亮斑。
  Emission Color
  自发光颜色,也就是当不被任何光照所照到时,对象的颜色。(R,G,B,A)四个分量分别代表红绿蓝和Alpha,取值为0到1之间。
  而打在对象上的完整光照颜色最终是:
  FinalColor=
  Ambient RenderSettings ambientsetting +
(Light Color Diffuse + Light Color *Specular) + Emission
  翻译过来的中文式子便是:
  最终颜色=环境光反射颜色 渲染设置环境设置 (灯光颜色漫反射颜色+灯光颜色镜面反射颜色)+自发光
  知道了这个式子,我们就知道了,在各种光的综合作用下,我们材质最终的颜色是怎么来的了。
  需要注意的是:方程式的灯光部分(也就是带括号的部分)对所有打在对象上的光线都是重复使用的。而我们在写Shader的时候常常会将漫反射和环境光光保持一致(所有内置Unity着色器都是如此)。

七、Shader书写实战
  上面讲了一堆一堆的概念,估计大家一遍看下来头都大了。没关系,让我们看一些示例Shader的写法,弄清楚上面这一堆堆的概念是如何应用的。
1、
  单色Shader
  首先,用上文讲到的Color命令,写出一个有效代码仅仅四行的袖珍Shader:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Shader
"浅墨Shader编程/1.基础单色"
 
 
Shader"浅墨Shader编程/1.基础单色"
{
    //---------------------------------【子着色器】----------------------------------
    SubShader
    {
        //----------------通道---------------
        Pass
        {
            //设为蓝色单色
            Color(0,0,0.6,0)
        }
    }
}
  此Shader编译后赋给材质的效果如下:


2、
  材质颜色&开启光照的Shader
  同样的,我们可以在Pass中加上材质块Material,在其中将将材质的漫反射和环境光反射颜色设为相同,并且在该Pass中开启光照:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
Shader
"浅墨Shader编程/2.材质颜色设置&开启光照"
 
 
 
    //---------------------------------【子着色器1】----------------------------------
 
 
    SubShader 
 
    {    
 
        //----------------通道---------------
 
 
        Pass 
 
        
 
            //----------材质------------
 
 
            Material 
 
            
 
                //将漫反射和环境光反射颜色设为相同
 
 
                Diffuse(0.9,0.5,0.4,1) 
 
                Ambient(0.9,0.5,0.4,1) 
 
            
 
            //开启光照
 
 
            Lighting On 
 
        
 
    
 
}
  Babybus-u3d技术交流-【浅墨Unity3D Shader编程】之二 雪山飞狐篇:Unity的基本Shader框架写法&颜色、光照与材质
  此Shader编译后赋给材质的效果如下:
3、
  可调漫反射光的Shader
  在上面Shader的基础上,我们引入一个color属性,于是就得到了如下可调漫反射光颜色的Shader:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
Shader "浅墨Shader编程/3.简单的可调漫反射光照"
 {
    //-------------------------------【属性】-----------------------------------------
    Properties
    {
        _MainColor ("主颜色", Color) = (1,.1,.5,1)
 
    }
 
    //---------------------------------【子着色器】----------------------------------
    SubShader
    {
        //----------------通道---------------
        Pass
        {
            //-----------材质------------
            Material
            {
                //可调节的漫反射光和环境光反射颜色
                Diffuse [_MainColor]
                Ambient[_MainColor]
            }
            Lighting On
        }
    }
}
  此Shader编译后赋给材质的效果如下:


4、
  光照材质完备beta版Shader
  我们把余下的Material属性补上,便有了此光照材质完备beta版的shader:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
Shader "浅墨Shader编程/4.光照材质完备beta版Shader"
{
    //-------------------------------【属性】-----------------------------------------
    Properties
    {
        _Color ("主颜色", Color) = (1,1,1,0)
        _SpecColor ("反射高光颜色", Color) = (1,1,1,1)
        _Emission ("自发光颜色", Color) = (0,0,0,0)
        _Shininess ("光泽度", Range (0.01, 1)) = 0.7
    }
 
    //---------------------------------【子着色器】----------------------------------
    SubShader
    {
        //----------------通道---------------
        Pass
        {
            //-----------材质------------
            Material
            {
                //可调节的漫反射光和环境光反射颜色
                Diffuse [_Color]
                Ambient [_Color]
                //光泽度
                Shininess [_Shininess]
                //高光颜色
                Specular [_SpecColor]
                //自发光颜色
                Emission [_Emission]
            }
            //开启光照
            Lighting On
        }
    }
}
  此Shader编译后赋给材质的效果如下,可以自由定制的选项多了不少:


5、
  简单的纹理载入Shader
  然后我们看一个简单的纹理载入Shader的写法:
1
2
3
4
5
6
[cpp]
view plain
copy
print
?
[
  在CODE上查看代码片

  [


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
Shader "浅墨Shader编程/5.简单的纹理载入Shader"
{
       //-------------------------------【属性】-----------------------------------------
       Properties
       {
              //纹理
              _MainTex("基本纹理",2D)="White"{TexGen SphereMap}
       }
       //---------------------------------【子着色器1】----------------------------------
       SubShader
       {
              //----------------通道---------------
              Pass
              {
                     //设置纹理为属性中选择的纹理
                     SetTexture[_MainTex]{combine texture}
              }
       }
       //---------------------------------【备胎】----------------------------------------
       //备胎设为Unity自带的普通漫反射
       Fallback" Diffuse "
}
  此Shader编译后赋给材质的效果如下:


  需要注意,这里用到了一点纹理生成的内容,具体用法我们下次再细讲。
6、
  光照材质完备正式版Shader
  结合Shader4 beta版的光照材质Shader和Shader5简单的纹理载入,我们写成了这篇文章的最终版Shader:
1
2
3
4
5
6
[cpp]
 view plain
 copy
 print
 ?
 [
  在CODE上查看代码片
  [


  派生到我的代码片
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
Shader "浅墨Shader编程/6.光照材质完备正式版Shader"
{
    //-------------------------------【属性】-----------------------------------------
    Properties
    {
        _Color ("主颜色", Color) = (1,1,1,0)
        _SpecColor ("高光颜色", Color) = (1,1,1,1)
        _Emission ("自发光颜色", Color) = (0,0,0,0)
        _Shininess ("光泽度", Range (0.01, 1)) = 0.7
        _MainTex ("基本纹理", 2D) = "white" {}
    }
 
    //--------------------------------【子着色器】--------------------------------
    SubShader
    {
        //----------------通道---------------
        Pass
        {
            //-----------材质------------
            Material
            {
                //可调节的漫反射光和环境光反射颜色
                Diffuse [_Color]
                Ambient [_Color]
                //光泽度
                Shininess [_Shininess]
                //高光颜色
                Specular [_SpecColor]
                //自发光颜色
                Emission [_Emission]
            }
            //开启光照
            Lighting On
            //开启独立镜面反射
            SeparateSpecular On
            //设置纹理并进行纹理混合
            SetTexture [_MainTex]
            {
                Combine texture * primary DOUBLE, texture * primary
            }
        }
    }
}
  其中,涉及到了纹理混合的知识,我们稍后会讲解。
  此Shader编译后赋给材质的效果如下,可以发现,在这么多的可定制选项下,我们可以自由调节出自己喜欢的材质效果来:


  自由调节出各种诡异的材质:





  OK,更多的材质效果图就不放出了,大家下载文章末尾处提供的源工程,然后找到这个Shader自己调着玩吧。本文中所有的Shader和Material都位于Shaders文件夹中:


八、QianMo’s Toolkit升级到v1.1版
  上次发布QianMo’s Toolkit v1.0之后,发现有一些可以改进的地方,以及一些新功能,于是就花了点时间将Tookit更新了一下,写了一点新功能,升级到了v1.1。
  8.1
  QianMo’s Toolkit v1.1版改动说明:
1、新加入工具SetMaxFPS 用于突破Unity每秒渲染 60帧的设定,自由设置最大帧率。
2、新加入工具ShowObjectInfoInGamePlay,用于发布游戏之后显示文本信息。之前的ShowObjectInfo仅能在Unity测试过程中显示文本信息。
3、修复导入后的警告提示“Someare Mac OS X (UNIX) and some are Windows.”
  8.2
  QianMo’s Toolkit v1.1版包含内容:
  ShowFPS
  :在游戏运行时显示帧率相关信息
  ShowObjectInfo
  :在测试过程里,于场景中和游戏窗口中分别显示添加给任意物体文字标签信息。隐藏和显示可选,基于公告板技术实现。
  ShowGameInfo
  :在游戏运行时显示GUI相关说明
  ShowLogo
  :在游戏运行时显示Logo
  ShowUI
  :在游戏运行时显示简单的镶边UI。
  SetMaxFPS
  :用于突破Unity每秒渲染 60帧的设定,自由设置最大帧率。
  ShowObjectInfoInGamePlay:
  用于发布游戏之后显示文本信息。


  点击这里单独下载QianMo’s Toolkit v1.1:
  【[QianMo’s Toolkit v1.1.unitypackage下载
  8.3
  设置Unity中最大帧率的写法
  不妨在这里贴一下设置最大帧率的代码,便需要的朋友参考:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
/-----------------------------------------------【脚本说明】-------------------------------------------------------
//      脚本功能:   设置在游戏运行时可达到的最大帧率
//      使用语言:   C#
//      开发所用IDE版本:Unity4.5 06f 、Visual Studio 2010   
//      2014年11月 Created by 浅墨   
//      更多内容或交流,请访问浅墨的博客:http://blog.csdn.net/poem_qianmo
//---------------------------------------------------------------------------------------------------------------------
 
//-----------------------------------------------【使用方法】-------------------------------------------------------
//      第一步:在Unity中拖拽此脚本到场景任意物体上,或在Inspector中[Add Component]->[浅墨's Toolkit v1.1]->[SetMaxFPS]
//      第二步:在面板中设置MaxFPSValue参数为需要的帧率值,以及其他参数
//---------------------------------------------------------------------------------------------------------------------
using UnityEngine;
using System.Collections;
 
//垂直同步数
public enum VSyncCountSetting
{
    DontSync,
    EveryVBlank,
    EverSecondVBlank
}
 
 
[AddComponentMenu("浅墨's Toolkit v1.1/SetMaxFPS")]
public class SetMaxFPS : MonoBehaviour
{
 
    public VSyncCountSetting VSyncCount = VSyncCountSetting.DontSync;//用于快捷设置Unity Quanity设置中的垂直同步相关参数
    public bool MaxNoLimit = false;//不设限制,保持可达到的最高帧率
    public int MaxFPSValue = 80;//帧率的值
 
 
 
    void Awake()
    {
        //设置垂直同步相关参数
        switch (VSyncCount)
        {
            //默认设置,不等待垂直同步,可以获取更高的帧率数
            case VSyncCountSetting.DontSync:
                QualitySettings.vSyncCount = 0;
            break;
 
            //EveryVBlank,相当于帧率设为最大60
            case VSyncCountSetting.EveryVBlank:
            QualitySettings.vSyncCount = 1;
            break;
            //EverSecondVBlank情况,相当于帧率设为最大30
            case VSyncCountSetting.EverSecondVBlank:
            QualitySettings.vSyncCount = 2;
            break;
 
        }
 
        //设置没有帧率限制,火力全开!
        if (MaxNoLimit)
        {
            Application.targetFrameRate = -1;
        }
        //设置帧率的值
        else
        {
            Application.targetFrameRate = MaxFPSValue - 1;
        }
 
    }
}
  拖动此脚本到场景的任意物体上,在Inspector新出现的这个脚本选项的Max FPS Value中填上自己期望的最大帧率就行了,前提是你的电脑有能力跑到这么高的帧数。或者直接勾上 Max No Limit的勾勾,便可以让你的电脑火力全开,用最高性能来跑出最大的帧率。

九、最终游戏场景效果演示——雪山飞狐
  上一次中我们处于炎热的夏威夷群岛之中,这次的场景,不妨让我们来到寒冷的冬季,领略刺骨的寒风。以大师级美工鬼斧神工的场景作品为基础,浅墨优化和压缩了此场景资源的尺寸,加入了音乐和音效,并修改了场景布局,加入了更多特效,于是便得到了如此这次让人颇显震撼的暴风雪场景。
  逼真的音效和暴风雪特效,让我们身临其境:


  浅墨在场景中放置了一个自动旋转的剑阵,瞬间武侠气息爆棚:


有没有要想要随便拿一把兵器,跃跃欲试:


我们将一些今天写的这些材质Shader运用到这些剑之上看看效果:


最后,放一张今天学的Shader的全家福:


  本篇文章的示例程序请点击此处下载:
  [【浅墨Unity3D Shader编程】之二 雪山飞狐篇配套Unity工程下载
  今天的文章到这里就基本结束了。这篇的内容算是非常多,信息量是非常大的,希望大家戒骄戒躁,看不懂的地方多看几遍。不用怕,Shader学起来其实很简单。

如社区发表内容存在侵权行为,您可以点击这里查看侵权投诉指引