解Unity 5 全局光照系统Enlighten问题
Unity 5正式版面世已经一段时间,许多开发者都已经在使用Unity 5进行开发。大家在使用Unity 5时或多或少都会碰到一些问题,今天我们Unity的官方技术工程师,柳振东将在本文中针对Unity 5中全新的Enlighten全局光照系统的一些问题,以及相应的解答与大家进行分享。
为什么烘焙的point灯光会有一圈圈的色阶变化?
当然Precomputed Realtime GI也是需要在场景静态物体的位置固定之后烘焙一次的,所以如果怕会忘记烘焙的话也可以勾选Build旁边的Auto选框。
下面展示了两种颜色空间下Realtime灯光与烘焙效果的对比:
怎么让不同物体占用不同数量的lightmap像素?
为大家解答的Unity 5 全局光照系统Enlighten问题就到这里了
Unity在5.4 Beta版本中引入了一种新的Draw Call Batching方式——GPU Instancing。当场景中有大量使用相同材质和网格的物体时,通过GPU Instancing可以大幅降低Draw Call数量。本文会简单介绍GPU Instancing的原理并引导读者修改已有的Shader来开启Instancing。
什么是GPU Instancing
GPU Instancing是指由GPU和图形API支持的,用一个Draw Call同时绘制多个Geometry相同的物体的技术。
上图中的场景有数千块陨石,但只有三种陨石模型,这种情况下使用Instancing之后只需要数十个Draw Call。
在D3D11中,Instanced Draw Call API如下所示:
[C]
void DrawIndexedInstanced(
);
注意前两个参数:IndexCountPerInstance和InstanceCount,这是不同于一般Draw Call API的地方。你需要告诉D3D每个Instance用到多少个顶点索引以及这个Draw Call要画多少个Instances。
那么如何做到像上图中那样每块石头都有不同的位置、旋转和大小呢?在使用Instancing时,我们一般会把世界矩阵这种每个Instance独有的数据放到一个额外的Buffer中以供Shader访问,可以是第二个Vertex Buffer,也可以是Constant Buffer。
Instancing的应用场景
Instancing并不是总能提高性能,所以有必要明白Instancing技术可以做什么、不能做什么。
Instancing能做什么:
通过减少Draw Call数量来降低CPU开销。
Instancing不能做什么:
减少GPU的负载。实际上,Instancing还会在GPU上带来一些额外的开销。
具体来说,如果你的场景具备以下条件,使用Instancing可能会给你带来性能提升。
●有大量使用相同材质和相同网格的物体
●性能受制于过多的Draw Call (图形驱动在CPU上负载过大)
在实际的游戏项目中,最合适使用Instancing来优化的是植被系统。因为通常植被系统需要绘制大量相同的树木和草,使用Instancing之后Draw Call的消耗会大幅降低。
在Unity 5.4中使用Instancing
●类似于Static / Dynamic Batching,Instancing是一种新的合并Draw Call的方式
●适用于MeshRenderer组件和Graphics.DrawMesh()
●需要使用相同的Material和Mesh
●需要把Shader改成Instanced的版本
●当所有前提条件都满足时,Instancing是自动进行的,并且比Static/Dynamic Batching有更高的优先级
Instancing的实现
●将Per-Instance Data(世界矩阵、颜色等自定义属性)打包成Uniform Array,存储在Instance Constant Buffers中
●对于可以使用Instancing的Batch,调用各平台图形API的Instanced Draw Call,这样会为每一个Instance生成一个不同的SV_InstanceID
●在Shader中使用SV_InstanceID作为Uniform Array的索引获取当前Instance的Per-Instance Data
如何修改Shader以支持Instancing
自定义Vertex / Fragment Shader:
下面的代码片段展示了如何把一个简单的Unlit Shader修改为支持Instancing的版本。红色字体的部分是在已有Shader基础上需要添加或修改的地方。
[C]
Shader "SimplestInstancedShader"
{
}
下面我们来逐一解释每一处的修改是什么意思。
#pragma multi_compile_instancing
“multi_compile_instancing”会使你的Shader生成两个Variant,其中一个定义了Shader关键字INSTANCING_ON,另外一个没有定义此关键字。
除了这个#pragma指令,下面所列其他的修改都是使用了在UnityInstancing.cginc里定义的宏(此cginc文件位于Unity_Install_Dir\Editor\Data\CGIncludes)。取决于关键字INSTANCING_ON是否被定义,这些宏将展开为不同的代码。
UNITY_INSTANCE_ID
用于在Vertex Shader输入 / 输出结构中定义一个语义为SV_InstanceID的元素。
UNITY_INSTANCING_CBUFFER_START(name) / UNITY_INSTANCING_CBUFFER_END
每个Instance独有的属性必须定义在一个遵循特殊命名规则的Constant Buffer中。使用这对宏来定义这些Constant Buffer。“name”参数可以是任意字符串。
UNITY_DEFINE_INSTANCED_PROP(float4, _Color)
定义一个具有特定类型和名字的每个Instance独有的Shader属性。这个宏实际会定义一个Uniform数组。
UNITY_SETUP_INSTANCE_ID(v);
这个宏必须在Vertex Shader的最开始调用,如果你需要在Fragment Shader里访问Instanced属性,则需要在Fragment Shader的开始也用一下。这个宏的目的在于让Instance ID在Shader函数里也能够被访问到。
UNITY_TRANSFER_INSTANCE_ID(v, o);
在Vertex Shader中把Instance ID从输入结构拷贝至输出结构中。只有当你需要在Fragment Shader中访问每个Instance独有的属性时才需要写这个宏。
UNITY_ACCESS_INSTANCED_PROP(_Color)
访问每个Instance独有的属性。这个宏会使用Instance ID作为索引到Uniform数组中去取当前Instance对应的数据。
最后我们需要提一下UnityObjectToClipPos:
在写Instanced Shader时,通常情况下你并不用在意顶点空间转换,因为所有内建的矩阵名字在Instanced Shader中都是被重定义过的。比如unity_ObjectToWorld实际上会变成unity_ObjectToWorldArray[unity_InstanceID];UNITY_MATRIX_MVP会变成mul(UNITY_MATRIX_VP, unity_ObjectToWorldArray[unity_InstanceID])。注意到如果直接使用UNITY_MATRIX_MVP,我们会引入一个额外的矩阵乘法运算,所以推荐使用UnityObjectToClipPos / UnityObjectToViewPos函数,它们会把这一次额外的矩阵乘法优化为向量-矩阵乘法。
Surface Shader
如果想把一个Surface Shader改写成支持Instancing的版本,你只需要加上“#pragma multi_compile_instancing” 就可以了。设置Instance ID的代码会自动生成。定义或访问每个Instance独有属性的方法同Custom Vertex / Fragment Shader。
另外,你可以在Project窗口右键单击,选择Create->Shader->Standard Surface Shader (Instanced)来创建一个示例Shader。
使用Instancing的限制
下列情况不能使用Instancing:
●使用Lightmap的物体
●受不同Light Probe / Reflection Probe影响的物体
●使用包含多个Pass的Shader的物体,只有第一个Pass可以Instancing
●前向渲染时,受多个光源影响的物体只有Base Pass可以instancing,Add Passes不行
另外,由于Constant Buffer的尺寸限制,一个Instanced Draw Call能画的物体数量是有上限的(参见UnityInstancing.cginc中的UNITY_MAX_INSTANCE_COUNT)
最后需要再次强调的是,Instancing在Shader上有额外的开销,并不是总能提高帧率。永远要以实际Profiling的结果为准!
Unity 5.4 新功能:光照探头代理体
Unity 5.4已经进入到Beta阶段,其中的一个突出功能是光照探头代理体(LPPV)。在这里将会向大家介绍什么是LPPV,并且通过一些小实验来演示它是如何进行工作的。
什么是光照探头代理体
LPPV是一个可以为无法使用烘焙光照贴图的大型动态物体提供更多光照信息的组件,想象一下蒙皮网格或者粒子系统。是的,接受烘焙光照信息的粒子系统,太棒了!
如何使用LPPV组件
LPPV组件从属于光探头组。这个组件位于 Component -> Rendering -> Light Probe Proxy Volume下,默认情况下,这个组件看起来是如下:
这个组件你需要添加到诸如网格甚至光照探头组这样的游戏对象中。那些要应用上LPPV的游戏对象需要具有MeshRenderer或者Renderer组件,并且将Light Probes属性设置为“Use Proxy Volume”。
你可以通过设置Proxy Volume Override选项来使用其它GameObject上的LPPV组件,只需拖放这个GameObject到每一个你想要使用它的Renderer组件的属性域里面。
例如:如果你将LPPV组件添加到光照探头组对象中,那么你就可以通过Proxy Volume Override属性在所有Renderer组件中共享它:
设置包围盒:
包围盒的设置有以下三个选项:
·
·
·
自动局部:
这是默认的属性设置 – 包围盒会在局部空间内计算,插值光探头位置将在这个包围盒内产生。包围盒计算包括了当前以及其下所有层级的将Light Probes属性设置为Use Proxy Volume的Renderer组件,自动全局也是一样的方式。
自动全局:
将会计算世界坐标轴对齐的包围盒。自动全局以及自动局部选项应该与Proxy Volume Override属性联合使用在其它的Renderer组件上。另外你可以通过将同一个LPPV组件设置到层级的父节点来使这个层级下的所有GameObject使用这个LPPV组件。
这个模式与自动局部的区别在于在自动局部模式下,当从一个父GameObject下的多层级GameObject使用同一个LPPV组件时包围盒计算会更加耗时,但是计算出来的包围盒尺寸会更小,这就意味着光照数据会更加紧凑。
自定义:
该模式使你可以自己在UI中编辑包围盒,修改Inspector窗口中的尺寸以及起始点或者使用工具来在场景视图中编辑。包围盒在游戏对象的局部空间中指定。在这种情况下你需要确定所有的Renderer组件所在的GameObject在LPPV的包围盒内。
设置分辨率/密度
在设置完成包围盒后,下面你需要考虑代理体的密度和分辨率。要实现这个在分辨率模式下有两个选项:
自动:
这是默认的属性设置 – 为密度设置一个值,例如每单位的探头数量。每单位的探头数量在X,Y以及Z轴上计算,所以(探头数量)是由包围盒的尺寸决定的。
自定义:
通过下拉菜单来设置X,Y以及Z轴上的分辨率值。值从1开始以2的乘方递增直到32.你最多可以有32x32x32 个插值探头
在使用LPPV时性能度量的考虑:
请记住每个64个插值光探头的批量的插值计算需要大概0.15毫秒的CPU时间(i7 – 4G赫兹)(使用Profiler观察)。光探头插值计算是多线程的,任何小于或等于64个插值光照探头将不会是多线程的,将只在主线程中运行。
使用Unity的内置Profiler,在Timeline视图下你可以看到主线程上的BlendLightProbesJob,如果你将插值光探头数量增加到超过64个,你将同样在工作线程中看到BlendLightProbesJob:
当只有一个批次的64个插值光探头的时候将只会运行在主线程上,当有多个批次(大于64)的时候将会安排在主线程上运行一个,其它的运行到工作线程上,但是这个行为只针对单个LPPV。如果你有多个LPPV,并且每个拥有少于64个插值光探头的话,那么他们都会运行在主线程上。
硬件需求:
该组件需要至少支持Shader Model 4的图形硬件以及相应的API支持,包括支持32位浮点格式以及线性过滤的3D纹理。
使用ShadeSHPerPixel的粒子系统着色器的范例:
标准着色器支持这个特性。如果你想将其添加到一个自定义着色器中,使用ShadeSHPerPixel函数。看看这个范例可以了解如何使用这个函数:
[C#]
Shader "Particles/AdditiveLPPV" {
Properties
{
}
Category
}
Unity5 新功能解析--GI(全局光)
Unity5带来的最大的改变就是全新的GI,在烘焙引擎上抛弃了4的beast,使用了虚幻采用的Enlighten,引入了实时的全局光。这里对UNITY5的新的光照系统做一总结.
·
·
构成 : 可实时更新的lightmap + 可实时更新的光照探头(light probe)+ 可实时更新的cubemap(Reflection probe)
实时全局光只实时计算物体之间的bounce light(物体之间来回反射的光)
实时全局光的实现:静态物体之间的bounce light可以在预先计算阶段搞定(light map)
相比UNITY4来讲,多出来的实时全局光部分应该是多出了lightmap 光照探头这些可以实时更新 而不是定死的
如下图在光源变化下,静态物体的对地板的反射绿色光也在跟着变化
此外UNITY5里面的标准材质的自发光在全局光计算中可以当做光源,即可以照亮周围物体并产生阴影,这是一个很屌的效果!,如下图,
自发光的GI效果如图
这可以表现一个很真实的发光体,相信看到这美术同学应该有点小激动吧~
·
·
·
·
·
·
·