Unity 5.4 新功能:光照探头代理体
Unity 5.4 已进入 Beta 阶段,其中一个突出功能是光照探头代理体(Light Probe Proxy Volume,简称 LPPV)。本文将详细介绍什么是 LPPV,并通过一些小实验演示其工作原理。
什么是光照探头代理体
LPPV 是一个可为无法使用烘焙光照贴图的大型动态物体提供更多光照信息的组件,例如蒙皮网格或粒子系统。想象一下,粒子系统也能接受烘焙光照信息,这是非常棒的特性。
如何使用 LPPV 组件
LPPV 组件从属于光探头组,位于 Component -> Rendering -> Light Probe Proxy Volume 之下。默认情况下,该组件具有特定的外观。
添加组件
你需要将此组件添加到诸如网格甚至光照探头组这样的游戏对象中。那些要应用 LPPV 的游戏对象需要具备 MeshRenderer 或者 Renderer 组件,并且将 Light Probes 属性设置为 “Use Proxy Volume”。
共享 LPPV 组件
你可以通过设置 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 函数。以下是一个范例:
Shader "Particles/AdditiveLPPV" {
Properties {
_MainTex ("Particle Texture", 2D) = "white" {}
_TintColor ("Tint Color", Color) = (0.5,0.5,0.5,0.5)
}
Category {
Tags {"Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent"}
Blend SrcAlpha One
ColorMask RGB
Cull Off Lighting Off ZWrite Off
SubShader {
Pass {
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile_particles
#pragma multi_compile_fog
// Don’t forget to specify the target
#pragma target 3.0
#include "UnityCG.cginc"
// You have to include this header to have access to ShadeSHPerPixel
#include "UnityStandardUtils.cginc"
fixed4 _TintColor;
sampler2D _MainTex;
struct appdata_t {
float4 vertex : POSITION;
float3 normal : NORMAL;
fixed4 color : COLOR;
float2 texcoord : TEXCOORD0;
};
struct v2f {
float4 vertex : SV_POSITION;
fixed4 color : COLOR;
float2 texcoord : TEXCOORD0;
UNITY_FOG_COORDS(1)
float3 worldPos : TEXCOORD2;
float3 worldNormal : TEXCOORD3;
};
float4 _MainTex_ST;
v2f vert (appdata_t v) {
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.worldNormal = UnityObjectToWorldNormal(v.normal);
o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
o.color = v.color;
o.texcoord = TRANSFORM_TEX(v.texcoord,_MainTex);
UNITY_TRANSFER_FOG(o,o.vertex);
return o;
}
fixed4 frag (v2f i) : SV_Target {
half3 currentAmbient = half3(0, 0, 0);
half3 ambient = ShadeSHPerPixel(i.worldNormal, currentAmbient, i.worldPos);
fixed4 col = _TintColor * i.color * tex2D(_MainTex, i.texcoord);
col.xyz += ambient;
UNITY_APPLY_FOG_COLOR(i.fogCoord, col, fixed4(0,0,0,0)); // fog towards black due to our blend mode
return col;
}
ENDCG
}
}
}
}
来源:Unity 官方社区