1. 什么是Shader
Shader(着色器)实际上就是一小段程序,它负责将输入的Mesh(网格)以指定的方式和输入的贴图或者颜色等组合作用,然后输出。绘图单元可以依据这个输出来将图像绘制到屏幕上。输入的贴图或者颜色等,加上对应的Shader,以及对Shader的特定的参数设置,将这些内容(Shader及输入参数)打包存储在一起,得到的就是一个Material(材质)。之后,我们便可以将材质赋予合适的renderer(渲染器)来进行渲染(输出)了。
所以说Shader只是一段规定好输入(颜色,贴图等)和输出(渲染器能够读懂的点和颜色的对应关系)的程序。而Shader开发者要做的就是根据输入,进行计算变换,产生输出而已。
在Unity中 Renderer - Material - Shader 的关系
- GameObject里有MeshRenderer
- MeshRenderer里有Material列表
- 每个Material里有且只有一个Shader
- Material在编辑器暴露该Shader的可调属性
Shader大体上可以分为两类,简单来说
- 表面着色器(Surface Shader)
- 为你做了大部分的工作,只需要简单的技巧即可实现很多不错的效果
- 顶点着色器(vertex Shader)片段着色器(Fragment Shader)
- 可以做的事情更多,但是也比较难写。使用片段着色器的主要目的是可以在比较低的层级上进行更复杂(或者针对目标设备更高效)的开发。
2. ShaderLab
目前面向GPU的编程有三种高级图像语言:HLSL语言,GLSL语言,Cg语言。
- HLSL语言:High Level Shading Language,由Microsoft公司提供,通过Direct3D图形软件库来编写的着色器语言。
- GLSL语言:OpenGL Shading Language,由OpenGL安委会提供,在OpenGL中进行着色器编程的语言。
- Cg语言:C for Graphics,由NVIDIA公司和Microsoft公司合作提供,有自己的一套关键字和函数库,独立于三维编程接口,在Direct3D和OpenGL上都可工作。
ShaderLab: Unity 自己又封装了一层CG/HLSL/GLSL的接口,但为了实现跨平台,Unity重点支持Cg着色器语言。
3. Shader文件编写
代码结构:
shader "xxx/xxx" //材质选择Shader的路径
{
Properties //属性,输入
{
}
SubShader //子着色器,一个Shader至少有一个SubShader
{
Pass //通道
{
}
}
}
简单的例子:
Shader "Example/Diffuse Texture" {
Properties {
_MainTex ("Texture", 2D) = "white" {}
}
SubShader {
Tags { "RenderType" = "Opaque" }
LOD 200
CGPROGRAM
#pragma surface surf Lambert
struct Input {
float2 uv_MainTex;
};
sampler2D _MainTex;
void surf (Input IN, inout SurfaceOutput o) {
o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb;
}
ENDCG
}
Fallback "Diffuse"
}
①Properties{}
- 属性模块,主要是定义参数,输入,这些属性可以在面板中显示出来
- 格式:_Name(“Display Name”, type) = defaultValue[{options}]
- _Name : 属性的名字,变量名,在之后整个Shader代码中将使用这个名字来获取该属性的内容,在其他脚本中引用这个属性用的也是这个名字
- "Display Name" :在Unity的材质编辑器面板中显示的名称
- type :这个属性的类型
- Color :一种颜色,由RGBA(红绿蓝和透明度)四个量来定义;
- 2D :一张2的阶数大小(256,512之类)的贴图。这张贴图将在采样后被转为对应基于模型UV的每个像素的颜色,最终被显示出来;
- 3D : 3D纹理,只能从脚本创建这种纹理,其中每个轴X,Y和Z对应于红色,绿色和蓝色的颜色值
- Rect : 一个非2阶数大小的贴图;
- Cube : 即Cube map texture(立方体纹理),简单说就是6张有联系的2D贴图的组合,主要用来做反射效果(比如天空盒和动态反射),也会被转换为对应点的采样;
- Range(min,max):滑动条,一个介于最小值和最大值之间的浮点数,一般用来当作调整Shader某些特性的参数(比如透明度渲染的截止值可以是从0~1的值等);
- Float : 任意一个浮点数;
- Vector : 一个四维数;
- defaultValue :定义了这个属性的默认值
- {options}:它只对2D,3D,Rect或者Cube贴图有关,一般写成空白{}就可以,当然{}内可以填写各种选项,选项之间用空格隔开
例子:
Properties
{
//结尾不需要使用分号;
_WaveScale ("Wave scale", Range (0.02,0.15)) = 0.07 // sliders
_ReflDistort ("Reflection distort", Range (0,1.5)) = 0.5
_RefrColor ("Refraction color", Color) = (.34, .85, .92, 1) // color
_ReflectionTex ("Environment Reflection", 2D) = "" {} // textures
_MainTex ("Texture", 2D) = "white" {}
_Cube ("Cubemap", CUBE) = "" {}
_Clip("Clip", float) = 0.6
}
②SubShader{}
Tags
Tags
{
"TagName1" = "Value1"
"TagName2" = "Value2"
}
- 常见的标签名字:
- Queue:渲染队列
- Background:默认值1000,最先渲染,此渲染队列在任何其他队列之前呈现,通常会将它用于真正需要在后台运行的东西。
- Geometry (默认):默认值2000,这用于大多数对象。不透明几何体使用此队列。
- AlphaTest:默认值2450,用来渲染经过Alpha Test的像素,单独为AlphaTest设定一个Queue是出于对效率的考虑
- Transparent:默认值3000,此渲染队列在Geometry之后呈现,并AlphaTest按照从前到后的顺序呈现。任何alpha混合(即不写入深度缓冲区的着色器)都应该放在这里(玻璃,粒子效果)。
- Overlay :默认值4000,此渲染队列用于叠加效果,最后渲染的东西应该放在这里(例如镜头光晕)。
- 可以通过"Queue" = "Geometry+10"来规定渲染的顺序,这样就可以确保场景中的对象被正确渲染了
- RenderType :渲染类型
- Opaque :大多数着色器(法线,自发光,反射,地形着色器)
- Transparent :大多数着色器(法线,自发光,反射,地形着色器)
- TransparentCutout :蒙版透明度着色器(透明剪切,两个植被着色器)。
- Background :Skybox着色器
- Overlay :GUITexture,Halo,Flare着色器
- TreeOpaque :地形中适用于树(植被)的着色器
- TreeTransparentCutout :地形中适用于树(植被)的着色器(含有透明效果的树)
- TreeBillboard :地形中适用于一整片树的着色器
- Grass :地形中适用于草的着色器
- GrassBillboard:地形中适用于一整片草的着色器
- DisableBatching :是否禁用此着色器的批处理
- ForceNoShadowCasting :是否投射阴影
- IgnoreProjector: 是否忽略投影的作用
- CanUseSpriteAtlas :是否将Sprite打包成地图集
- PreviewType :指示材料检查器预览应如何显示材料
例子:
Tags
{
"RenderType"="Opaque"
"Queue"="Geometry+1"
"ForceNoShadowCasting"="True"
}
LOD
- Level of Detail(细节等级)
- 这个数值决定了我们能用什么样的Shader,在Unity的Quality Settings中我们可以设定允许的最大LOD,当设定的LOD小于SubShader所指定的LOD时,这个SubShader将不可用。Unity内建Shader定义了一组LOD的数值,我们在实现自己的Shader的时候可以将其作为参考来设定自己的LOD数值,这样在之后调整根据设备图形性能来调整画质时可以进行比较精确的控制。
- VertexLit及其系列 = 100
- Decal, Reflective VertexLit = 150
- Diffuse = 200
- Diffuse Detail, Reflective Bumped Unlit, Reflective Bumped VertexLit = 250
- Bumped, Specular = 300
- Bumped Specular = 400
- Parallax = 500
- Parallax Specular = 600
Shader主要功能模块
CGPROGRAM
#pragma surface surf Lambert
struct Input
{
float2 uv_MainTex;
};
sampler2D _MainTex;
void surf (Input IN, inout SurfaceOutput o)
{
o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb;
}
ENDCG
- 表面着色器的实现代码需要放在
CGPROGRAM-ENDCG
代码块中而不是Pass结构中,他会自己编译到各个Pass中 - CGPROGRAM - ENDCG :表示在这两个关键字之中的程序是CG类型的程序段
- HLSLPROGRAM - ENDHLSL:表示在这两个关键字之中的程序是HLSL类型的程序段
- 编译指令:
- 表面着色器 :格式:#pragma surface surfaceFunction lightModel [optionalparams]
- surfaceFunction : 函数名,往下代码中可以找到这个函数的定义
- lightModel:灯光类型
- Standard :照明模型使用SurfaceOutputStandard输出结构,并匹配Unity中的标准(金属工作流)着色器。
- StandardSpecular :照明模型使用SurfaceOutputStandardSpecular输出结构,并匹配Unity中的标准(镜面设置)着色器。
- Lambert(漫射)和BlinnPhong(反射,高光) :照明模型不是基于物理的,但使用它们的着色器可以更快地在低端硬件上渲染。
#pragma surface surf Lambert
- 顶点着色器或片段着色器 :
- #pragma vertex functionName : 将函数名称编译为顶点着色器
- #pragma fragment functionName : 将函数名称编译为片段着色
#pragma vertex vert
#pragma fragment frag
当然这只是最简单的编译指令,当然还有一些其他的编译指令,这里就不写了,但是还是要去了解的
https://docs.unity3d.com/Manual/SL-SurfaceShaders.htmlhttps://docs.unity3d.com/Manual/SL-ShaderPrograms.html
sampler2D _MainTex;
- 在 CGPROGRAM 内还要再一次进行声明_MainTex,这是将原先在 Properties 内声明的变量进行链接并转化成 CG 语言能够识别,的变量类型。
struct Input
{
float2 uv_MainTex;
};
- 这是表面着色器的固定要求,一个名为Input的结构体,这个结构体也就是需要进行处理的输入数据,其中例子里
float2
表示有两个float类型的值。 - 既然有输入,那么就一定有输出,标准的表面着色器输出结构:
struct SurfaceOutput {
half3 Albedo; //像素的颜色
half3 Normal; //像素的法向值
half3 Emission; //像素的发散颜色
half Specular; //像素的镜面高光
half Gloss; //像素的发光强度,光泽度
half Alpha; //像素的透明度
};
所以这个例子中表面着色最主要的功能函数:
void surf (Input IN, inout SurfaceOutput o)
{
o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb;
}
- surf : 函数名,是当时
#pragma surface surf Lambert
指定的函数 - 输入 :输入类型为
Input
类型,即自己编写的Input结构体 - 输出 :添加一个关键字
inout
表示输出,输出的类型为SurfaceOutput
类型 - 这里用到了一个
tex2D
函数,这是CG程序中用来在一张贴图中对一个点进行采样的方法,返回一个float4类型的值。这里对_MainTex
在输入点IN.uv_MainTex
上进行了采样,并将该点颜色的rgb值赋予了输出的像素颜色
好啦,这就是对着色器的简单入门学习,主要学的是表面着色器,下面是我参考的文章,都很有用~
参考:https://onevcat.com/2013/07/shader-tutorial-1/https://docs.unity3d.com/Manual/SL-SurfaceShaders.htmlhttps://docs.unity3d.com/Manual/SL-Shader.html