Unity Shader(基本概念)
一、什么是Shader
shader,中文名为着色器。shader其实就是专门用来渲染图像的一种技术,通过shader,我们可以自定义显卡渲染画面的算法,使画面达到我们想要的效果。
shader分为两类:
- 顶点shader(3D图形都是由一个个三角面片组成的,顶点shader就是计算每个三角面片上的顶点,并为最终像素渲染做准备)。
- 像素shader,顾名思义,就是以像素为单位,计算光照、颜色的一系列算法。几个不同的图形API都有各自的shader语言,在DirectX中,顶点shader叫做 Vertex Shader ,像素Shader叫做 Pixel Shader; 在OpenGL中,顶点Shader也叫做 Vertex Shader ,但像素Shader叫做 Fragment Shader,也就是我们常说的片断Shader或者片元Shader。
二、Shader编程语言
既然Shader是一段代码,那必然要用一种语言来书写它,目前主流的有三种语言:
- 基于OpenGL的OpenGL Shading Language,简称GLSL。
- 基于DirectX的High Level Shading Language,简称HLSL。
- 还有NVIDIA公司的C for Graphic,简称Cg语言
那么我们在Unity中应该用什么语言来书写Shader呢?
官方的建议是用Cg/HLSL来编写,当然你也可以使用GLSL,主要是因为Cg/HLSL有更好的跨平台性,更倾向于使用Cg/HLSL来编写Shader程序。
Unity Shader严格来说并不是传统上的Shader,而是Unity自身封装后的一种便于书写的Shader,又称为ShaderLab。
三、什么是Unity Shader
在Unity中有3种Shader(其实就是三种不同的写法):
- Surface Shaders 表面着色器
- Vertex/Fragment Shaders 顶点/片断着色器
- Fixed Function Shaders 固定管线着色器
Surface Shader其实就是Unity对Vertex/Fragment Shader的又一层包装,以使Shader的制作方式更符合人类的思维模式,同时可以以极少的代码来完成不同的光照模型与不同平台下需要考虑的事情。
并且在Unity2018后的版本中推出了Unity官方自己的可视化Shader工具(Shader Graph)。从生成的代码上来看,也全部是用的Vertex/Fragment Shader,那是不是可以理解为在今后的可编程渲染管线中,Unity自己也抛弃了Surface Shader,而全部采用了Vertex/Fragment Shader?
四、Shader模板
Standard Surface Shader
标准表面着色器,是一种基于物理的着色系统(使用了Physically Based Rendering(简称PBR)技术,即基于物理的渲染技术),以模拟现实真实的方式来模拟材质与灯光之间的关系,可以很轻易的表现出各种金属反光效果,同时此种shader的书写逻辑也更符合人类的思维模式。
Unlit Shader
Vertex/Fragment Shader,也就是最基本的顶点片段着色器,不受光照影响的Shader,多用于特效、UI上的效果制作。这也是最简单最基础的。
Image Effect Shader
也是顶点片段着色器,只不过是针对后处理而定制的模板,后处理是什么呢?Bloom(也有人叫Glow/泛光/辉光等说法)、调色、景深、模糊等,这些基于最终整个屏幕画面而进行再处理的shader就是后处理。
Compute Shader
Compute shader是运行再图形显卡上的一段程序,独立于常规渲染管线之外的,它可以直接将GPU作为并行处理器加以利用,从而使GPU不仅具有3D渲染能力,还具有其他的运算能力。
五、材质与shader的关系
新建一个Unlit Shader,由于unity中shader就是运行在图形显卡上的一段包含指令的代码,所以我们想要再创建一个材质来关联它,这样才能把材质赋给场景中的物体来实现我们想要的效果。
我们选中新建的材质,在inspector面板中就可以看到它的相关参数,比如这个材质所引用的是哪一个shader,以及具体暴露出来的参数是哪些等等。
先重点看一下shader这个参数,它代表的是当前材质球是与哪一个shader绑定关联的。我们可以直接从下拉框中选择,或者在project面板中直接拖动shader到材质球上。
总结一下shader与材质的关系:
一个shader可以与无数个材质关联
一个材质同一时刻只能关联一个shader(因为我们可以通过代码去动态改变材质所关联的shader)
材质可以赋与模型,但是shader不行
材质就像是shader的示例,每一个材质都可以参数不一样呈现不同的效果,但是当shader改变时,关联它的所有材质都会相应的改变。
六、shader框架
我们可以在VS中安装插件(搜索shader即可)会有shader语法提示。打开我们新建的shader,然后我们删除一些只保留框架。
Shader "Unlit/NewUnlitShader"
{
Properties
{
}
SubShader
{
pass
{
}
}
FallBack "DIFFUSE"
}
shader名称路径
第一行Shader关键字后面的内容就是具体的路径和名称,当然我们可以修改它。
Properties
属性,材质球面板中显示的贴图和一些参数什么的都是在此Properties中进行定义设置的,内容必须写在Properties后的{}内。当然,如果不需要的话你也可以将其全部删除,因为它是可选的。
属性的写法有个通用的格式:
[Attribute]_Name ("Display Name",Type) = Default Value
-
Attribute:属性标记,说白了就是Unity内置的几个属性标记关键字,用于对当前这条属性进行一些特殊的处理,此标记不是必选项,可以不添加,同时一条属性上也可以有多条属性标记。
-
_Name:属性的名称,也就是变量名,变量名要使用英文并且加上下划线
-
Display Name:显示在材质面板上的名称,主要起到说明解释的作用。
-
Type:属性的类型,常用的有以下几种:
- Color:颜色
- Int:整型
- Float:浮点数
- Vector:四维数
- 2D:2D纹理,默认值有:white、black、gray和bump
- 3D:3D纹理
- Cube:立方体纹理
Properties { _Color("I am Color", Color) = (1,1,1,1)//依次为RGBA(0-1) [HDR]_Color("I am also Color", Color) = (1,1,0,1) _Int("I am Int", Int) = 1 _Float("I am Float", Float) = 0.5 _Float("I am also Float", Range( 0 , 1)) = 0.5 [PowerSlider(3)]_Float("I am another Float", Range( 0 , 1)) = 0.5 [IntRange]_Float("i am Float", Range( 0 , 1)) = 1 [Toggle]_Float("I am Toggle", Range( 0 , 1)) = 1 [Enum(UnityEngine.Rendering.CullMode)]_Float("我是Float", Float) = 1 _Vector("我是Vector", Vector) = (0,0,0,0) _MainTex("我是2D纹理", 2D) = "white" {} [NoScaleOffset]_MainTex("我是2D纹理", 2D) = "white" {} [Normal]_MainTex("我是2D纹理", 2D) = "white" {} _MainTex("我是3D纹理", 3d) = "" {} _MainCubeTex("我是Cube纹理", CUBE) = "" {} }
SubShader
实际上每一个Shader中都可以包含多个SubShader,不可以没有,必须至少一个,因为当前Shader的核心算法就是在SubShader中来实现。当加载Shader时,Unity将遍历所有SubShader列表,并最终选择用户机器支持的第一个。
我们都知道不同的硬件性能是不一样的,游戏内通常把机器配置分为高中低三种,假如我们做了一个效果很好的Shader,但是呢却只能在高配机上有较好的性能表现,中低端就显的太费性能,这时怎么办呢?SubShader在这时就可以派上用场了,我们可以在这个Shader内做三个SubShader,分别对应于高中低不同的配置。
FallBack
备胎,就是备胎的意思。有时候我们写的Shader难免在一些机器上会出现不支持的问题(最终呈现的就是显示成粉红色),而这个时候我们只要添加了FallBack,并且在后面的双引号内写上了其他Shader的有效路径名称,那么在碰到不支持的硬件时这个Shader就会自动切换成FallBack内的Shader,如果FallBack内的Shader也不支持呢?那就继续从FallBack内的Shader中再找FallBack
就是显示成粉红色),而这个时候我们只要添加了FallBack,并且在后面的双引号内写上了其他Shader的有效路径名称,那么在碰到不支持的硬件时这个Shader就会自动切换成FallBack内的Shader,如果FallBack内的Shader也不支持呢?那就继续从FallBack内的Shader中再找FallBack