Unity Shader学习-2.Unity Shader基础

发表于2016-11-09
评论0 1.2k浏览

想免费获取内部独家PPT资料库?观看行业大牛直播?点击加入腾讯游戏学院游戏程序行业精英群

711501594
  一、概述
  从上一节我们了解了渲染流水线,而Shader则其实就是流水线中的某些特定阶段。接下来说一下Unity中的两个概念,Unity Shader和材质。
  Unity Shader和材质
  在Unity中进行开发的时候,需要Unity Shader和材质一起进行使用才能达到需要的效果。
  一般的流程是:
  ·         1、创建一个材质
  ·         2、创建一个Unity Shader,并将它赋给上一步创建的材质
  ·         3、把材质赋给要渲染的对象
  ·         4、在材质面板中对参数(纹理、值、漫反射系数等)进行控制以得到想要的效果
  由上面Unity Shader与材质的关系可以看出,UnityShader定义了渲染所需要的代码、属性和各种指令,而材质则允许我们对这些参数进行更改并将其最终赋给需要渲染的物体。
  Unity中的材质
  Unity中的材质需要结合Mesh或者Particle System来工作,它决定了我们的游戏对象是怎样表现的。Unity中的材质提供了面板来对各种参数进行调整。

材质面板]

  这样可视化的操作方法可以使得开发者不需要在代码中设置和改变渲染所需要的参数。
  Unity中的Shader
  在Unity中只要以下步骤则可创建一个Shader,通过菜单栏Assets->Create->Shader或者在Project视图中右击->Create->Shader可以进行创建。
  Unity总共提供四种Unity Shader模板给我们选择:
  ·         Standard Surface Shader:会产生一个包含了标准光照模型的表面着色器
  ·         Unlit Shader:会产生一个不包含光照(但包含雾效)的顶点/片元着色器
  ·         Image Effect Shader:会为我们实现屏幕后处理效果
  ·         Compute Shader:会产生一种特殊的Shader文件,这类Shader旨在利用GPU的并行性来进行一些与常规渲染流水线无关的计算
  一个Unity Shader需要与材质结合起来才能发挥他的作用,UnityShader的本质是一个文本文件,和Unity许多的外部文件一样都有导入设置面板。

设置面板

  其中包括了渲染队列、LOD等信息的显示,如果该着色器是一个固定函数着色器,在Fixed function的后面也会出现一个Show generated code按钮,通过点击此按钮我们可以查看固定函数着色器生成的顶点/片元着色器。而Compiledand show code下拉列表可以让开发者检查该Unity Shader针对不同的图像编程接口(DirectX、OpenGL)最终编译成的Shader代码。

二、Shader Lab
  Shader Lab可以说是Unity为了降低开发者的学习难度而提供的一套高层级的渲染抽象层。
  没有Shader Lab你可能需要像下面的开发者一样面对众多的问题。

没有ShaderLab的话

  而有了Shader Lab你只需要通过他来编写Unity Shader文件就可以完成所有的工作。

有了ShaderLab的话

三、结构
  先来看一下Unity Shader的基本结构。

UnityShader基本结构

  一个Unity Shader有他的名称定义、一个Properties定义,然后也有多个子着色器,最后的Fallback在上面的子着色器无效时生效,也就是默认值,每个子着色器有着多个Pass。
  UnityShader的名字
  每个Unity Shader文件的第一行都需要通过Shader语义来定义该Shader的名字,例如"Custom/MyShader",这就定义了我的着色器名字为MyShader而前面的Custom表示包含该Shader的目录。

Unity Shader名字

  定义后在材质中选择时就可以看到我们的着色器的位置在Custom目录下,名为MyShader。

目录结构

  当然我们也可以定义为Mycustom/Folder1/MyShader来定制多级目录。

多级目录

  UnityShader的属性
  在Properties语义块中包含了一系列的属性,这些属性将会显示在材质面板中。如下图的Main Color属性,用于表示颜色的属性值。

