参考文献 https://onevcat.com/2013/07/shader-tutorial-1/
用Unity做游戏开发的小伙伴对此一定不陌生,Shader也称之为“着色器”,实际上就是一小段程序,它负责将输入的Mesh(网格)以指定的方式和输入的贴图或者颜色等进行组合处理,然后输出,同时绘图单元可以依据输出的内容来将图像绘制到屏幕上。
我们在Unity中输入的贴图或者颜色等属性,加上对应写好的Shader,经过Shader的处理过后我们得到了我们想要的纹理效果,将这些内容打包存储在一起,得到的就是一个Material(材质)。之后,我们便可以将材质赋予合适的renderer(渲染器)来进行渲染(绘制)了。
类型 | 说明 |
---|---|
Surface | 通俗来讲Surface Shader(表面着色器)已替我们完成大部分的工作,只需要简单的技巧即可实现很多不错的效果。 |
Fragment | FragmentShader(片段着色器)可以在比较低的层级上进行更复杂更有针对性地开发,因此可以做更多的事情,但比较难写。 |
这里直接先给出一段Unity预制的Standard Surface Shader,下面会针对每一个层级进行解释
Shader "Custom/Diffuse Texture" {
Properties {
_MainTex ("Base (RGB)", 2D) = "white" {}
}
SubShader {
Tags { "RenderType"="Opaque" }
LOD 200
CGPROGRAM
#pragma surface surf Lambert
sampler2D _MainTex;
struct Input {
float2 uv_MainTex;
};
void surf (Input IN, inout SurfaceOutput o) {
half4 c = tex2D (_MainTex, IN.uv_MainTex);
o.Albedo = c.rgb;
o.Alpha = c.a;
}
ENDCG
}
FallBack "Diffuse"
}
从上面的代码中不难看出Shader的结构主要由【Shader】【Properties】【SubShader】【FallBack】构成,值得一提的是,一个【Shader】下可以包含多个【SubShader】,原因后面会讲。
Shader "Custom/Diffuse Texture" {...}
//此处声明了一个叫做"Diffuse Texture"的Shader,同时将其放置在自定义的Custom父级菜单下。
需要注意的是"Custom/Diffuse Texture"并非指代项目Assets下的目录层级,而是Unity根据你此处自定义的层级自动生成的Shader选项菜单,在Unity中你看到的是这样的
Properties {
//此处存放声明的属性
}
Properties是存放所有声明属性的容器,属性的声明有格式上的要求,如下:
_属性名称 ("Unity中可见名称" , 属性类型名称) = 属性初始默认值
就拿例子中的shader来说
_MainTex ("Base (RGB)", 2D) = "white" {}
MainTex = 属性名称 (注意名称前的"_"不可缺失)
Base (RGB) = Unity中可见名称
2D = 属性类型名称
white = 属性初始默认值 (此处即为空白)
Properties {
_MainTex ("Base (RGB)", 2D) = "white" {}
}
紧接着我们来看看几种常见的Type(属性类型)
类型 | 说明 |
---|---|
Color | 一种颜色,由RGBA(红绿蓝和透明度)四个量来定义 |
2D | 一张2的阶数大小(256,512之类)的贴图。这张贴图将在采样后被转为对应基于模型UV的每个像素的颜色,最终被显示出来 |
Rect | 一个非2阶数大小的贴图 |
Cube | 立方体纹理,简单说就是6张有联系的2D贴图的组合,主要用来做反射效果(比如天空盒和动态反射),也会被转换为对应点的采样 |
Range(min, max) | 一个介于最小值和最大值之间的浮点数,一般用来当作调整Shader某些特性的参数(比如透明度渲染的截止值可以是从0至1的值等) |
Float | 任意一个浮点数 |
Vector | 一个四维数 |
Shader的作用是将输入的贴图或者颜色等进行组合处理,然后输出得到我们想要的效果,整个处理的逻辑都在SubShader中。在解释SubShader的逻辑含义之前,首先我们要来说说两个在SubShader中常见的属性Tags和LOD:
之前我们提到一个Shader下可以包含若干个SubShader,那么当我们想让系统在渲染时调用Shader进行处理时,有那么多的SubShader,系统最终会调用哪一个呢?Tags就起到了引导的作用,硬件会判定Tags修饰标签的内容,然后在渲染时调用指定的SubShader进行处理。
我们来看一下例子:
SubShader {
Tags { "RenderType"="Opaque" }
}
此处"RenderType"="Opaque"即表示,当进行渲染非透明物体时,调用该SubShader下的逻辑方法。
LOD是Level Of Detail的缩写,顾名思义,它是一种控制细节级别的技术。LOD的数值越高,画面的渲染质量越高,但是对硬件也有较高的要求。在Unity中,我们可以在Quality面板为项目设置Maximum LOD Level(最大LOD等级)
Maximum LOD Level可以设置7个等级,Maximum LOD Level会与Shader LOD进行大小比较,只有在Maximum LOD Level范围内的Shader才可以被调用,因此使用LOD可以针对不同平台进行渲染质量的调节,Shader LOD具体对照如下
Maximum LOD Level | Shader LOD说明 |
---|---|
0 | 任何LOD值的Shader都可以被调用 |
1 | LOD 100 |
2 | LOD 200 |
3 | LOD 300 |
4 | LOD 400 |
5 | LOD 500 |
6 | LOD 600 |
回到例子
SubShader {
LOD 200
}
此处设置的LOD值为200,参照上述表格,假设当Maximum LOD Level小于等于2时,这个Shader就可以被调用,反之不可。
SubShader {
CGPROGRAM
#pragma surface surf Lambert
sampler2D _MainTex;
struct Input {
float2 uv_MainTex;
};
void surf (Input IN, inout SurfaceOutput o) {
half4 c = tex2D (_MainTex, IN.uv_MainTex);
o.Albedo = c.rgb;
o.Alpha = c.a;
}
ENDCG
}
主体部分由CGPROGRAM开始,于ENDCG结束,这里面包含了整个Shader的核心逻辑,输入端的内容将会在这里进行处理并进行输出,由于主体相对比较复杂先大概扫一眼就好,我会在下一期专门做有关Shader主体部分的笔记。
FallBack "Diffuse"
这个很好理解,当上面所有的SubShader都无法运行时,会直接执行FallBack命令,并使用FallBack所指向的Shader,简单来说就是Shader给自己留的一条“后路”。
这一期的笔记主要整理了UnityShader的一些基础知识,笔者是个刚跨入计算机图形学领域的小白,也还在学习相关的知识,开始写博客的目的也是希望能给自己所学的知识做一个巩固,同时也希望各位行业大大们纠正指出我理解有误的地方,从而提升自己的技术水平。那么,下期笔记见!