(二)Unity Shader基础知识(上)

 

一、Unity Shader概述


 

 

没有Unity编辑器情况下的shader编码(伪代码):

 

Main.cs

 1 Void InitializationI(){
 2 
 3 //从硬盘上加载顶点着色器代码
 4 
 5 String vertexShaderCode =LoadShaderFromFile(VertexShader.shader);
 6 
 7 //从硬盘上加载片元着色器代码
 8 
 9 String fragmentShaderCode = LoadShaderFromFile(FragmentShader.shader);
10 
11 //把顶点着色器加载到GPU中
12 
13 LoadVertexShaderFromString(vertexShaderCode);
14 
15 //把片元着色器加载到GPU
16 
17 LoadFragmentShaderFromString(fragmentShaderCode);
18 
19  
20 
21 //设置名为”VertexPosition”的属性的输入,即模型顶点坐标
22 
23 SetVertexShaderProerty(“VertexPosition”,vertices);
24 
25 //设置名为”MainTex”的属性的输入,someTexture是某张已加载的纹理坐标
26 
27 SetVertexShaderProerty(“MainTex”,someTexture);
28 
29 //设置名为”MVP”的属性的输入,MVP是之前由开发者计算好的变换矩阵
30 
31 SetVertexShaderProerty(“MVP”,MVP);
32 
33  
34 
35 //关闭混合
36 
37 Disable(Blend);
38 
39 //设置深度测试
40 
41 Enable(ZTest);
42 
43 SetZTestFunction(LessOrEqual);
44 
45 //其他设置
46 
47 ...
48 
49  
50 
51 }
52 
53 //每一帧进行渲染
54 
55 Void OnRendering(){
56 
57 //调用渲染命令
58 
59 DrawCall();
60 
61 //当涉及多种渲染设置时,我们可能还需要在这里进行改变各种渲染设置
62 
63 ...
64 
65 }

 

 

 

VertexShader.shader

 

 1 //输入:顶点位置、纹理、MVP变换矩阵
 2 
 3 In float3 VertexPosition;
 4 
 5 In sampler2D MainTex;
 6 
 7 In Matrix4x4 MVP;
 8 
 9 //输出:顶点经过MVP变换后的位置
10 
11 Out float4 position;
12 
13 Void main(){
14 
15 //使用MVP对模型顶点进行坐标进行变换
16 
17 Position = MVP * vertexPosition;
18 
19 }

 

 

FragmentShader.shader

 1 //输入VertexShader输出的Position、经过光栅化程序插值后的该片元对应的Position
 2 
 3 In float4 position;
 4 
 5 //输出:该片元的颜色值
 6 
 7 Out float4 fragColor;
 8 
 9 Void main(){
10 
11 //将片元颜色设为白色
12 
13 fragColor = float4(1.0,1.0,1.0,1.0);
14 
15 }

 

 

该伪代码已经简化,随着渲染的工程量变大,上述过程变得更加复杂和冗长,Unity Shader的出现为了改善上述情况。

 

1、材质和Unity Shader

使用Unity Shader常见流程,如下图:

 

2、Unity中的材质

创建材质的方法:

  1. 第一种,在Unity菜单栏中选择Assets->Create->Material
  2. 第二种,在Project视图右击->Create->Material
  3. 第三种,贴图拖至游戏对象会直接生成一个材质

 

默认情况下,新建的材质将使用Unity内置的Standard Shader,这是一种基于物理渲染的着色器。

3、Unity中的shader

创建shader的方法:

  1. Unity的菜单栏中选择Assets->Create->shader
  2. Project视图中右击->Create->shader

 

Unity5.2以及以上版本中,Unity一共提供了4种Unity Shader模板来让我们选择:

  1. Standard Surface Shader
  2. Unlit Shader
  3. Image Effect Shader
  4. Compute Shader 
  • Standard Surface Shader

包含了一个标准光照模型的表面着色器模板。

  • Unlit Shader

产生一个不会包含光照的基本的顶点/片元着色器。

  • Image Effect Shader

为我们实现各种屏幕后处理效果提供了一个基本模板。

  • Compute Shader

产生一种特殊的Shader文件,这类shader旨在利用GPU的并行性来进行一些与常规渲染流水线无关的计算。

以表面着色器为例:

  • 红色区域:单击Show generated code生成一个新文件,该文件将显示Unity在背后为该表面着色器生成的顶点/片元着色器。可以方便我们对生成的代码进行研究。
  • 紫色区域:用来查看该固定函数着色器生成的顶点/片元着色器。
  • 绿色区域:Compiled and show code可以让开发者检查该Unity Shader针对不同图像编程接口(例如OpenGL、D3D9、D3D11)最总编译成shader代码。

二、Unity Shader的基础:shaderLab


 

一、什么是shaderLab?

 

Unity Shader是Unity为开发者提高的高级层的渲染抽象层。和这层抽象层打交道的途径就是通过使用Unity提供的一种专门为Unity Shader服务的语言——ShaderLab。

 

