学习3D开发技术的时候无可避免的要接触到Shader,那么Shader是个什么概念呢?其实对于开发同事来说还是比较难理解的,一般来说Shader是服务于图形渲染的一类技术,开发人员可以通过其shader语言来自定义显卡渲染页面的算法,从而达到按照自己的想法来渲染出目标效果。
1. Unity Shader
不同的图形API会对应不同的Shader语言,感兴趣的可以去了解下更多这方面的内容。Unity目前开发阶段基于Windows平台开发和运行,一般采用Direct3D或OpenGL,如需要兼容其他平台可在打包的时候选取对应平台如安卓、IOS等,并关注切换后是否存在"粉色"的情况,如出现模型表面为"粉色",一般是Shader出现不兼容错误,需要修复。
在编码之前我们需要先来了解下Unity常见的几种Shader:
- Surface Shaders 表面着色器
Unity对Vertex/Fragment Shader的又一层包装,以使Shader的制作方式更符合人类的思维模式,同时可以以极少的代码来完成不同的光照模型与不同平台下需要考虑的事情。
- Vertex/Fragment Shaders 顶点/片段着色器(重要)
一般的顶点+片段着色器的工作原理如下图所示
a. 顶点处理(顶点着色器)
主要执行坐标转换和逐顶点光照的任务,坐标转换是将顶点坐标从模型空间转换到齐次裁剪空间中,可以使用UnityObjectToClipPos()函数来实现。
b. 图元处理
在裁剪空间中进行裁剪(Clipping)、背面剔除(Back-Face Culling)、屏幕映射(ScreenMapping)等操作。为的是剔除不需要的顶点/面,并做好三维坐标到二维屏幕坐标的转换。
c. 光栅化
是将变换到屏幕空间的图元离散化为片元的过程。
d. 片段处理(片段着色器)
(1). 纹理贴图 - Textures
纹理贴图也称为纹理映射,是将图像信息映射到三角形网格上的技术,以此来增加物体表面的细节,令物体更具有真实感。
(2). 光照计算 - Lighting
光照由直接光和间接光组成,计算光照最常用的就是phong模型了,它是一个经验模型,参数信息都是经验得到的,并没有实际的物理意义,所以利用Phong模型会出现违背物理规则的时候。
漫反射Difuse + 镜面发射Specular + 环境光Ambient = Phong
(3). 混合与测试
输出合并(Output-Merger):在DirectX中,该阶段被称为输出合并阶段,而OpenGL将其称为逐片元操作(Per-Fragment Operations),从称呼中就可以看出,这个阶段主要是对每一个片元进行一些输出合并操作,包括Alpha测试、模板测试、深度测试和混合等,它的主要任务是决定片元的可见性,对通过测试的片元颜色进行混合
Alpha测试:通过片元数据,可以获取该片元的alpha值,如果alpha值小于某个数的话,则直接将该片元丢弃,不进行渲染
模板测试:GPU会首先读取模板缓冲区中该片元位置的模板值,然后将该值和读取到的参考值进行比较,例如小于时舍弃该片元,或者大于等于时舍弃该片元。
深度测试:近处的物体会遮挡远处的物体,这种效果我们可以通过深度测试来模拟实现。
混合:对于半透明物体,我们就需要使用混合操作来让这个物体看起来是透明的
e. 帧缓存
可以简单理解为一个临时画布,GPU渲染完成的信息会存放在帧缓存区,等待使用,上述各种测试也是在帧缓冲区进行的
以下这个图更容易理解:
自己理解简而言之就是:
描点(坐标变换)->
连线(三角面)->
形成格子(光栅化)->
上色(纹理、光照、) ->
后期处理(测试、混合)
2. 几个和Shader相关的概念
(1)网格(Mesh)
网格Mesh是GameObject的3D骨架,它是有顶点(Vertex)定义的一个2D多边形,这些顶点是3D空间中存储为XYZ坐标。网格还包括法线,Shader通过法线让面看起来更光滑。
(2)贴图、纹理(Maps、textures)
纹理(Texture):2D 图像文件,它像一张纸一样环绕GameObject,以设置网格中的颜色、镜面反射或金属度、物理纹理和其他属性。图像文件的数据被组织为通道(RGB/RGBA)。
底图纹理(Base Map Texture):一个常规 RGB 或 RGBA 彩色图像文件,用于定义对象表面的漫反射(即颜色)
平铺纹理(Tiled Texture):平铺纹理的纹理设计为围绕任何网格平铺。每个文件中的地图就像地板上的瓷砖一样简单地重复
UV贴图:对于像球体这样的基元和像我们的块这样的简单形状,纹理贴图在哪些点与对象的网格对齐并不重要。但是在角色模型等详细对象上,纹理贴图与网格的对齐非常重要
Alpha裁剪(Alpha Clip):根据Mesh网格填充纹理,避免大量计算。(也可用于溶解效果、消失/出现效果)
凹凸贴图(Bump Texture):使用凹凸贴图,着色器将表面细节的外观添加到网格中,而无需实际添加多边形。这种技术对性能更好。使用法线贴图和高度贴图(normal maps and height maps)
法线贴图:通过设置顶点位置,使着色器创建表面上的片段(像素)面向不同方向的错觉。
高度贴图:设置网格中每个像素的相对高度
遮挡贴图(Occlusion Map):遮挡贴图为这些遮挡区域添加阴影。
发射贴图(Emission Map):制作发光效果的贴图
(2)反射
环境光遮蔽AO(Occlusion):大部分情况下,AO通过构造一个发射光线的半(hemisphere)来计算。半球上表面布满朝各个方向发射的光线,然后判断这些光线是否与其它的物体相交。如果光线到达背景或者天空,那么就增加这个光线发出点的表面亮度,如果与其他物体相交,则不用。(靠得非常近的物体之间的阴影效果会非常明显)
漫反射(Base map):
-
反照率(Albedo):描述了漫反射的测量。它通常指定为常规颜色,表示为红色、绿色和蓝色的三个值(RGB 值)。RGB 值可以转换为色调、饱和度和亮度(亮度)的值。反照率颜色的亮度对应于漫反射的量,色调和饱和度描述了从表面逸出的光的质量。
-
地图(Map):地图可以是纯色的,也可以用 2D 图像指定以增加表面的变化。
镜面反射(Specular):镜面反射作为材料的一种属性,是一种指定表面看起来像金属的方法。您可以使用两种工作流程来指定材质中的金属外观------Specular、Metallic
平滑度(Smoothness):平滑度,也称为光泽度或光泽度,使镜面反射成为焦点。
3. 如何编写Shader
(1). ShaderLab
ShaderLab 是一种在着色器源文件中使用的类C语言的声明性语言。它使用嵌套大括号语法来描述 Shader 对象。其结构如下:
cs
Shader "Examples/ShaderSyntax"
{
CustomEditor = "ExampleCustomEditor"
Properties
{
// 此处是材质属性声明
}
SubShader
{
// 此处是定义子着色器的其余代码
Pass
{
// 此处是定义通道的代码
}
}
Fallback "ExampleFallbackShader"
}
ShaderLab是一种声明式语言格式来编写Shader,可在代码自由编写Shader支持的所有特性和属性,并设置回退行为。
(2). HLSL
都是着色语言,作为中间语言(Intermediate Language),即交给GPU可以理解的语言。
因为Microsoft和NVIDIA合作,所以Cg/HLSL实际上是同一种语言。而ShaderLab内部可以嵌套Cg/HLSL语言编写着色代码,需要嵌套在命令CGPROGRAM和ENDCG之间。如下:
cs
Pass {
// Pass 的标签和状态设置
CGPROGRAM
// 编译指令
#pargma vertex vert
#pargma fragment frag
// CG代码
ENDCG
// 其他设置
}</pre>