Shader,中文名为着色器。
Shader其实就是专门用来渲染图形的一种技术,通过shader,我们可以自定义显卡渲染画面的算法,使画面达到我们想要的效果。
shader是伴随可编程渲染管线(SRP-Scriptable Render Pipeline)出现的。SRP替代了传统的固定渲染管线,可以在管线中应用3D图形学相关计算,拓展了创作者的发挥空间,是渲染技术的一次革命。
其他缩写:
PBR(基于物理渲染)——一种拟真渲染技术
SRP(可编程渲染管线)
URP (通用渲染管线)
HDRP(高清渲染管线)
DRP(默认内置渲染管线)
PBR:基于物理渲染
以前的渲染是在模仿灯光的外观
现在是在模仿光的实际行为
试图形看起来更真实
1.Build-In Render 内置渲染器(默认)兼容太多,反而不能面面俱到,效果不好
2.Scriptable Render Pipline 可编程渲染管线技术,是Unity提供的新渲染系统,可用C#脚本定制Unity的渲染过程,但自己定制渲染管线对编程要求很高,难度大,所以Unity提供里2个预制的管线,基本上涵盖了我们所有的需求,使用时不需要太底层的技术要求!
2.1 High Definition Render Pipleline 高清管线流程,专注于高端图形渲染,针对高端硬件配置,像PC、XBox 和Playstation,其面向高逼真度的游戏、图形demo和建筑渲染、超写实效果,以及所需的最佳图形效果。同时针对高端图形处理时,它要比内置渲染器要快得多,但如果用来做low poly风格的作品就有点杀鸡用牛刀了,纯属浪费资源和时间;但要想得到完成利用HDRP的完美表现能力,需要大量的贴图,漫反射贴图、高光贴图、金属贴图、平滑贴图、AO贴图、法线贴图、凹凸贴图、高度贴图…So,要做HDRP流程需要非常长的时间和庞大的制作团队,还有充足的预算!建议5人以下的小团队慎入!
2.2 Universal Render Pipleline 通用管线流程(URP前身为Lightweight Render Pipeline — LWRP轻量级渲染管线),专注于性能,URP是选了不会错的渲染管线,它被设计为能够在任何平台上都能提供最好的性能,所以除非有特殊需求只能在HDRP或者SRP解决的,其他都应该选择URP,不管是移动端、主机、PC等,URP都能提供高性能的渲染,目前URP能做的也非常多,它拥有很多HDRP相同的功能,但为了在所有平台达到更好的性能,其做了一定的缩减,但这并不意味着URP做不出漂亮的游戏;
这两种管线流程都利用里Unity新的可编程渲染管线技术,Unity正在把他们变成新的标准,shader graph,visual effect graph这些新功能都是他们的专属,这两个都是创作shader和粒子特效很棒的工具,也支持VR,不过HDRP做逼真风格的VR性能要求非常高!因为Unity实际上把所有东西渲染2次(因为有两个镜头),所以延迟渲染现在只有HDRP支持,不过对URP的支持官方已经在路上。
两者最大的区别是对光照的支持,HDRP提供高级和丰富的光照功能,比如实时全局光照(RealTime GI),能够模拟光线反射、体积光、能模拟光穿过空气中的粒子,还有重头戏的光线跟踪,一种新的光线反射和阴影渲染技术,其原理是跟踪光线在场景中放射的路径,模拟光线在真实世界里与物体交互的效果,这技术对硬件性能非常高,但能产生非常逼真的效果,可以直接用来做电影级别的预渲染作品。
另外一个区别是Shader,HDRP提供一系列高端的shader特效,例如高度、细节和parallax Maps,分别用于纹理的位移、细节和深度模拟,它还支持子面散射,用于模拟光线穿过很薄的物体,比如皮肤和衣物,它提供了高级的shader,像是stacklit,能够让你同时使用多个材质的属性,比如子面散射、彩虹色、各向异性和模糊参数化。
对于后处理效果,两者不相伯仲,HDRP独占的最重要效果,包括AO(环境光遮蔽)、自动曝光(模拟人眼适应不同光线条件的能力)、屏幕空间反射(模拟基于屏幕上可见物体的反射);URP的AO支持已经在做了,但好东西不是都在HDRP,2D光照和阴影就是URP独占的,所以如果你在做2D游戏,就选URP!
另外两个管线都支持一个很Cool的特效—相机堆叠,让你能够同时用多个相机渲染。
原文链接:https://blog.csdn.net/weixin_41622043/article/details/107623694
Shader分为两类 :
顶点Shader(3D图形都是由一个个三角面片组成的,顶点Shader就是计算每个三角面片上的顶点,并为最终像素渲染做准备)。
像素Shader,顾名思义,就是以像素为单位,计算光照、颜色的一系列算法。
几个不同的图形API都有各自的Shader语言
在DirectX中,顶点shader叫做 Vertex Shader ,像素Shader叫做 Pixel Shader;
在OpenGL中,顶点Shader也叫做 Vertex Shader ,但像素Shader叫做 Fragment Shader,也就是我们常说的片断Shader或者片元Shader。
说白了,Shader其实就是一段代码,这段代码的作用是告诉GPU具体怎样去绘制模型的每一个顶点的颜色以及最终每一个像素点的颜色。
渲染操作由GPU完成,由于GPU使用不同于CPU的并行运算结构,需要一种适用于GPU的编程语言,也是shader编写使用的编程语言:
目前主流的Shader编程语言(面向GPU的编程语言)有三种:
基于OpenGL的OpenGL Shading Language,简称GLSL。
基于微软DirectX的High Level Shading Language,简称HLSL。
还有NVIDIA公司的C for Graphic,简称Cg语言。
GLSL与HLSL分别是基于OpenGL和Direct3D的接口,两者不能混用。
而Cg语言是用于图形的C语言,这其实说明了当时设计人员的一个初衷,就是让基于图形硬件的编程变得和C语言编程一样方便,自由。正如C++和 Java的语法是基于C的,Cg语言本身也是基于C语言的。如果您使用过C、C++、Java其中任意一个,那么Cg的语法也是比较容易掌握的。Cg语言极力保留了C语言的大部分语义,力图让开发人员从硬件细节中解脱出来,Cg同时拥有高级语言的好处,如代码的易重用性,可读性高等。
Cg语言是Microsoft和NVIDIA相互协作在标准硬件光照语言的语法和语义上达成了一致,所以,HLSL和Cg其实是同一种语言。
Unity3d里CG如何编译
Windows:Direct3D GPU汇编代码
Mac:OpenGL GPU汇编代码
Flash: flash GPU汇编代码
Ios/Android: Unity会将CG转换成GLSL代码
为什么使用GPU编程渲染
优点: GPU高并行结构 拥有更多逻辑运算单元(ALU)适合用于密集型数据处理,对数据进行独立运算(多个数据并行运算时间和一个数据单独执行时间是一样的)
CPU大部分面积为控制器和寄存器
GPU并行处理能力强于CPU 让它同一时间处理很多顶点数据
缺点:由于是数据独立运算难以实现数据之间相关性的算法(射线和物体的求交运算)
GPU无法实现CPU强大逻辑运算能力(这就是为何GPU如此牛皮却无法取代CPU的原因)
选择:
使用HLSL进行shader编程
曾使用CG,Unity当初是以移动平台为主要目标的。而移动平台的主流图形API是OpenGL,对应的除了GLSL之外,就是CG。但是CG已经停止更新很多年了。随着GPGPU的出现,以及Unity扩大对象平台范围,特别是最近的Compute Shader、Mesh Shader、以及光线跟踪的出现,CG已无法胜任。而微软在家用机市场的地位依然强健,且HLSL的粒度比GLSL细(就是更底层),在互转的时候HLSL转GLSL容易而反之困难,此外竞争对手虚幻引擎也是用的HLSL。
在Unity中编写的Shader最终会根据不同的平台来编绎成不同的着色器语言。
Unity Shader严格来说并不是传统上的Shader,而是Unity自身封装后的一种便于书写的Shader,又称为ShaderLab。
由于有更好的跨平台性,Unity官方的建议是用Cg/HLSL来编写shader。(当然你也可以使用GLSL)
在Unity中有4种Shader:
Surface Shaders 表面着色器
(Unity对Vertex/Fragment Shader的又一层包装。更符合人类的思维模式,同时可以以极少的代码来完成不同的光照模型与不同平台下需要考虑的事情。)
(在进行表面着色器的开发时,我们将直接在Subshader这个层次上写代码,系统将把我们的代码编译成若干个合适的Pass——Subshader与pass概念详见后续‘shader语法框架’相关内容)
Vertex/Fragment Shaders 顶点/片断着色器
(更基础的shader,Vertex/Fragment Shader能实现的效果,Surface Shader不一定能实现,反过来则成立)
Fixed Function Shaders 固定管线着色器
(已经被淘汰,不再推荐)
固定功能的shader,是对硬件能执行的基本的命令进行编写的shader。这类shader功能很有限,但是速度却是最快的。
涉及五方面内容Properties(属性)、Material(材质)、Lighting(光照)、Settexture(设置纹理)以及Pass(通道)。
用于不支持高级着色器特性的旧硬件上,完全采用ShaderLab编写。
unity内建了许多固定功能管线着色器。
shader是伴随可编程渲染管线(SRP-Scriptable Render Pipeline)出现的。
早期GPU遵循的管线叫固定管线(Existing Fixed Function Pipeline),固定管线只能配置“是否开启雾效”、“是否开启光照”、“是否开启深度测试”等一些参数,而不能控制物体与光交互的算法。
固定管线只需要传递不同存储器所需要的数据到参数列表就可以了. 至于它的底层是如何实现的,并不需要关心.因为这一切它已经封装起来了.
OpenGL从3.1版本开始,固定管线从核心模式中去除,因此我们必须使用着色器来完成工作。
初步推断:
[Fixed Function Shaders就是以shader编程的方式去实现从核心模式中去除的固定管线功能? ]
[但unity也有针对固定管线的shader,但功能上是固定的,shader功能上更偏向与选择设置,无法进行自由定义的3D图形学运算。]
Compute Shader
这是Unity3D新增的一种。
Compute Shader技术是微软DirectX 11 API新加入的特性,在Compute Shader的帮助下,程序员可直接将GPU作为并行处理器加以利用,GPU将不仅具有3D渲染能力,也具有其他的运算能力,也就是我们说的GPGPU的概念和物理加速运算。
Compute Shader 使用HLSL语言。
shader种类用Vertex/Fragment Shader,shader语言用HLSL。
Unity2018后的版本中推出了Unity官方自己的可视化Shader工具(Shader Graph),其生成的代码全部是用的Vertex/Fragment Shader。在今后的可编程渲染管线中,更倾向于较全面的Vertex/Fragment Shader。
早期的Unity中的Shader是用Cg写的,但是后面都转到了HLSL。虽然二者有一定一致性,但Cg已经很久不更新了,所以使用HLSL才是正道。
(可编程渲染管线的shader使用HLSL,之前的内置渲染管线shader一般用Cg)
创建编写shader->创建材质并关联shader->关联shader所需的贴图作为一项输入->将材质赋予游戏物体->最后运行以渲染出目标画面。
Shader(着色器)实际上就是一小段程序,它负责将输入的顶点数据以指定的方式和输入的贴图或者颜色等组合起来,然后输出。绘图单元可以依据这个输出来将图像绘制到屏幕上。
输入的贴图或者颜色等,加上对应的Shader,以及对Shader的特定的参数设置,将这些内容(Shader及输入参数)打包存储在一起,得到的就是一个Material(材质),之后,我们便可以将材质赋予三维物体来进行渲染(输出)了。
【材质好比引擎最终使用的“整套解渲染决方案”,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渲染能力,还具有其他的运算能力。
Ray Tracing Shader
射线追踪着色器,用于 GPU 射线追踪的着色器。此着色器应至少包含一个射线生成着色器。
…
Shader Variant Collection
Shader变体收集器,在上面创建的时候,你会发现Shader Variant Collection与以上四个是被隔开的,就是因为这个与它们不一样,它不是制作Shader的模版,而只是对Shader变体进行打包用的容器。
shader是指令代码,需关联材质才能赋予游戏对象以达成效果。
材质——按照关联的shader的规则,处理贴图等输入信息,达到特定的画面效果。
创建材质,创建shader,并关联(一个Shader可以与无数个材质关联)。
unity standard shader使用的十大贴图分类:
贴图
为了满足艺术效果,会使用图形数据:贴图
但是贴图过多也会增大内存的负荷
贴图主要分两类:
1.图像贴图:美术画的,表现物体形状
2.计算贴图:辅助计算,达到特殊效果
常用贴图:
1.固有色贴图(Diffuse)
物体在白色阳光下所呈现的固有颜色
使角色呈现一个合理的饱和度,也能体现模型的一部分质感
2.透明贴图(Alpha)
只由黑白像素构成的灰度图(纯黑全透明,纯白不透明)
通常用它实现磨砂或者半透明效果
只占有一个通道的灰度图,有时也将有色贴图的A通道来存储透明贴图
(如果有其他贴图的闲置通道,也可以合并一起使用)
由于像素精度问题,可能使用透明贴图的时候有锯齿
解决办法:
调整半透明物体的边缘羽化,在材质中调整透明裁切(AlphaCutOff),只有贴图的透明度大于AlphaCutOff时才可以绘制
3.AO贴图(Ambient Occlusion)
环境光吸收贴图,只要用来制作模型的阴影
为了不受外界光照变换的影响,AO贴图采用 “吸收” 光线的计算方法,模拟全局光照效果改善阴影的细节
可以优化墙角阴影浅淡、缝隙阴影发虚等问题,加强明暗对比,增加空间的层次,灯光复杂的场景中比较有用
因为不受光照角度的影响,几乎算是静态效果,为了节省贴图所占用的内存,可以将AO贴图和固有色贴图合并为一张
5.法线贴图(Normal Map)
作为模型表面的扩展,包括了像素点的高度值
可以模拟起伏或不规则的表面
虽然会大幅增加渲染时间,但能够使几千面的模型拥有几万、几十万的视角效果
不会改变模型形状,也可以精确地表现模型表面的光照细节,大大降低了运算消耗
是RGB贴图,红色通道编码法线方向的左右轴,绿色通道编码法线方向的上下轴,蓝色通道编码垂直深度
一般法线贴图是在切空间下,直接把高度图(Height Map)转换成一张法线图(Normal Map)
6.高光贴图(Specular)
控制模型的质感
皮具、布料、金属的高光表现各不相同
相同的材料也会因为磨损程度的不同呈现不同的高光效果
7.自发光贴图(Glow)
标定模型的发光区域
贴图的白色区域渲染为完全发光,黑色区域则保持原样,灰色区域渲染未部分自发光
自发光区域不应受灯光的影响和阴影的影响
8.反射贴图(Reflection)
标注有光泽的物体表面反射的效果
为了使反射贴图获得较好的渲染效果,贴图应具有较高的分辨率
9.渐变贴图(Ramp)
辅助计算光照角度引起的不同变化
通过创建高度自定义的渐变,或使用双向反射贴图,可以模拟复杂的光照效果
10.噪声贴图(Noise)
通过噪声函数计算出的贴图
可以模拟云朵、火焰等自然现象
最常用的就是:
1.固有色贴图
2.法线贴图
3.渐变贴图
REF:https://www.jianshu.com/p/723fa8171469
more:
Matcap贴图:将PBR的高光环境光等信息记录下来,通过采样Matcap图片来替代复杂的计算。
sdf贴图:有向距离场(SDF,Signed Distance Field),
REF:图形学基础|基于SDF的卡通阴影图
REF:卡通渲染之基于SDF生成面部阴影贴图的效果实现(URP)
Unity4.x 阶段,画面渲染使用的是AutoDesk 的Beast 技术,老被拿来和UrealEngine(虚幻)进行对比,得到的结论就是Unity 是“渣画质”。
Unity5.x阶段彻底放弃了Beast 技术,改为使用和UrealEngine 一样的一款第三方渲染引擎Enlighten 来进行画面渲染。了配合这个新的渲染引擎,推出了“标准Shader”。理论上,我们可以使用Unity5 制作出和UrealEngine 差不多的画面效果。
标准Shader 使用的是PBR 渲染,基于现实物理效果的渲染表现形式。
一个模型能不能使用标准Shader 来进行渲染,是在做这个模型的贴图的时候决定的。有没有按照PBR 贴图的制作规范和模式来制作,决定了该模型是否可以使用标准Shader 渲染。
框架主体:
Shader "name"
{
[Properties]
{
}
SubShaders
{
pass
{
}
}
[FallBack] "name"
[CustomEditor] "EditorName"
}
其中[]方括号表示的是可选,意思就是说在Shader中可以没有它,只要不需要的话不加也是没有问题的。
可以拆分成以下几个大部分:
Shader “name” (shader路径名称,也就是我们在Project面板中的资源文件的名称,可与文件名不同)
Properties (属性,材质球面板中显示的贴图和一些参数的设置位置)
SubShaders (可以有不止一个,核心算法在SubShader中实现,可针对高中低等不同硬件配置提供不同的SubShader,加载Shader时,Unity将遍历所有SubShader列表,并最终选择用户机器支持的第一个。)
pass (渲染一次)
FallBack (备胎,在当前机器不支持此shader时替换到对应的其他shader)
CustomEditor (自由定义材质面板的显示结果,它可以改写Properties中定义的显示方式。c#)
Properties可以理解为是材质与Shader的连接通道,我们在材质面板上需要设置的内容都必须通过Properties来实现并暴露。
属性的写法有个通用的格式:
[Attribute]_Name ("Display Name",Type) = Default Value[{options}]
/*
【】Attribute:属性关键字,对属性进行一些特殊的处理,同属性可有多个关键字。
如:
[HDR]亮度可大于1(颜色属性);
[PowerSlider(N)]数值滑动条区间微调(N值:前段精度高0~1,后段精度高1~N)
[Toggle]开关
[Enum]枚举
面板格式属性关键字(同Unity面板):
[Header]标注
[HideInInspector]隐藏
【】_Name:变量名,ID,一定要加上下划线。
(如果此Shader有FallBack的话,一定要将此Shader中的变量名与FallBack中的变量名保持一致)
【】Display Name:显示在材质面板上的名称。
【】Type:属性的类型
如:
Color颜色;
Int整数;
Float浮点数;
Vector四维数;
2D纹理;
3D纹理;
Cube立方体纹理;
Range(min,max)范围和滑动条控制(数值属性)
详细——
Color - 一种颜色,由RGBA(红绿蓝和透明度)四个量来定义;
2D - 一张2的阶数大小(256,512之类)的贴图。这张贴图将在采样后被转为对应基于模型UV的每个像素的颜色,最终被显示出来;
Rect - 一个非2阶数大小的贴图;
Cube - 即Cube map texture(立方体纹理),简单说就是6张有联系的2D贴图的组合,主要用来做反射效果(比如天空盒和动态反射),也会被转换为对应点的采样;
Range(min, max) - 一个介于最小值和最大值之间的浮点数,一般用来当作调整Shader某些特性的参数(比如透明度渲染的截止值可以是从0至1的值等);
Float - 任意一个浮点数;
Vector - 一个四维数;
【】Default Value:属性对应默认值。第一次指定此Shader时,或者在材质面板上执行Reset时会使用默认值。
如:
Color - 以0~1定义的rgba颜色,比如(1,1,1,1);
2D/Rect/Cube - 对于贴图来说,默认值可以为一个代表默认tint颜色的字符串,可以是空字符串或者”white”,”black”,”gray”,”bump”中的一个
Float,Range - 某个指定的浮点数
Vector - 一个4维数,写为 (x,y,z,w)
【】{option},它只对2D,Rect或者Cube贴图有关,在写输入时我们最少要在贴图之后写一对什么都不含的空白的{},当我们需要打开特定选项时可以把其写在这对花括号内。
如果需要同时打开多个选项,可以使用空白分隔。可能的选择有ObjectLinear, EyeLinear, SphereMap, CubeReflect, CubeNormal中的一个,这些都是OpenGL中TexGen的模式。
*/
详细例子:
Shader "Unlit/MyFirstShader"
{
Properties
{
_Color("Color", Color) = (1,1,1,1)
}
SubShader
{
Pass
{
CGPROGRAM //CG代码起点
#pragma vertex vertShaderName //编绎指令:声明定义使用的顶点着色器名称
#pragma fragment fragShaderName //编绎指令:声明定义使用的片断着色器名称
fixed4 _Color; //unityshader编写规则:在CG代码中 再次声明所需使用的Properties里已暴露出的属性(内嵌的CG代码块和外边的unity能直接编译的ShaderLab代码块 无法共用变量)
//返回值 顶点着色器名 输入参数 参数语义标记 返回值语义标记
float4 vertShaderName ( float4 vertex : POSITION ) : SV_POSITION
{
return UnityObjectToClipPos(vertex);//unity内置函数,将顶点从模型坐标转换到裁剪坐标,给片断着色器用
}
//返回值 片断着色器 返回值语义标记
fixed4 fragShaderName () : SV_Target
{
return _Color;
}
ENDCG //CG代码结束
}
}
}
//标识语义:(在 顶点着色器中 可通过标识语义自动获取到 直接存储在模型中的 顶点数据——顶点位置、法线信息、顶点颜色等等)
//POSITION-模型的顶点位置信息(模型中自动获取);
//SV_POSITION-经过顶点着色器变换之后的顶点坐标(顶点着色器返回值中自动获取);
//SV_Target-系统值,下一个阶段输出的颜色值,用于最终显示颜色(片断着色器返回值中自动获取);
运行步骤:
性能:
顶点着色器 每顶点执行一次
片断着色器 每像素执行一次
显示模型要的像素比模型自身顶点多得多
所以从性能的角度来考虑,我们要尽量把计算放在顶点着色器中去执行。其次在片断着色器中也要尽量的简化算法,节省开支。
Cg/HLSL中的几种常见数据类型:
(默认情况下在移动平台纹理会被自动转换成低精度的纹理类型,想要用高精度,需在声明时加后缀:_half或_float)
Properties与Cg/HLSL中的类型对应:
向量分量表示:
在Cg/HLSL中我们可以通过_Color来访问颜色,也可以通过_Color.rgba来访问:
红通道就是_Color.r;
绿通道和透明通道就是_Color.ga
(也可使用.xyzw,意义相同)
图形渲染管线之所以被叫做管线,就是因为它和一根管子的概念很像,我们可以理解为这根管子的末端连接的是我们最终的显示屏幕,管子的起始端连接的是我们的原始素材。可理解为渲染流水线。
渲染管线又称渲染流水线,它是图形图像从数据一步一步形成最终输出的画面所要经历的各种操作过程。指的是对一些原始数据经过一系列的处理变换并最终把这些数据输出到屏幕上的整个过程。
基础概念:
渲染(rendering):计算机根据模型(model)创建图像的过程。
模型(model):根据几何图元创建的物体(object)。
几何图元:包括点、直线和多边形等,它是通过顶点(vertex)指定的。
这根管子从头到尾大致流程可以分为以下三个大阶段(概念上):
(CPU处理:3D应用程序->3d硬件驱动接口DirectX/OpenGL)
就是我们的原始素材准备阶段,包括我们的模型、贴图、相机和光源等,经过这个阶段会将所有素材转换成渲染图元并提交到下一阶段中(几何阶段)。
模型导入3D程序,3D程序调用图形学硬件API接口。
unity通过shader调用进入GPU控制语句块。
——表现为顶点着色器的输入参数
【顶点着色器工作阶段 】
(GPU处理:【GPU前端模块】到【图元装配】)
主要是对上一阶段中传过来的数据进行顶点上的加工处理,包括各种矩阵转换与顶点着色等,最终处理完后会输出屏幕空间的二维顶点坐标、顶点着色等信息,并再提交到下一阶段(光栅化阶段)。
这中间如果在过去T&L流水线的过程当中是由硬件设计好的运算过程,也就是集成的,它不能够进行编程控制。当图形硬件具有了可编程能力之后,我们就可以在这个阶段使用【顶点着色器】进行编写运算逻辑,用于取代过去直接集成在硬件当中的运算。
【Culling & Depth Test】裁剪和深度测试过程(用于选择给哪些数据到下一步)
——表现为顶点着色器的返回数据,即传递给片断着色器的数据
【 片断着色器工作阶段】
纹理采样(Texturing)和雾化处理(Fog)
经过几何阶段处理完后输送到光栅化阶段,从像素对应级别上,对每个像素对应数据进行加工处理,光栅化过后得到的结果是【帧缓存】,其保存了图形硬件为了控制屏幕上所有像素的颜色和强度所需要的全部信息。最终用于显示在屏幕上。
【Alpha Test】去绘制那些半透明的或全透明的物体。
——表现为最终片断着色器的输出
以上介绍的是渲染管线的流程,要把握的主要的过程就是我们可编程能力是两个部分,一个就是几何变换和光照使用的顶点着色器部分,另一个就是关于如何去采样,去计算颜色以及雾化处理等使用的片段着色器部分。
通过结构体(同C语言)传递更多参数
通过自定义函数(同C语言)简化着色器区块的代码
输入数据,要靠语义来标识对应源或目标
应用阶段传入顶点着色器的数据:
struct appdata
{
//数据类型 名称 赋予POSITION语义(表示该数据来源是自动获取的模型顶点数据)
float4 vertex : POSITION; //顶点
float4 tangent : TANGENT; //切线
float3 normal : NORMAL; //法线
float4 texcoord : TEXCOORD0; //UV1
float4 texcoord1 : TEXCOORD1; //UV2
float4 texcoord2 : TEXCOORD2; //UV3
float4 texcoord3 : TEXCOORD3; //UV4
fixed4 color : COLOR; //顶点色
};
顶点着色器到片断着色器的数据:
struct v2f
{
float4 pos:SV_POSITION; //处理后的顶点位置———顶点着色器输出的屏幕裁剪空间下的顶点位置。
float2 uv:TEXCOORD0; //TEXCOORD0、TEXCOORD1、...、TEXCOORD2等等,主要用于高精度数据。
float dp:COLOR0; //COLOR0、COLOR1、...、COLORN等等,主要用于低精度数据。
};
特殊的可输入片断着色器的数据:VFACE
如果渲染表面朝向摄像机,则Face节点输出正值1,如果远离摄像机,则输出负值-1。
片断着色器输出相关语义:
通常情况下,片断着色器最终只需返回一个颜色值即可。:SV_TARGET
在需要输出多个RenderTarget时:SV_TARGET0,SV_TARGET1,SV_TARGET2…
需要自定义像素深度值时:SV_Depth
shader框架的更多内容:在SubShader和Pass中都有Tags可选参数组:
(表面着色器可以被若干的标签(tags)所修饰,而硬件将通过判定这些标签来决定什么时候调用该着色器。)
Tags { "TagName1" = "Value1" "TagName2" = "Value2" }
//名称(TagName)和值(Value)是成对成对出现的,并且全部用字符串表示。
Shader "name"
{
[Properties]
{
}
SubShaders
{
tags //--------------------------------------------
{
}
pass
{
tags //--------------------------------------------?
{
}
}
}
[FallBack] "name"
[CustomEditor] "EditorName"
}
SubShader Tags:
Tag:Queue(渲染队列,指定对象什么时候渲染,每个队列其实都是利用一个整数进行索引的。)
//写法示例:
Tags{ "Queue" = "Geometry" }
//通过值的加减,提供更多层级选择的 自定义渲染队列:
Tags{ "Queue" = "Geometry+1" }
在Unity中,
渲染队列小于2500的对象都被认为是不透明的物体,从前往后绘制,不会重复绘制,无额外消耗。
反之大于2500的,会从后往前绘制,以表现透明效果,会导致像素的多次绘制,额外消耗运算资源。
所以:需要尽可能地把物体的队列设置为不透明物体的渲染队列,而尽量避免重复绘制。
Tag:RenderType
该值为内部的约定,用来区别这个Shader要渲染的对象是属于什么类别的,
改为自定义的名称,也并不会影响到Shader的效果。
但约定名称可以利用Camera.SetReplacementShader来更改最终的渲染效果:
//替代渲染方法:
camera.SetReplacementShader (EffectShader, Tag);
//着色器设置函数:
//tag为空则所有物体Shader都替换成shaderA进行渲染。
//tag为某一类,则先检索出有该tag的对象,并判断该tag是否与EffectShader里的该tag值相等
//,相等则使用shaderA渲染,否则就直接不渲染。
Tag:DisableBatching
利用Shader在模型的顶点本地坐标下做一些位移动画时,如果此模型有批处理时会出现效果不正确的情况,这是因为批处理会将所有几何转换为世界坐标空间,因此“本地坐标空间”将丢失,导致基于本地坐标的效果失效。
Tag:ForceNoShadowCasting
是否强制关闭投射阴影,值可为:
Tag:IgnoreProjector
是否忽略Projector投影器的影响,Projector是Unity中内置的组件,可用于实现贴花等功能。
Tag:CanUseSpriteAtlas
是否可用于精灵打包图集,
意思就是如果某个图片精灵被设置为打包进图集中,那么当此精灵所指定Shader中设置为
“CanUseSpriteAtlas”=“False”
时就会使其无法工作,相应的UI上也会有警告提示。
Tag:PreviewType
Plane
平面预览
Skybox
天空盒预览
材质面板的预览窗口如何显示模型,默认显示的是球体,
此功能仅仅影响的只是材质面板的预览,对Shader本身没有什么影响。
细节等级:Level of Detail
这个数值决定了我们能用什么样的Shader。(判断对应Subshader是否可用)
在Unity的Quality Settings中我们可以设定允许的最大LOD,当设定的LOD小于SubShader所指定的LOD时,这个SubShader将不可用。
Unity内建Shader定义了一组LOD的数值,我们在实现自己的Shader的时候可以将其作为参考来设定自己的LOD数值,这样在之后调整根据设备图形性能来调整画质时可以进行比较精确的控制。
内建Shader定义的LOD数值:
渲染状态:UnityShaderLab层面相关的设置:
subShader层的其他信息:
SubShader
{
Tags {...}
Cull Off //关闭裁剪,因为我们需要Plane的背面面向摄像机时,我们也能看到这个流光效果。
Lighting Off //关闭光照。我们不希望这个魔法阵受到光照的影响。
ZWrite Off //像素深度不写入Z缓冲区。一般半透shader都要加上这句,不然离摄像机更远的被Plane挡住的模型可能会直接显示不出来,半透效果也就失去了本身的意义。
Fog { Mode Off } //关闭雾效,不然如果场景开启了雾效,这张图就会变成下面的样子,整个Plane的轮廓都被显示出来了
Blend One One //为了节省内存,贴图采用了ETC压缩格式,没有alpha通道,所以为了呈现半透的效果,Blend就要用叠加(additive blending)而不是alpha混合(alpha blending)的方式。
Pass {...}
}
/*
Cull 摄像头渲染裁剪:Back/Front/Off 剔除背面(默认)/剔除前面(例如用于显示建筑物内部)/关闭(正反面都渲染)
Lighting 是否受环境光照影响:On/Off
ZWrite On/Off
Fog { Mode Off }
Blend One One
*/
与 Vertex/Fragment Shaders 自行在不同pass里编写shader内容代码不同,在进行 Surface Shaders 的开发时,我们将直接在Subshader这个层次上写代码,系统将把我们的代码编译成若干个合适的Pass。
(按官方可视化Shader工具输出的代码看,更倾向于使用功能更全面的 Vertex/Fragment Shaders编写方案)
(参考第6章节内容,此章节主要例举Surface Shaders相关的 额外及区别内容)
主体框架:
Shader "name"
{
[Properties]
{
}
SubShader
{
}
[FallBack] "name"
[CustomEditor] "EditorName"
}
详细例子:(对比6.2章节的详细例子理解)
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; //与Properties内容关系(详见下方注释)
};
//CG规定了声明为表面着色器的方法(就是我们这里的surf)的参数类型和名字,
//因此我们没有权利决定surf的输入输出参数的类型,只能按照规定写。
//这个规定就是第一个参数是一个Input结构(自定义struct),第二个参数是一个预定义好的inout的SurfaceOutput结构(详见下方注释)。
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"
}
/*
【】编译指令:#pragma surface surf Lambert
它声明了我们要写一个表面Shader,并指定了光照模型。它的写法是这样的
#pragma surface surfaceFunction lightModel [optionalparams]
surface - 声明的是一个表面着色器
surfaceFunction - 着色器代码的方法的名字
lightModel - 使用的光照模型。
【】uv_MainTex
在CG程序中,我们有这样的约定,在一个贴图变量(在我们例子中是_MainTex)之前加上uv两个字母,就代表提取它的uv值(其实就是两个代表贴图上点的二维坐标 )。我们之后就可以在surf程序中直接通过访问uv_MainTex来取得这张贴图当前需要计算的点的坐标值了。
【】SurfaceOutput结构:
struct SurfaceOutput {
half3 Albedo; //像素的颜色
half3 Normal; //像素的法向值
half3 Emission; //像素的发散颜色
half Specular; //像素的镜面高光
half Gloss; //像素的发光强度
half Alpha; //像素的透明度
};
*/
渲染是游戏内资源消耗占比极高的部分,对渲染过程进行优化是非常重要的步骤。
Unity优化当中有一个主要部分就是减少Draw Call的调用,Draw Call就指的是应用程序去调用图形硬件GPU去进行渲染的调度过程。应用程序需要准备很多的数据,包括顶点数据、矩阵、向量等数据,都需要通过应用程序传递给GPU,这样个调度过程CPU必须要去收集数据以后才产生一个API的调用,这个过程的消耗是高昂的,如果反复地启动这个调用,那么就仅仅收集和传递参数这样的过程相当耗时耗力,就会造成了应用程序或游戏运行的瓶颈。因此要尽量减少Draw Call,尽量减少CPU对GPU这样的调用。
REF1:https://zhuanlan.zhihu.com/p/46745694 《零基础入门Unity Shader》
REF2:https://onevcat.com/2013/07/shader-tutorial-1 《猫都能学会的Unity3D Shader入门指南》
REF3:https://www.jianshu.com/p/a41cdff34f6b 《Unity 渲染流程》
REF4:https://blog.csdn.net/hbysywl/article/details/80369425 《Unity渲染优化(UI向)》
REF5:https://www.cnblogs.com/maple-share/p/5395097.html 《Shader和渲染管线》
REF6:https://www.jianshu.com/p/6ad7a414ece5 《Unity渲染管线 入门概览》
REF7:https://www.jianshu.com/p/4b8ddf752328 《OpenGL 下几种固定管线着色器》
REF8: 《》
REF9: 《》
REF10: 《》
REF11: 《》
REF12:https://docs.unity3d.com/Manual/SL-SurfaceShaderExamples.html shader相关unity官方文档