Unity中,所有Unity Shader都是使用ShaderLab来编写。ShaderLab是Unity提供的编写UnityShader的一种说明性语言。它使用了一些嵌套在花括号内部的语义(syntax)来描述一个UnityShader文件的结构。这些结构包含了许多渲染所需的数据。他们都定义了要显示一个材质所需的所有东西,而不仅仅是着色器代码。

 

一个UnityShader的基础结构如下所示:

 

 1 Shader”ShaderName”{
 2   Properties{
 3     //属性
 4   }
 5   SubShader{
 6     //显卡A使用的着色器
 7   }
 8   SubShader{
 9     //显卡B使用的着色器
10   }
11   Fallback “VertexLit”
12 }

 

Unity在背后会根据适用平台把这些结构编译成真正的代码和shader文件,而开发者只需要和ShaderLab打交道即可。

 

二、Unity Shader的结构

1、Unity Shader的命名

每个Unity Shader文件的第一行都需要通过Shader语义来指定Unity Shader的名字。这个名字通过字符串来定义,这个名称会显示在材质列表里。例如:

1 Shader “Custom/MyShader”{}

 

2、材质和Unity Shader的桥梁:Properties

Properties语义块包含了一系列属性(Property),这些属性将会出现在材质面板。格式如下:

1 Properties{
2 
3     Name(“Display Name”,PropertyType)=DefaultValue;
4 
5     Name(“Display Name”,PropertyType)=DefaultValue;
6 
7     //更多属性
8 
9 }

 

属性名字Name:shader中访问的变量,在Unity中通常由一个_下划线开始。

显示名称Display name:出现在材质面板上的名字。

属性类型PropertyType:一些常见的属性的类型。

属性类型DefaultValue:属性的默认值。

 

Int、Float、Range这些数字类型的属性,其默认值就是一个单独的数字。

Color和Vector这类属性,其默认值是一个四维向量。

2D、Cube、3D这三种纹理型,默认值是通过一个字符串后面跟一个花括号来指定,字符串要么为空,要么是内置的纹理名称。如“white”,“black”“gray”或者“bump”。{}内原本用于指定一些纹理属性,通过TexGenCubeReflect、TexGenCubeNormal等选项,控制固定管线的纹理坐标生成。Unity5.0以后的版本{}中选项移除,需自己在顶点着色器中编写计算相应纹理坐标。

Unity允许我们重载默认的材质编辑面板,以提供更多自定义的数据类型。在Properties语义块中声明这些属性,也可以直接在Cg代码片段中定义变量。我们也通过脚本向shader中传递这些属性,Proper

3、重量级成员:SubShader

 

Unity加载这个UnityShader时,会扫描所有的SubShader语义块,然后选择第一个可以在目标平台上运行的SubShader。如果都不支持使用Fallback语义指定的UnityShader。

 

SubShader{

//可选的标签
[Tags]

//可选的状态
[RenderSetup]

//定义一次完整的的渲染流程,Pass过多导致渲染性能下降
Pass{
//可以在Pass内声明状态和标签
}

//Other Passes

}

 

[RenderSetup]状态设置

常见的渲染设置状态设置选项,这些指令可以设置显卡的各种状态。

当在SubShader块中设置了上述渲染状态,将会应用到所有Pass。

 

[Tags]标签

 

SubShader的标签(Tags)是一个键值对(Key/Value Pair),它的键和值都是字符串类型。这些键值对是SubShader和渲染引擎之间的沟通桥梁。它告诉Unity如何渲染。

Tags标签的结构如下:

Tags{“TagName” = “Value” “TagName2” = “Value2”}

[Pass]语义块

 

Pass{

[Name]//定义该Pass名称
 
[Tags]//除SubShader中的标签可以用在这里外,还可以使用固定管线着色器
 
[RenderSetup]

//other code

}

Pass标签中使用的标签类型:

 

 Unity内部会把所有Pass的名称转换成大写字母表示,在使用UsePass命令时必须使用大写形式的名字。

 

[其他特殊的Pass]

UsePass:可以使用UsePass命令,使用其他Unity Shader中的Pass。在使用UsePass命令时必须使用大写名字,例如:MyShader/MYPASSNAME

GrabPass:该Pass负责抓取屏幕并将结果存储在一张纹理中,以用来后续的Pass处理。

 

4、留一条后路:Fallback

紧跟所有SubShader语义块后面,可以是Fallback指令,它用于告诉Unity如果上面所有的SubShader在显卡上都不能运行,就使用最低级的Shader。

Fallback“最低级Shader名称”
//或者
Fallback Off //关闭Fallback

Fallback会影响阴影的投射,在渲染阴影纹理时,Unity会在每个UnityShader中寻找一个阴影投射的Pass。通常我们不需要自己去实现这个,内置shader包含了这个东西。

 

 

posted @ 2017-07-24 22:07  20世纪少年  阅读(369)  评论(2)    收藏  举报