Unity Shader属性
  Properties的语义块的形式通常如下:
  Properties{    Name ("display name", PropertyType) = DefaultValue    //More...}
  其中的Name就是表示属性的名字,而display name则表示在材质面板上出现的名字,如上图中的Main Color,就是他的displayname,PropertyType则是为属性定义的类型,DefaultValue表示我们为属性设置的默认值,在我们第一次把Unity Shader赋给一个材质时,材质面板上显示的就是他的默认值。

属性类型

默认值的定义语法例子
Intnumber_Int("Int", Int) = 2
Floatnumber_Float("Float", Float) = 1.0

Range(min,max)

numberRange("Range", Range(0.0,10.0)) = 2.0




















  以上的类型可以规为以下几类:
  ·         数字类型,他们的默认值即为数据,如Int.Float.Range
  ·         四维向量,他们的默认值是用圆括号包围的一个四维向量,如Color.Vector
  ·         纹理类型,他们的默认值是一个字符串后面跟一个花括号来指定的,其中的字符串要么是空的要么是内置的纹理的名称,如"black", "white"等,花括号的用处原本是用于指定一些纹理属性的,但是在Unity5.0后的版本中这些选项被移除了。
  下面是关于这些属性的代码
Shader "Custom/Properties"{      
Properties {      
_Int("Int", Int) = 2      
_FLoat("Float", Float) = 1.0      
_Range("Range", Range(0,10)) = 5      
_Color("Color", Color) = (1,1,1,1)      
_Vector("Vector", Vector) = (0,0,0,1)      
_2D("2D", 2D) = ""{}      
_Cube("Cube", Cube) = "white"{}      
_3D("3D",3D) = "black"{}      
}       
Fallback "Diffuse"   
}
  Properties语义块的作用仅是为了让这些属性可以出现在材质面板中
 
材质面板

  SubShader
  每一个Unity Shader文件至少要包含一个SubShader语义块,Unity在需要加载这个Shader的时候,会扫描所有的SubShader语义块,然后选择第一个能够在目标平台上运行的SubShader。SubShader语义块 中包含的定义通常如下:
SubShader

{    //可选的/标签   
[Tags]   

//可选的/状态   
[RenderSetup]   

Pass{   
}   
//其他Pass
}
  在SubShader中定义了一系列的Pass及可选的状态(RenderSetup)和标签(Tags)设置。每个Pass定义一次完整的渲染流程,但是如果Pass过多,会造成渲染性能的下降。注意:如果我们对 SubShader进行了这些设置,那就会应用到所有的Pass。
  状态设置
  ShaderLab提供了一系列渲染状态的设置指令,这些指令可以设置显卡的各种状态,如开启混合/深度测试,具体选项如下。
  
状态名称设置指令解释
CullCull Back/Front/Off设置剔除模;式:剔除背;/正面/关闭:剔除
ZTest

Ztest Less Greater/LEqual/GEqual/Equal/NotEqual/Always


ZWriteZWrite On/Off

Blend

Blend SrcFacotr DstFactor

  标签
  SubShader的标签是一个键值对,它的键和值都是字符串类型,这些键值对是SubShader与渲染引擎之间的沟通桥梁。标签的结构如下:
  Tags {"TagName1" = "Value1" "TagName2" = "Value2"}
  SubShader支持的标签类型如下:


  Pass语义块标签声明仅可以在SubShader中场景,而不可以在Pass块中声明。Pass中虽然也可以定义标签,但这些标签是不同于SubShader中的标签的。
  Pass语义块包含的语义如下:
Pass{   
[Name]   
[Tags]   
[RenderSetup]   
//...
}
  [Name]:首先我们可以在Pass中定义Pass的名称
  Name "PassName"
  通过这个名称,我们可以使用ShaderLab的UsePass命令来直接使用其它的UnityShader中的Pass,例如:
  ·      UsePass"MyShader/MYPASS"
  这样可以提高代码复用性,但是由于Unity内部会把所有Pass的名称转换成大写字母的表示,因此在使用UsePass命令的时候必须使用大写形式的名字。
  Pass语义块的标签不同于SubShader的标签,下面是Pass语义块中的标签类型:


  ·         UsePass:使用该命令来复用其他Unity Shader中的Pass除了上面普通的Pass定义外,还支持一些特殊的Pass:
  ·         GrabPass:该Pass负责抓取屏幕并将结果存储在一张纹理中,以用于后续的Pass处理
  Fallback
  这一指令用于在其它的SubShader不能运作的时候,使用Fallback值中的Shader
Fallback "name"
Fallback off

三、Unity Shader的形式
  Unity Shader最重要的任务还是指定各种着色器所需的代码,这些着色器代码可以写在SubShader语义块中,也可以写在Pass语义块中。
SubShader "MyShader"{   
Properties{   
}   
SubShader{       
//表面着色器或者       
//顶点/片元着色器或者       
//固定函数着色器   
}
}

  表面着色器
  表面着色器是Unity自己创造的一种着色器代码类型。他有以下特点:
  ·         它需要的代码量很少
  ·          渲染的代价比较大(本质和顶点/片元着色器一样)
        Shader "Custom/Surace Shader"{     
SubShader{         
Tags{"RenderType" = "Opaque"}         
CGPROGRAM         
#pragma surface surf Lambert         
struct Input{             
float4 color : COLOR;         
};         
void surf(Input IN, inout SurfaceOutput o){             
o.Albedo = 1;         
}         
ENDCG     
}     
Fallback "Diffuse" 
}
  表面着色器是定义在SubShader语义块中的(而不是Pass语义块)CGPROGRAM 和ENDCG之间,这是因为表面着色器不关心开发者使用了多少的Pass,CGPROGRAM和ENDCG之间的代码是使用CG/HLSL编写的,但这里的CG/HLSL是Unity封闭后提供的,他的语法和标准的CG/HLSL几乎一样。
  顶点/片元着色器
  顶点片元着色器的特点是:
  ·         复杂
  ·          灵活性高
Shader "Custom/VertexFragment Shader"{     
SubShader{         
Pass{             
CGPROGRAM             
#pragma vertex vert             
#pragma fragment frag             
float4 vert(float4 v : POSITION) : SV_POSITION{                 
return mul(UNITY_MATRIX_MVP, v);             
}             
float4 frag() : SV_Target{                 
return fixed4(1.0,0.0,0.0,1.0);             
}             
ENDCG         
}     
}
  ·          顶点/片元着色器也要定义在CGPROGRAM和ENDCG之间,不同的是,顶点/片元着色器是写在Pass语义块内,而非SubShader内的。原因是我们需要自己定义的每个Pass需要使用的Shader代码。这样做的好处是灵活性高。
  固定函数着色器
  一些旧设备不支持可编程管线着色器,因此我们就需要固定函数着色器来完成渲染。
Shader "Custom/FixedShader"{   
Properties{       
_Color("Main Color", Color) = (1,1,1,1)   
}   
SubShader{       
Pass{           
Diffuse {_Color}       
}       
Lighting On   
}
}
  从上面代码可以看出,这些设置类似开关一样,没有之前提到的两个着色器那么灵活,对于固定函数着色器,我们需要完全使用ShaderLab的语法而不能使用CG/HLSL。
  选择Unity Shader的建议
  ·         除非设备是不支持可编程管线的着色器的,才考虑使用固定函数着色器,否则都使用可编程管线的着色器
  ·         想和各种光源打资产,则可以使用表面着色器,但要注意他在移动端的表现
  ·         如果需要使用的光照数目特别少,那么顶点/片元着色器是一个更好的选择
  ·         如果有很多自定义的渲染效果,那么选择顶点/片元着色器
  文/ZeroTryTryTry(简书作者)
  原文链接:http://www.jianshu.com/p/614382d059c1
  著作权归作者所有,转载请联系作者获得授权,并标注“简书作者”。

原文链接

著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

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

游戏学院公众号二维码
腾讯游戏学院
微信公众号

提供更专业的游戏知识学习平台