渲染流程
此处的渲染流程只是一个概念流水线。大概分为应用阶段、几何阶段、光栅化阶段。
应用阶段
主要输出渲染所需的几何信息,包括点、线、三角面等,传递给下一阶段使用;这一阶段主要CPU处理,该阶段产生的产物就是渲染图元。
主要工作
- 把数据加载到显存中:硬盘(HDD)->系统内存(RAM)->显存(VRAM)
- 设置渲染状态:定义场景网格如何被渲染
- 调用Draw Call命令:CPU命令GPU进行渲染,CPU给GPU传递需要被渲染的图元列表
几何阶段
负责和每个渲染图元打交道,主要任务顶点坐标转变到屏幕空间中,再交给光栅化处理,这阶段会输出二维顶点坐标及对应的深度值等,这阶段主要在GPU。
主要工作
- 顶点着色器:主要完成坐标变换和逐顶点光照,必须完成把顶点坐标从模型空间转换为齐次裁剪空间,接着通常再由设备做透视除法,最终得到归一化的设备坐标(NDC)。
OpenGL和unity:NDC坐标的z分量范围[-1,1]——DirectX:NDC坐标的z分量范围[0,1]
- 曲面细分着色器:细分图元
- 几何着色器:执行逐图元着色操作
- 裁剪:将不在摄像机的顶点裁剪掉,并剔除某些三角图元的面片
- 屏幕映射:负责把每个图元的坐标转换到屏幕坐标系。此处输入的坐标仍是三维坐标系下的坐标(范围在单位立方体内),屏幕映射是把每个图元的x,y坐标转换到二维坐标系下的屏幕坐标系。
窗口坐标系=屏幕坐标系+z坐标
OpenGL把屏幕的左下角当成最小的窗口坐标值;DirectX把屏幕的左上角当成最小的窗口坐标值
光栅化阶段
产生屏幕上的像素,渲染最终图像.这阶段主要在GPU。
主要工作
- 三角形设置:根据上一阶段输出的三角网格顶点得到每条边的像素坐标,进而得到三角网格表示数据的过程。
- 三角形遍历:找到哪些像素被三角网格覆盖的过程,也叫扫描变换。检查每个像素是否被一个三角网格所覆盖,如果覆盖的话,就会生成一个片元。
片元不是真正意义上的像素,而是包含了很多状态的集合,这些状态用于计算每个像素的最终颜色
- 片元着色器:逐片元的着色操作,它仅可以影响单个片元,但是可以访问到导数信息。
- 逐片元操作:修改颜色、深度缓冲、混合等。前面的光栅化阶段实际上并不会影响屏幕上每个像素颜色,而是产生一系列数据信息,存在每个片元中,逐片元操作才会真正影响每个像素。
这个阶段主要完成两个任务:
4.1. 决定每个片元的可见性:涉及很多测试工作,例如深度测试和模板测试。
4.2. 如果通过所有测试,就需要把这个片元的颜色值和已经存储在颜色缓冲区的颜色进行合并或者说混合。
4.3. 简化后的逐片元操作:只有通过了所有的测试后,新生成的片元才能和颜色缓冲区中已经存在的像素颜色进行混合,最后再写入颜色缓冲区中。
测试顺序并不一定唯一,unity中技术Early-Z技术:深度测试在片元着色器之前
为了避免我们看到正在进行光栅化的图元,GPU使用双重缓冲
着色器入门
unity shader是一种抽象,我们和这个抽象打交道的途径——ShaderLab
UnityShader基础结构
此处主要以顶点片元着色器为例
Shader "Unlit/NewUnlitShader"
{
Properties
{//外部可以访问
//声明一个Color类型颜色
_Color ("颜色", Color) = (1,1,1,1)
}
SubShader
{
//配置SubShader标签,设置分类
Tags { "RenderType"="Opaque" }
//配置深度写入与裁切
Zwrite Off Cull Off
//配置混合模式
Blend One Zero
Pass
{
CGPROGRAM
//编译指令:告诉Unity哪个函数包含了顶点着色器的代码
#pragma vertex vert
//编译指令:告诉Unity哪个函数包含了片元着色器的代码
#pragma fragment frag
//引入写好的内置文件
#include "UnityCG.cginc"
//在CG代码中,我们需要定义一个与属性名称和类型都匹配的变量
float4 _Color;
//定义顶点着色器输入
struct appdata
{//把数据从应用(application)阶段传递到顶点着色器(vertex shader)中
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
/*
*以上语义内容均来自MeshRender组件提供
*在每帧调用DrawCall的时候,MR组件会把它负责渲染的模型数据发送给UnitySHader
*一个模型包含一组三角面片,每个三角面由三个顶点组成,定点包含各种信息
*/
};
//定义顶点着色器输出
struct v2f
{
float4 vertex : SV_POSITION;
float3 color : COLOR0;
};
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.color=fixed3(0.5,0.5,0.5);
return o;
}
fixed4 frag (v2f i) : SV_Target
{
fixed3 c = i.color;
c*=_Color.rgb;
return fixed4(c,1);
}
ENDCG
}
}
}
Properties
shaderLab中Properties语句块定义了着色器所需的各种属性
- 1.1. 除此之外,我们还需要为每个属性指定一个默认值
Properties 语义块支持的属性类型 | ||
---|---|---|
属性类型 | 默认值的定义语法 | 例子 |
Int | number | _Int("Int",Int)=2 |
Float | number | _Float("Float",Float)=0.2 |
Range(min,max) | number | _Range("Range",Range(0.0,2.0))=1.2 |
Color | (number,number,number,number) | _Color("Color",Color)=(1,1,1,1) |
Vector | (number,number,number,number) | _Vector("Vector",Vector)=(1,2,5,3) |
2D | "defaulttexture" {} | _2D("2D",2D)="" {} |
Cube | "defaulttexture" {} | _Cube("Cube",Cube)="white" {} |
3D | "defaulttexture" {} | _3D("3D",3D)="black" {} |
SHaderLab属性类型和CG变量类型的匹配关系 | |
---|---|
ShaderLab属性类型 | CG变量类型 |
Color,Vector | float4,half4,fixed4 |
Range,Float | float,half,fixed |
2D | sampler2D |
Cube | samplerCube |
3D | sampler3D |
- 有时,在CG变量前会出现uniform关键字,它是一种修饰词,它仅用于提供一些关于该变量的初始值是如何指定和存储的相关信息。在unity shader中,uniform关键字可以忽略。
- float:高精度浮点值,通常是32位。
- half:中精度浮点值。通常是16位,范围是-60000至+60000,它适合存储UV坐标,颜色值等。
- fixed:低精度浮点值。通常是11位,范围是-2.0至+2.0,精度为1/256。这是三者中最小的一个,可以用于光照计算、颜色等。
- sampler*:分为6类 :sampler、sampler1D、sampler2D、sampler3D、samplerCUBE、samplerRECT。DirectX profiles不支持samplerRECT类型。
- 1.2. 为了在Shader中可以访问到这些属性,我们需要在CG代码片中定义和这些属性类型匹配的变量;也可在CG代码片中定义这些变量,此时,我们可以通过脚本向Shader传递这些属性。所以Properties只是为了让这些属性在面板可见。
SubShader
每个UnityShader文件至少有一个SubShader,unity会选择第一个可以在目标平台运行的SubShader,都不支持就运行Fallback。
- 2.1. Tag标签结构和SubShader的标签块支持的标签类型(注意和pass支持标签块类型区分)
SubShader的标签类型 | ||
---|---|---|
标签类型 | 说明 | 例子 |
Queue | 控制渲染顺序,指定该物体属于哪一个渲染队列,通过这种方式可以保证所有透明物体可以在所有不透明物体后面被渲染,我们也可以自定义使用的渲染队列来控制物体渲染顺序。Unity在内部使用一系列整数索引来表示每个渲染队列,且索引号越小表示越早被渲染。 | Tags{"Queue"="Geometry"} |
RenderType | 对着色器进行分类,例如这是一个不透明的着色器,或是一个透明的着色器等,这可以被用于着色器替换(Shader Replacement)功能。 | Tags{"RenderType"="Opaque"} |
DisableBatching | 一些SubShader在使用Unity的批处理功能时会出现问题,例如使用了模型空间下的坐标进行顶点动画,这时可以通过该标签来直接指明是否对该SubShader使用批处理。 | Tags{"DisableBatching"="True"} |
ForceNoShadowCasting | 控制使用该SubShader的物体是否会投射阴影 | Tags{"ForceNoShadowCasting"="True"} |
IgnoreProjector | 如果该标签值为“true”,那么使用该SubShader的物体将不会受Projector的影响,通常用于半透明物体。 | Tags{"IgnoreProjector"="True"} |
CanUseSpriteAtlas | 当该SubShader是用于精灵(Sprites)时,将该标签设为"Flase"。 | Tags{"CanUseSpriteAtlas"="False"} |
PreviewType | 指明材质面板将如何预览该材质。默认情况下,材质将显示为一个球形,我们可以通过把该标签的值设为"Plane"来改变预览类型。 | Tags{"PreviewType"="Plane"} |
- 2.2. Unity提前定义的5个渲染队列
名称 | 队列索引 | 描述 |
Background | 1000 | 这个渲染队列会在任何其他队列之前被渲染,我们通常使用该队列来渲染那些需要绘制在背景上的物体 |
Geometry | 2000 | 默认的渲染队列,大多数物体都使用这个队列,不透明物体使用这个队列 |
AlphaTest | 2450 | 需要透明度测试的物体使用这个队列,在Unity5中它从Geometry队列中被单独分出来了,这是因为在所有不透明物体渲染之后再渲染它们会更加高效。 |
Transparent | 3000 | 这个队列中的物体会在所有Geometry和AlphaTest物体渲染后,再按从后往前的顺序进行渲染,任何使用了透明度混合(例如关闭了深度写入的Shader)的物体都应该使用这个队列 |
Overlay | 4000 | 该队列用于实现一些叠加效果。任何需要在最后渲染的物体都应该使用该队列 |
- 2.3. ShaderLab中常见的渲染状态设置选项
常见的渲染状态设置选项 | ||
---|---|---|
状态名称 | 设置指令 | 解释 |
Cull | Cull Back | Front | Off | 设置剔除模式:剔除背面/正面/关闭剔除 |
ZTest | ZTest Less Greater | LEqual | GEqual | Equal | NotEqual | Always | 设置深度测试时使用的函数 |
ZWrite | ZWrite On | Off | 开启/关闭深度写入 |
Blend | Blend One Zero | 开启并设置混合模式 |
混合(Blend)
- 3.1. 混合是一个逐片元的操作,它是高度可配置但是不可编程的
- 3.2. 混合等式:两个操作数:源颜色S和目标颜色D,想得到输出颜色O,就必须使用一个等式计算,我们把这个等式称为混合等式。
混合等式分为两个:一个用于混合RGB通道的,一个用于混合A通道的。默认情况下,混合使用的操作都是加操作,我们只需要设置一下混合因子即可。混合因子就是和源颜色或者目标颜色相乘的。 - 3.3. ShaderLab中的混合因子
参数 | 描述 |
One | 因子为1 |
Zero | 因子为0 |
SrcColor | 因子为源颜色值。当用于混合RGB的混合等式时,使用SrcColor的RGB分量作为混合因子;当用于A的混合等式时,使用SrcColor的A分量作为混合因子 |
SrcAlpha | 因子为源颜色的透明值(A通道) |
DstColor | 因子为目标颜色值。当用于混合RGB的混合等式时,使用DstColor的RGB分量作为混合因子;当用于A的混合等式时,使用DstColor的A分量作为混合因子 |
DstAlpha | 因子为目标颜色的透明度值(A通道) |
OneMinusSrcColor | 因子为(1-源颜色) |
OneMinusSrcAlpha | 因子为(1-源颜色的透明度值) |
OneMinusDstColor | 因子为(1-目标颜色) |
OneMinusDstAlpha | 因子为(1-目标颜色的透明度值) |
- 3.4. 混合操作:当我们把源颜色和目标颜色与它们对应的混合因子相乘后,对他们的结果如何操作呢?可以不使用加法吗?可以的。
操作 | 描述 |
Add | 将混合后的源颜色和目标颜色相加,默认的混合操作 |
Sub | 用混合后的源颜色减去混合后的目标颜色 |
RevSub | 用混合后的目标颜色减去混合后的源颜色 |
Min | 使用源颜色和目标颜色中较小的值,是逐分量比较的 |
Max | 使用源颜色和目标颜色中较大的值,是逐分量比较的 |
-
3.5. 通过混合操作和混合因子命令的组合,我们可以得到Photoshop混合模式中的混合效果(可以在网上单独查找,应该会有很多)
-
Blend SrcAlpha OneMinusSrcAlpha // Alphablending alpha混合
-
Blend One One // Additive 相加混合
-
Blend One OneMinusDstColor // Soft Additive柔和相加混合
-
Blend DstColor Zero // 正片叠底Multiplicative 相乘混合
-
BlendDstColor SrcColor // 2x Multiplicative 2倍相乘混合
Pass语义块
- 4.1. 可以在Pass中定义该Pass的名称:Name “MyPassName”,然后可以通过ShaderLab的UsePass命令来直接使用其他Unity Shader中的Pass
Use Pass “MyShader/MYPASSNAME”//必须使用大写形式
- 4.2. Pass可以设置渲染状态
- 4.3. Pass可以设置标签(与UnityShader中设置的标签不同)
Pass的标签类型 | ||
标签类型 | 说明 | 例子 |
LightMode | 定义该Pass在Unity的渲染流水线中的角色 | Tags{"LightMode"="ForwardBase"} |
RequireOptions | 用于指定当满足某些条件时才渲染该Pass,它的值是一个由空格分隔的字符串。目前,Unity 支持的选项有:SoftVegetation。在后面的版本中,可能会增加更多选项。 | Tags{"RequireOptions"="SoftVegetation"} |
- 4.4. 特殊语义:在UnityShader编写的过程中经常遇到一些特殊的大写单词,他们在里面有自己的特殊意思,下面介绍其中一部分
Fallback
语义:Fallback "name"或者Fallback Off
平台差异:
Ⅰ、在OpenGL中,(0,0)点对应了屏幕的左下角,在DirectX中,(0,0)点对应了屏幕左上角
Ⅱ、我们不仅可以将渲染结果渲染到屏幕上,还可以输出到不同的渲染目标上,但当我们把屏幕图像渲染到一张渲染纹理中时,如果不采取任何措施,就会因为①的原因造成纹理翻转的情况,不过,Unity在背后为我们处理了这种反转问题,不过当我们在Unity中开启了抗锯齿,此时如果基于DirectX平台使用渲染纹理技术,同时处理多张渲染图象的时候,我们就需要自己处理反转问题。
UnityShader透明方式
前言
- 深度缓冲:对于不透明物体,不需要考虑它们的渲染顺序,因为强大的深度缓冲(z-buffer),在实时渲染中,深度缓冲是用于解决可见性问题的,基本思想:根据深度缓冲中的值来判断该片远距离摄像机的距离。
- 透明度:当开启透明混合后,当一个物体被渲染到屏幕上时,每个片元除了颜色值和深度值之外,还有另一个属性——透明度。
实现方法
透明度测试
采用一种“霸道极端”的机制,只要一个片元的透明度不满足条件,那么它对应的片元就会被舍弃,被舍弃的片元不再进行任何处理,也不会对颜色缓冲产生任何影响;否则就会按照普通的不透明物体的处理方法来处理它。
透明度混合
这种方法可以得到真正的半透明效果,它会使用当前片元的透明度作为混合因子,与已经存在的颜色缓冲中的颜色值进行混合,得到新的颜色。但是透明度混合仅需要关闭深度写入,没有关闭深度测试,这使得我们要小心物体的渲染顺序。对于透明度混合,深度缓冲是只读的。(如果不关闭深度写入,那么就会发生前面的透明物体遮住了后面的不透明物体)
2.1. 渲染顺序
因为有时关闭深度写入,那么就导致渲染顺序将变得非常重要。所以渲染引擎一般会先对物体进行排序,再渲染。常用方法:
2.1.1. 先渲染所有不透明物体,并开启它们的深度测试和深度写入。
2.1.2. 把半透明物体按他们距离摄像机的远近进行排序,然后按照从后往前的顺序渲染这些半透明物体,并开启它们的深度测试,但关闭深度写入。
Unity纹理
纹理最初的目的就是使用一张图片来控制模型的外观。使用纹理映射技术,我们可以把一张图黏在模型表面,逐纹素(纹素的名字是为了和像素进行区分)地控制模型的颜色。
UnityShader基础纹理
- 通常在建模软件中利用纹理展开技术把纹理映射坐标存储在每个顶点上,纹理映射坐标定义了该顶点在纹理中对应的2D坐标。
- 纹理映射坐标通常使用一个二维变量(u,v)表示,其中u是横向坐标,v是纵向坐标。通常也称为UV坐标。
属性面板
-
Wrap Mode
1.1. Repeat:这种模式下,如果纹理坐标超过了1,那么它的整数部分将会被舍弃,而直接使用小数部分采样,这样的结果是纹理将会不断重复。
1.2. Clamp:这种模式下,如果纹理坐标大于1,那么将截取1,如果小于0,就截取到0。 -
Filter Mode
它决定了当纹理由于变换而产生拉伸时将会采用哪种滤波模式。有三种模式:它们得到的图片滤波效果依次提升,但需要耗费的性能也依次增大。
· Point:使用了最近邻滤波,图像会看起来有种像素风格,
· Bilinear:使用线性滤波,图像看起来被模糊了。
· Trilinear:和Bilinear滤波几乎一样,只是在多级渐远纹理之间进行混合了。 -
Max Texture Size:如果纹理大小超过了这个值,Unity将会把该纹理缩放到这个最大分辨率,理想情况下,纹理可以不是正方形,但是长宽的大小应该是2的幂,如果使用非2的幂大小(Non Power of Two ,NPOT)的纹理,那么会占用更多内存空间,GPU读取速度也会下降。甚至有些平台不支持。
-
多级渐远纹理技术:将原纹理提前用滤波处理得到很多更小的图像,形成一个图像金字塔,每一层都是对上一层图像降采样的结果。这是一种典型的用空间换取时间的方法。在Unity中,勾选Generate Mip Maps即开启多级渐远纹理技术。
应用
凹凸映射
就是使用一张纹理来修改模型表面的法线,以便为模型提供更多的细节。
实现方法
1.1. 高度映射:使用一张高度纹理来模拟表面位移,然后得到一个修改后的法线值。
高度图中存储着强度值,它用来表示模型表面局部的海拔高度。颜色越浅表明该位置的表面越向外凸起,而颜色越深表明该位置越向里凹。
高度图通常会和法线映射一起使用,用于给出表面凹凸的额外信息。
1.2. 法线映射:使用一张法线纹理来直接存储表面法线。通常使用法线映射
法线纹理中存储的就是表面的法线方向。由于法线方向的分量范围在[-1,1],而像素的分量
范围为[0,1],因此我们需要做一个映射,但是我们在Shader中对法线纹理进行纹理采样
后,还需要对结果进行一次反映射的过程,以得到原先的法线方向:
1.3. 法线纹理的坐标空间
1.3.1. 模型空间的法线纹理
优点:实现简单,更加直观。我们甚至都不需要模型原始的法线和切线等信息。
缺点:在纹理坐标的缝合处和尖锐的边角部分,可见的突变较少,既可以提供平滑的边 界。切线空间下的法线纹理中的法线信息是依靠纹理坐标的方向得到的结果,可能会在边缘处或尖锐的部分造成更多可见的缝合迹象。
1.3.2. 切线空间的法线纹理
优点:自由度高,模型空间的法线纹理是绝对法线信息,而切线空间下的法线纹理记录的是相对法线信息。可进行UV动画;可以重用法线纹理;可压缩。
1.4. 在利用法线纹理做凹凸映射的时候,实际上他只有两个通道是真正必不可少的,第三个通道可以通过另外两个推导出来,所以使用DXT5nm的压缩方法可以减少纹理的占用内存。
但是再利用高度图生成法线纹理的时候,除了需要将纹理类型设置为Normal map外,还需要勾选Create from Grayscale,这样我们就可以把它和切线空间下的法线纹理同等对待了。
勾选Create from Grayscale后:
Bumpiness:控制凹凸程度
Filtering:Smooth:使生成后的法线纹理比较平滑
Shard:它会使用Sobel滤波来生成法线。
渐变纹理
在很多卡通风格的渲染中会用到一种冷到暖色调的着色技术,这种技术会用到渐变纹理。
遮罩纹理
- 遮罩允许我们保护某些区域,使它们免于某些修改。
- 使用遮罩纹理的流程一般是:通过采样得到遮罩纹理的纹素值,然后使用其中的某个(或者某几个)通道的值(例如:texel.r)来与某种表面属性进行相乘,这样,当该通道的值为0时,可以保护表面不受该属性的影响。
UnityShader立方体纹理
- 定义:是环境映射的一种实现方法。环境映射可以模拟物体周围的环境,而使用了环境映射的物体可以看起来像镀了层金属一样反射周围的环境。
- 采样方法:对立方体纹理采样我们需要提供一个三维的纹理坐标,这个三维纹理坐标表示了我们在世界空间下的一个3D方向。这个方向矢量从立方体的中心出发,当它向外延伸时,就会和立方体的6个纹理之一发生相交,而采样得到的结果就是由该交点计算而来的。
应用
天空盒子
-
对于Unity自带的Skybox材质:
1.1. 6张图片位置要摆正,同时为了让天空盒子正常渲染,我们需要把这6张纹理的WrapMode设置为Clamp,以防止在接缝处出现不匹配的现象。
1.2. 材质界面除了6张图还有3个属性:
Tint Color:用于控制该材质的整体颜色;
Exposure:用于调整天空盒子的亮度;
Rotation:用于调整天空盒子沿+y轴方向的旋转角度。 -
如果我们想某些摄像机可以使用不同的天空盒子,可以通过向该摄像机添加Skybox组件,覆盖掉之前的设置。
-
在Unity中,天空盒子是在所有不透明物体之后渲染的,而其背后使用的网格是一个立方体或一个细分后的球体。
菲涅尔反射
根据视角方向控制反射程度。
- Schlick菲涅尔近似等式:
其中,是一个反射系数,用于控制菲涅尔反射的强度,v是视角方向,n是表面法线。 - Empricial菲涅尔近似等式:
其中,bias、scale和power是控制项。
UnityShader渲染纹理
定义:unity为渲染目标纹理定义了一种专门的纹理类型——渲染纹理。在unity中使用渲染纹理通常有两种方式:
- 在Project目录下创建一个渲染纹理,然后把某个摄像机的渲染目标设置成该渲染纹理,这样该摄像机的渲染结果就会实时更新到渲染纹理中,而不会显示在屏幕上。
- 在屏幕后处理时使用GrabPass命令或OnRenderImage函数来获取当前屏幕图像,unity会把这个屏幕图像放到一张和屏幕分辨率等同的渲染纹理中,下面我们可以在自定义的Pass中把它们当成普通的纹理来处理,从而实现各种屏幕特效。
应用
镜子效果
- 实现原理:使用一个渲染纹理作为输入属性,并把该渲染纹理在水平方向上翻转后直接显示到物体上即可。
玻璃效果
- 实现原理:一种特殊的Pass:就是GrabPass。当我们在shader中定义了一个GrabPass后,unity会把当前屏幕的图像绘制在一张纹理中,以便我们在后续的pass中访问它。
- 与使用简单的透明度混合不同,使用GrabPass可以让我们对该物体后面的图像进行更复杂的处理,而不是简单的原屏幕颜色混合。
- 实现流程:首先使用一张法线纹理修改模型的法线信息,然后通过一个Cubemap来模拟玻璃的反射,而在模拟折射时,则使用了GrabPass获取玻璃后面的屏幕图像,并使用切线空间下的法线对屏幕纹理坐标偏移后,再对屏幕图像进行采样来模拟近似的折射效果。
UnityShder程序纹理
定义:指的是那些由计算机生成的图像,我们通常使用一些特定的算法来创建个性化图案或者非常真实的自然元素,例如石头、木头等。
UnityShader光照
我们直接讲解标准光照模型,它的基本理念就是只关心直接光照,也就是那些直接从光源发射出来照射到物体表面后,经过物体表面的一次反射直接进入摄像机的光线。它的基本方法就是把进入到摄像机的光线分为4个部分,分别是自发光、高光反射、漫反射、环境光,每个部分使用一种方法来计算它的贡献度。
环境光
定义:这部分用于描述其他所有的间接光照。
- 环境光unity在Window->Lighting中已经设置好,我们在shader中只需要通过UNITY_LIGHTMODEL_AMBIENT就可以获得环境光的颜色和强度信息。
- 在标准光照模型中,我们使用环境光来近似模拟间接光照。
- cambient=gambient
gambient:表示全局环境光
自发光
定义:这部分用于描述当给定一个方向时,一个表面本身会向该方向发射多少辐射量。
- 自发光表面往往不会照亮周围的表面,但是在Unity5引入全新的全局光照系统则可以模拟自发光物体对周围物体的影响。
- Unity中计算自发光,只需要在片元着色器输出最后颜色前,把材质的自发光颜色添加到输出颜色上即可。
- cemissive=memissive
memissive:该材质的自发光颜色
漫反射
定义:这部分用于描述当光线从光源照射到模型表面时,该表面会向每个方向散射多少辐射量。
- 漫反射光照符合兰伯特定律:反射光线的强度与表面法线和光源方向之间夹角的余弦值成正比。因此,漫反射部分的计算如下:
cdiffuse=(clight · mdiffuse)max(0,n · I)
其中,n是表面法线,I是指向光源的单位矢量,mdiffuse是材质的漫反射颜色,clight是光源颜色。需要注意的是,我们需要防止法线和光源方向点乘的结果为负值,为此,我们使用取最大值的函数来将其截取到0,这可以防止物体被从后面来的光源照亮。
高光反射
定义:这部分用于描述当光线从光源照射到模型表面时,该表面会在完全镜面反射方向散射多少辐射量。