Unity Shader入门精要学习笔记 - 第3章 Unity Shader 基础
来源作者:candycat http://blog.csdn.net/candycat1992/article/
概述
总体来说,在Unity中我们需要配合使用材质和Unity Shader才能达到需要的效果。一个最常见的流程是。
1)创建一个材质
2)创建一个Unity Shader,并把它赋给上一步创建的材质
3)把材质赋给要渲染的对象
4)在材质面板中调整Unity Shader的属性,以得到满意的效果
下图显示了Unity Shader和材质是如何一起工作来控制物体的渲染的。
Unity中的材质需要结合一个GameObject的Mesh 或者Particle Systems 组件来工作。它决定了我们的游戏对象看起来是什么样子的(这当然也需要Unity Shader的配合)。
Unity Shader 的基础:ShaderLab
Unity Shader 为控制渲染过程提供了一层抽象。如果没有使用Unity Shader(上左),开发者需要很多文件设置打交道,才能让画面呈现想要的效果,而在Unity Shader的帮助下(上右),开发者只需要使用ShaderLab来编写Unity Shader文件就可以完成所有的工作。
在Unity中,所有的Unity Shader 都是使用ShaderLab来编写的。ShaderLab是Unity提供的编写Unity Shader 的一种说明性语言。它使用了一些嵌套在花括号内部的语义来描述一个Unity Shader文件的结构。这些结构包含了许多渲染所需的数据,例如Properties 语句块中定义了着色器所需的各种属性,这些属性将会出现在材质面板中。从设计上来说,ShaderLab类似于CgFx的Direct3D Effects (.Fx)语言,它们都定义了要显示一个材质所需要的所有东西,而不仅仅是着色器代码。
一个Unity Shader 的基础结构如下所示
Unity 会在背后根据使用的平台来把这些结构编译成真正的代码和Shader 文件,而开发者只需要和Unity Shader 打交道即可。
Unity Shader 的结构
每个Unity Shader文件的第一行都需要通过Shader语义来指定该Unity Shader的名字。这个名字由一个字符串来定义,例如"MyShader"。当为材质选择使用的Unity Shader 时,这些名称就会出现在材质面板的下拉面板里。通过在字符串中添加斜杠(“/”),可以控制Unity Shader在材质面板中出现的位置。
Properties语义块中包含了一系列属性,这些属性将会出现在材质面板中。
Properties语义块通常定义如下:
开发者们声明这些属性是为了在材质面板中能够方便地调整各种材质属性。如果我们需要再Shader 中访问它们,就需要使用每个属性的名字。在Unity中,这些属性的名字通常由一个下划线开始。显示的名称则是出现在材质面板上的名字。我们需要为每个属性指定它的类型,常见的属性类型如下表。除此之外,我们还需要为每个属性指定一个默认值,在我们第一次把该Unity Shader赋给某个材质,材质面板上显示的就是这些默认值。
每个Unity Shader 文件可以包含多个SubShader语义块,但最少要有一个,当Unity需要加载这个Unity Shader时,Unity 会扫描所有的SubShader 语义块,然后选择第一个能够在目标平台上运行的SubShader。如果都不支持的话,Unity就会使用Fallback语义指定的Unity Shader。
Unity提供这种语义的原因在于,不同的显卡具有不同的能力。
SubShader语义块中包含的定义通常如下。
SubShader中定义了一系列Pass以及可选的状态和标签设置。每个Pass定义了一次完整的渲染流程,但如果Pass的数目过多,往往会造成渲染性能的下降。因此,我们应尽量使用最小数目的Pass。状态和标签同样可以在Pass声明。不同的是,SubShader中的一些标签设置是特定的。也就是说,这些标签设置和Pass中使用的标签是不一样的。而对于状态设置来说,其使用的语法是相同的。但是,如果我们在SubShader进行了这些而设置,那么将会用于所有的Pass。
ShaderLab提供了一系列渲染状态的设置指令,这些指令可以设置显卡的各种状态,下表给出了ShaderLab中常见的渲染状态设置选项。
当在SubShader块中设置了上述渲染状态时,将会应用到所有的Pass。如果我们不想这样(例如在双面渲染中,我们希望在第一个Pass中剔除正面来对背面进行渲染,在第二个Pass中剔除背面来对正面进行渲染),可以在Pass语义块中单独进行上面的设置。
SubShader的标签是一个键值对,它的键和值都是字符串类型。这些键值对是SubShader和渲染引擎之间的沟通桥梁。它们用来告诉Unity的引擎:SubShader我希望怎样以及何时渲染这个对象。支持的标签类型如下:
需要注意的是,上述标签仅可以在SubShader中声明,而不可再Pass块中声明。Pass块虽然也可以定义标签,但这些标签是不同于SubShader的标签类型。
Pass语义块的定义如下:
首先,我们可以在Pass中定义该Pass的名称,例如:
Name "MyPassName"
通过这个名称,我们可以使用ShaderLab的UsePass命令来直接使用其他Unity Shader 中的Pass,例如:
UsePass "MyShader/MYPASSNAME"
这样可以提高代码的复用性。需要注意的是,由于Unity内部会把所有Pass的名称转换成大写字母的表示,因此,在使用UsePass命令时必须使用大写形式的名字。
其次,我们可以对Pass设置渲染状态。SubShader的状态设置通用适用于Pass。除了上面提到的状态设置外,在Pass中我们还可以使用固定渲染管线的着色器命令。
Pass同样可是设置标签,但它的标签不同于SubShader的标签。这些标签页是用于告诉渲染引擎我们希望怎样来渲染该物体。下表给出了Pass中使用的标签类型。
除了上面普通的Pass定义外,Unity Shader 还支持一些特殊的Pass,以便进行代码复用或实现更复杂的效果。
紧跟着在各个SubShader语义块后面的,可以是一个Fallback指令。它用于告诉Unity,如果上面所有的SubShader在这块显卡上都不能允许,那就使用这个最低级的吧。
它的语义如下:
Fallback "name"
//或者
Fallback off
如上所述,我们可以通过一个字符串来告诉Unity这个最低级的Shader是谁,我们也可以关闭Fallback 功能。
事实上,Fallback 还会影响阴影的投射。在渲染阴影纹理时,Unity会在每个Unity Shader中寻找一个阴影投射的Pass。通常情况下,我们不需要自己专门实现一个Pass,这是因为Fallback使用的内置Shader包含了这样一个通用的Pass。因此,为每个Unity Shader正确设置Fallback是非常重要的。
Unity Shader 的形式
尽管Unity Shader可以做的事情非常多,但其最重要的任务还是指定各种着色器所需的代码。这些着色器代码可以写在SubShader语义块中(表面着色器的做法),也可以写在Pass语义块中(顶点/片元着色器和固定函数着色器的做法)。
在Unity中,我们可以使用下面3种形式来编写Unity Shader。而不管使用哪种形式,真正意义上的Shader代码都需要包含在ShaderLab 语义块中,如下所示:
Shader "MyShader"
{
Properties
{
//所需的各种属性
}
SubShader
{
//真正意义上的Shader代码会出现在这里
//表面着色器或者顶点/片元着色器或者固定函数着色器
}
表面着色器是Unity自己创造的一种着色器代码类型。它需要的代码量很少,Unity在背后做了很多工作,但渲染的代价比较大。它在本质上和下面要讲到的顶点/片元着色器是一样的。也就是说,当给Unity提供一个表面着色器的时候,它在背后仍旧把它转换成对应的顶点/片元着色器。我们可以理解成,表面着色器是Unity对顶点/片元着色器的更高一层的抽象。它存在的价值在于,Unity为我们处理了很多光照细节,使得我们不需要操心这些烦人的事情。
一个非常简单表面着色器实例代码如下:
Shader "Custom/Simple Surface Shader"
{
SubShader
{
Tags { "RenderType" = "Opaque" }
CGPROGRAM
#pragma surface surf Lambert
struct Input
{
float4 color : COLOR;
};
void surf(INPUT IN,inout SurfaceOutput o)
{
o.Albedo = ;
}
ENDCG
}
Fallback "Diffuse" }
从上述程序中可以看出,表面着色器被定义在SubShader语义块中的CGPROGRAM 和 ENDCG之间。原因是,表面着色器不需要开发者关心使用多少个Pass、每个Pass如何渲染等问题,Unity会在背后为我们做好这些事情。我们要做的只是告诉它:“使用这些纹理去填充颜色,使用这个法线纹理去填充法线,使用Lambert光照模型。”
CGPROGRAM 和 ENDCG之间的代码是使用CG/HLSL 编写的,也就是说,我们需要把CG/HLSL语言嵌套在ShaderLab语言中。值得注意的是,这里的CG/HLSL是Unity经封装后提供的,它的语法和标准的CG/HLSL语法几乎一样,但还是由细微不同的,例如有些原声的函数和用法Unity并没有提供支持。
在Unity中我们可以使用CG/HLSL 语言来编写顶点/片元着色器。它们更加复杂,但灵活性也更高。
一个非常简单的顶点/片元着色器示例代码如下:
和表面着色器类似,顶点/片元着色器的代码也需要定义在CGPROGRAM 和 ENDCG之间,但不同的是,顶点/片元着色器是写在Pass语义块内,而非SubShader内的。原因是,我们需要自己定义每个Pass需要使用的Shader代码。虽然我们可能需要编写更多的代码,但带来的好处是灵活性很高。更重要的是,我们可以控制渲染的实现细节。同样,这里的CGPROGRAM和ENDCG之间的代码也是使用CG/HLSL编写的。
上述两种Unity Shader 形式都使用了可编程管线。而对于一些较旧的设备,它们不支持可编程管线着色器,因此,这时候我们就需要使用固定函数着色器来完成渲染。这些着色器往往只可以完成一些非常简单的效果。
一个非常简单的固定函数着色器示例代码如下:
Shader "Tutorial/Basic"
{
Properties{
_Color ( "Main Color" , Color) = (,0.5,0.5,)
}
SubShader{
Pass{
Material{
Diffuse[_Color]
}
Lightinh On
}
}
}
可以看出,固定函数着色器的代码被定义在Pass语义块中,这些代码相当于Pass中的一些渲染设置。
对于固定函数着色器来说,我们需要完全使用ShaderLab的语法来编写,而非使用CG/HLSL。
由于现在绝大多数GPU都支持可编程的渲染管线,这种固定管线 编程方式已经逐渐被抛弃。实际上,在Unity5.2中,所有固定函数着色器都会在背后Unity被编译成对应的顶点/片元着色器,因此真正意义上的固定函数着色器已经不存在了。
如何选择哪种Unity Shader:
除非有非常明确的需求必须要使用固定函数着色器,例如需要在非常旧的设备上运行游戏,否则不要使用固定函数着色器。
如果想和各种光源打交道,你可能更喜欢使用表面着色器,但需要小心它在移动平台的性能表现。
如果光照数量少,例如只有一个平行光,那么使用顶点/片元着色器是一个更好的选择。
更重要的是,如果有很多自定义的渲染效果,那么顶点/片元着色器更好。
Unity Shader入门精要学习笔记 - 第3章 Unity Shader 基础的更多相关文章
- Unity Shader入门精要学习笔记 - 第17章 Unity的表面着色器探秘
转自 冯乐乐的<Unity Shader 入门精要> 2010年的Unity 3 中,Surface Shader 出现了. 表面着色器的一个例子. 我们先做如下准备工作. 1)新建一个场 ...
- Unity Shader入门精要学习笔记 - 第16章 Unity中的渲染优化技术
转自冯乐乐的 <Unity Shader 入门精要> 移动平台的特点 为了尽可能一处那些隐藏的表面,减少overdraw(即一个像素被绘制多次),PowerVR芯片(通常用于ios设备和某 ...
- Unity Shader入门精要学习笔记 - 第4章 学习 Shader 所需的数学基础
摘录自 冯乐乐的<Unity Shader入门精要> 笛卡尔坐标系 1)二维笛卡尔坐标系 在游戏制作中,我们使用的数学绝大部分都是计算位置.距离.角度等变量.而这些计算大部分都是在笛卡尔坐 ...
- Unity Shader入门精要学习笔记 - 第15章 使用噪声
转载自 冯乐乐的 <Unity Shader 入门精要> 消融效果 消融效果常见于游戏中的角色死亡.地图烧毁等效果.这这些效果中,消融往往从不同的区域开始,并向看似随机的方向扩张,最后整个 ...
- Unity Shader入门精要学习笔记 - 第14章非真实感渲染
转载自 冯乐乐的 <Unity Shader 入门精要> 尽管游戏渲染一般都是以照相写实主义作为主要目标,但也有许多游戏使用了非真实感渲染(NPR)的方法来渲染游戏画面.非真实感渲染的一个 ...
- Unity Shader入门精要学习笔记 - 第11章 让画面动起来
转自 冯乐乐的 <Unity Shader入门精要> Unity Shader 中的内置变量 动画效果往往都是把时间添加到一些变量的计算中,以便在时间变化时画面也可以随之变化.Unity ...
- Unity Shader入门精要学习笔记 - 第10章 高级纹理
转载自 冯乐乐的 <Unity Shader入门精要> 立方体纹理 在图形学中,立方体纹理是环境映射的一种实现方法.环境映射可以模拟物体周围的环境,而使用了环境映射的物体可以看起来像镀了层 ...
- Unity Shader入门精要学习笔记 - 第9章 更复杂的光照
转载自 冯乐乐的<Unity Shader入门精要> Unity 的渲染路径 在Unity里,渲染路径决定了光照是如何应该到Unity Shader 中的.因此,如果要和光源打交道,我们需 ...
- Unity Shader入门精要学习笔记 - 第8章 透明效果
转载自 冯乐乐的 <Unity Shader入门精要> 透明是游戏中经常要使用的一种效果.在实时渲染中要实现透明效果,通常会在渲染模型时控制它的透明通道.当开启透明混合后,当一个物体被渲染 ...
随机推荐
- zoj 2315 New Year Bonus Grant 解题报告
题目链接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=1315 题目意思:Bill Hates 是公司的老总,她管辖着很多程序 ...
- spring 从jsp到jsp
小例子 1.jar信息 2.web.xml文件配置 <?xml version="1.0" encoding="UTF-8"?> <web-a ...
- HDFS副本设置——默认3
首先 dfs.replication这个参数是个client参数,即node level参数.需要在每台datanode上设置. 其实默认为3个副本已经够用了,设置太多也没什么用. 一个文件,上传到h ...
- php filter 安全过滤函数
转自:http://www.blags.org/archives/741.html php 利用filter 扩展编写的参数处理静态类,欢迎使用.希望大家看得开心,用得放心. <?php /** ...
- poj3613Cow Relays——k边最短路(矩阵快速幂)
题目:http://poj.org/problem?id=3613 题意就是求从起点到终点的一条恰好经过k条边的最短路: floyd+矩阵快速幂,矩阵中的第i行第j列表示从i到j的最短路,矩阵本身代表 ...
- JAVA 内部类 (二)
一.为什么要使用内部类 为什么要使用内部类?在<Think in java>中有这样一句话:使用内部类最吸引人的原因是:每个内部类都能独立地继承一个(接口的)实现,所以无论外围类是否已经继 ...
- VMware桥接模式选择宿主机物理网卡
当宿主机有多块物理网卡时,VMware桥接模式也要根据情况选择使用的物理网卡. 比如宿主机有两块物理网卡,一个连外网,一个连内网,如果想与内网组成局域网就需要选择宿主机的内网网卡,反之选择外网网卡,当 ...
- UItextFied 的属性
网络学习笔记 在iPhone应用中通过UITextField填写信息时,经常出现出现自动更正输入信息.首字母大写等情况 尤其是在填写用户名时,这种本想提供便捷的功能反而让人感到特别麻烦 今天查了相关书 ...
- 开源跨平台声波传输库:Sonic
简介 [Sonic](https://github.com/linyehui/sonic) 是一个跨平台的声波传输库(iOS & Android),技术上类似于[chirp](http://c ...
- 1、css选择器
一.CSS rgb颜色对照表:https://www.114la.com/other/rgb.htm 1.在标签上设置style属性 <!DOCTYPE html> <html la ...