Baked Light(烘焙光)
——光照映射和探針
本節(jié)內(nèi)容
- 烘焙靜態(tài)全局照明(GI)
- 采樣光照貼圖、探針粥血、光照探針代理體(LPPVs)
- 創(chuàng)建一個元通道(meta pass)
- 支持自發(fā)光(Emissive)
這是關(guān)于如何創(chuàng)建Custom SRP的系列教程的第五個部分梧喷,它使得烘焙靜態(tài)光照到貼圖和探針中成為可能概龄。
這個教程使用的是Unity版本是2019.2.6f1.
(ps:文章總被吞…最后偶然看到可能會被吞的一些詞兒…嘗試改了點(diǎn)但有些意思感覺不到位~)
1. 烘焙靜態(tài)光(Rendering Shadows)
到目前為止昌腰,我們已經(jīng)在渲染時計算了所有的光照,但這不是唯一的選擇坏挠。光照也可以提前計算,并存儲在光照貼圖和探針中邪乍。這樣做有兩個主要原因:減少實時運(yùn)算和添加不能在運(yùn)行時計算的間接照明降狠。后者是被稱為全局光照的一部分:光不是來自直接光源,而是間接式的通過反射庇楞、環(huán)境或自發(fā)光表面榜配。
烘焙光照的缺點(diǎn)是它是靜態(tài)的,所以不能在運(yùn)行時改變吕晌。它還需要進(jìn)行存儲蛋褥,這增加了項目構(gòu)建的大小和內(nèi)存占用。
那么實時全局照明呢?
.
Unity使用了啟發(fā)式照明系統(tǒng)(Enlighten System)進(jìn)行實時全局照明(realtime-global-illumination)睛驳,但這已經(jīng)被棄用了烙心,所以我們不會使用它。 此外乏沸,反射探針可以在運(yùn)行時渲染淫茵,以創(chuàng)建高光環(huán)境反射,但我們不會在本教程中介紹它們屎蜓。
1.1 場景照明設(shè)置(Scene Lighting Settings)
GI是通過照明窗口的場景選項卡逐場景配置的痘昌。烘焙光照通過Mixes Lighting
下的Baked Global Illumination
開關(guān)啟用。還有一個Lighting Mode
光照模式選項炬转,我們將選用Baked Indirect
辆苔,這意味著我們烘焙所有靜態(tài)間接照明。
如果你的項目是在Unity 2019.2或更早的時候創(chuàng)建的扼劈,那么你也會看到一個啟用實時照明的選項驻啤,這應(yīng)該是禁用的。 如果你的項目是在Unity 2019.3或更高版本中創(chuàng)建的荐吵,那么該選項將不會顯示骑冗。
再往下是一個Lightmapping Settings
部分赊瞬,可以用來控制光照映射過程,這是由Unity編輯器完成的贼涩。 我將使用默認(rèn)設(shè)置巧涧,除了LightMap Resolution
被降低到20,Compress Lightmaps
被禁用遥倦,Directional Mode
被設(shè)置為Non-Directional
谤绳。我還使用了Progressive CPU
光照貼圖。
那么實時全局照明呢?
.
它還烘焙平行光數(shù)據(jù)袒哥,這使得法線貼圖能夠影響入射的烘烤光缩筛。因為我們目前還不支持法線映射,所以沒有理由啟用它堡称。
1.2 靜態(tài)對象(Static Objects)
為了演示烘焙光照瞎抛,我創(chuàng)建了一個場景,用一個綠色的平面作為地面却紧,放置了一些盒子和球體桐臊,中間放了一個只有一面沒有墻壁的房間,所以它的內(nèi)部都會產(chǎn)生陰影啄寡。
場景有一個簡單的平行光豪硅,其模式設(shè)置為混合。這告訴Unity它應(yīng)該烘焙這個光的間接照明挺物。除此之外懒浮,這盞燈仍然像普通的實時光一樣工作。
我還將地面和所以立方體识藤,包括組成房間的立方體砚著,都納入到了烘焙過程。它們將是光線反射的對象痴昧,因此要變成間接的稽穆。這是通過啟用MeshRenderer
組件的Contribute Global Illumination
開關(guān)來實現(xiàn)的。啟用這一功能也會自動將它們的Receive Global Illumination
模式切換到Lightmaps
赶撰,這意味著到達(dá)它們表面的間接光會被烘焙到光照貼圖中舌镶。 您也可以通過從對象的Static
下拉列表中啟用Contribute GI
來啟用這個模式,或者使它完全靜態(tài)化豪娜。
一旦啟用餐胀,場景的照明將再次烘焙,假設(shè)在Lighting
窗口中的Auto Generate
開啟瘤载,否則你將不得不按下Generate Lighting
按鈕否灾。光照貼圖設(shè)置也顯示在MeshRenderer
組件中,包括一個包含對象的光照貼圖視圖鸣奔。
不應(yīng)該有很多綠色的間接光嗎(因為綠色的地表)?
.
是的墨技,我們稍后會講到
球體不顯示在光照貼圖中惩阶,因為它們沒有貢獻(xiàn)全局照明,因此被認(rèn)為是動態(tài)的扣汪。 它們必須依賴光照探針断楷,我們稍后會講到。靜態(tài)對象也可以通過將它們的Receive Global Illumination
模式切換回Light Probes
來排除在貼圖之外崭别。 它們?nèi)匀粫绊懞姹盒Ч晟粫加霉庹召N圖的空間。
1.3 完全烘焙的光(Fully-Baked Light)
烘焙光照大部分是藍(lán)色的紊遵,因為它由天空盒主導(dǎo),代表來自環(huán)境中天空的間接照明侥蒙。房間中心周圍明亮的區(qū)域是由從地面和墻壁反射過來的間接照明造成的暗膜。
我們也可以將所有的照明烘焙到貼圖中,無論是直接的還是間接的鞭衩。這可以通過將燈的模式設(shè)置為“Baked”來實現(xiàn)学搜。 然后它不再提供實時照明。
實際上论衍,烘培光的直接照明也被視為間接照明慌核,從而在使光照貼圖更亮晨川。
2. 采集烘焙光(Sampling Baked Light)
目前所有的東西都被渲染成純黑色,因為沒有實時光,我們的著色器還不知道全局照明疼鸟。 我們必須對光照貼圖進(jìn)行采樣,才能實現(xiàn)這個功能仅讽。
2.1 全局照明(Global Illumination)
創(chuàng)建一個新的ShaderLibrary/GI.hlsl
文件沮趣,包含所有與全局照明相關(guān)的代碼。 在里面定義一個GI
結(jié)構(gòu)體和一個GetGI
函數(shù)咪啡,給定光照貼圖的UV坐標(biāo)首启。間接光來自四面八方,因此只能用于漫反射撤摸,而不能用于鏡面反射毅桃。所以給GI
一個漫反射字段。最初用光照貼圖的UV填充它准夷,用于調(diào)試目的钥飞。
#ifndef CUSTOM_GI_INCLUDED
#define CUSTOM_GI_INCLUDED
struct GI {
float3 diffuse;
};
GI GetGI (float2 lightMapUV) {
GI gi;
gi.diffuse = float3(lightMapUV, 0.0);
return gi;
}
#endif
那么鏡面全局照明呢?
.
*鏡面環(huán)境反射通常是通過反射探針提供的冕象,我們將在以后的教程中介紹代承。屏幕空間反射是另一種選擇。 *
添加一個GI
參數(shù)到GetLighting
渐扮,并在累計實時照明之前使用它初始化顏色论悴。在現(xiàn)在我們不計算表面的漫反射掖棉,所以我們可以看到未修改的入射光。
float3 GetLighting (Surface surfaceWS, BRDF brdf, GI gi) {
ShadowData shadowData = GetShadowData(surfaceWS);
float3 color = gi.diffuse;
…
return color;
}
在LitPass
中Lighting
之前引用GI
#include "../ShaderLibrary/GI.hlsl"
#include "../ShaderLibrary/Lighting.hlsl"
在LitPassFragment
獲得GI膀估,初始化為0幔亥,并將其傳遞給GetLighting
。
GI gi = GetGI(0.0);
float3 color = GetLighting(surface, brdf, gi);
2.2 光照貼圖坐標(biāo)(Light Map Coordinates)
為了獲得光照貼圖的UV坐標(biāo)察纯,Unity必須將它們發(fā)送到著色器帕棉。我們必須指示管線為每個參與光照映射的對象做這個。這是通過在CameraRenderer.DrawVisibleGeometry
中將DrawingSettings
的perObjectData
屬性設(shè)置為PerObjectData.Lightmaps
來實現(xiàn)的饼记。
var drawingSettings = new DrawingSettings(
unlitShaderTagId, sortingSettings
) {
enableDynamicBatching = useDynamicBatching,
enableInstancing = useGPUInstancing,
perObjectData = PerObjectData.Lightmaps
};
Unity現(xiàn)在將使用帶有LIGHTMAP_ON
關(guān)鍵字的著色器變體來渲染光照映射的對象香伴。為我們的光照著色器的CustomLit
通道添加一個multi-compile
指令。
#pragma multi_compile _ LIGHTMAP_ON
#pragma multi_compile_instancing
光照貼圖的UV坐標(biāo)是Attributes
頂點(diǎn)數(shù)據(jù)的一部分具则。 我們必須將它們轉(zhuǎn)移到Varyings
即纲,這樣我們才能在LitPassFragment
中使用它們。但我們應(yīng)該只在需要的時候這樣做博肋。我們可以使用類似于傳輸GPU-Instancing
標(biāo)識符的方法低斋,并依賴GI_ATTRIBUTE_DATA
、GI_VARYINGS_DATA
和TRANSFER_GI_DATA
宏匪凡。
struct Attributes {
…
GI_ATTRIBUTE_DATA
UNITY_VERTEX_INPUT_INSTANCE_ID
};
struct Varyings {
…
GI_VARYINGS_DATA
UNITY_VERTEX_INPUT_INSTANCE_ID
};
Varyings LitPassVertex (Attributes input) {
Varyings output;
UNITY_SETUP_INSTANCE_ID(input);
UNITY_TRANSFER_INSTANCE_ID(input, output);
TRANSFER_GI_DATA(input, output);
…
}
加上另一個GI_FRAGMENT_DATA
宏來檢索GetGI所需的參數(shù)膊畴。
GI gi = GetGI(GI_FRAGMENT_DATA(input));
我們需要在GI.hlsl
中自己定義這些宏。最初定義他們?yōu)榭詹∮危?code>GI_FRAGMENT_DATA是0唇跨。一個宏的參數(shù)列表的工作方式類似于一個函數(shù),除了沒有類型衬衬,并且在宏名和參數(shù)列表之間不允許有空格轻绞,否則列表將被解釋為宏定義的東西。
#define GI_ATTRIBUTE_DATA
#define GI_VARYINGS_DATA
#define TRANSFER_GI_DATA(input, output)
#define GI_FRAGMENT_DATA(input) 0.0
當(dāng)定義LIGHTMAP_ON
時佣耐,宏應(yīng)該將另一個UV集添加到結(jié)構(gòu)體政勃、復(fù)制它和檢索它。光照貼圖UV是通過第二個紋理的坐標(biāo)通道提供的兼砖,所以我們需要在Attributes
中使用TEXCOORD1
語義奸远。
#if defined(LIGHTMAP_ON)
#define GI_ATTRIBUTE_DATA float2 lightMapUV : TEXCOORD1;
#define GI_VARYINGS_DATA float2 lightMapUV : VAR_LIGHT_MAP_UV;
#define TRANSFER_GI_DATA(input, output) output.lightMapUV = input.lightMapUV;
#define GI_FRAGMENT_DATA(input) input.lightMapUV
#else
#define GI_ATTRIBUTE_DATA
#define GI_VARYINGS_DATA
#define TRANSFER_GI_DATA(input, output)
#define GI_FRAGMENT_DATA(input) 0.0
#endif
所有靜態(tài)烘焙的對象現(xiàn)在顯示他們的UV,而所有動態(tài)對象保持黑色讽挟。
2.3 轉(zhuǎn)換的光照貼圖坐標(biāo)(Transformed Light Map Coordinates)
光照貼圖坐標(biāo)通常是由Unity或部分導(dǎo)入的網(wǎng)格數(shù)據(jù)自動生成的懒叛。 他們定義了一個展開的紋理,使網(wǎng)格變平耽梅,從而映射到紋理坐標(biāo)薛窥。 展開是在光照貼圖縮放和定位每個對象,所以每個實例都有自己的空間。這就像基礎(chǔ)UV的縮放和平移一樣诅迷。 我們必須把這個應(yīng)用到光照貼圖UV上佩番。
CBUFFER_START(UnityPerDraw)
float4x4 unity_ObjectToWorld;
float4x4 unity_WorldToObject;
float4 unity_LODFade;
real4 unity_WorldTransformParams;
float4 unity_LightmapST;
float4 unity_DynamicLightmapST;
CBUFFER_END
光照映射是否與GPU實例化一起工作?
.
*是的罢杉,所有的UnityPerDraw
數(shù)據(jù)在需要時得到實例化趟畏。 *
然后調(diào)整TRANSFER_GI_DATA
宏,使其應(yīng)用轉(zhuǎn)換滩租。宏定義可以分成多行赋秀,每一行的末尾除了最后一行都需要用反斜杠標(biāo)記。
#define TRANSFER_GI_DATA(input, output) \
output.lightMapUV = input.lightMapUV * \
unity_LightmapST.xy + unity_LightmapST.zw;
2.4 采集光照貼圖(Sampling the Light Map)
光照貼圖的采樣是GI
的職責(zé)律想。 光照貼圖紋理被稱為unity_Lightmap
猎莲,帶有相應(yīng)的采樣狀態(tài)。引用EntityLighting.hlsl
技即,因為我們將使用它來檢索光照數(shù)據(jù)益眉。
#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/EntityLighting.hlsl"
TEXTURE2D(unity_Lightmap);
SAMPLER(samplerunity_Lightmap);
創(chuàng)建一個SampleLightMap
函數(shù),該函數(shù)在有光照貼圖時調(diào)用SampleSingleLightmap
姥份,否則返回0。 在GetGI
中使用它來設(shè)置漫射光年碘。
float3 SampleLightMap (float2 lightMapUV) {
#if defined(LIGHTMAP_ON)
return SampleSingleLightmap(lightMapUV);
#else
return 0.0;
#endif
}
GI GetGI (float2 lightMapUV) {
GI gi;
gi.diffuse = SampleLightMap(lightMapUV);
return gi;
}
SampleSingleLightmap
函數(shù)需要更多的參數(shù)澈歉。首先,我們必須將紋理和采樣狀態(tài)作為前兩個參數(shù)傳遞給它屿衅,為此我們可以使用TEXTURE2D_ARGS
宏埃难。
return SampleSingleLightmap(
TEXTURE2D_ARGS(unity_Lightmap, samplerunity_Lightmap), lightMapUV
);
然后是要應(yīng)用的縮放和轉(zhuǎn)換。 因為我們之前已經(jīng)做過了涤久,我們將在這里使用恒等變換涡尘。
return SampleSingleLightmap(
TEXTURE2D_ARGS(unity_Lightmap, samplerunity_Lightmap), lightMapUV,
float4(1.0, 1.0, 0.0, 0.0)
);
然后是一個布爾值,用于指示光照映射是否被壓縮响迂,在UNITY_LIGHTMAP_FULL_HDR
沒有定義的情況下就是這種情況考抄。最后一個參數(shù)是包含解碼指令的float4。 對第一個組件使用LIGHTMAP_HDR_MULTIPLIER
蔗彤,對第二個組件使用LIGHTMAP_HDR_EXPONENT
川梅,其他組件沒有使用。
return SampleSingleLightmap(
TEXTURE2D_ARGS(unity_Lightmap, samplerunity_Lightmap), lightMapUV,
float4(1.0, 1.0, 0.0, 0.0),
#if defined(UNITY_LIGHTMAP_FULL_HDR)
false,
#else
true,
#endif
float4(LIGHTMAP_HDR_MULTIPLIER, LIGHTMAP_HDR_EXPONENT, 0.0, 0.0)
);
2.5 禁用環(huán)境照明(Disabling Environment Lighting)
烘焙光非常明亮然遏,因為它還包括來自天空的間接照明贫途。 我們可以通過將Intensity Multiplier
設(shè)置為零來禁用它。 這使得我們能夠聚焦于單一方向的光待侵。
注意丢早,房間內(nèi)部現(xiàn)在是間接照明,主要是通過地面秧倾。
我們也可以烘焙其他類型的光嗎怨酝?
.
*是的傀缩,盡管我們目前只關(guān)注平行光,但其他類型的燈也可以烘焙凫碌,但需要一些額外的工作扑毡。 *
3. 光照探針(Light Probes)
動態(tài)對象不影響烘焙的全局照明,但它們可以通過光照探針受其影響盛险。光照探針是場景中的一個點(diǎn)瞄摊,它烘焙了所有入射光,通過一個三階多項式來近似它苦掘,確切的說是L2球面諧波
换帜。光照探針被放置在場景里,Unity在它們之間對每個物體進(jìn)行插值鹤啡,以達(dá)到近似它們位置的最終照明惯驼。
3.1 光照探針組組件(Light Probe Group)
通過GameObject/Light/Light Probe Group
創(chuàng)建一個光照探針組,光照探針被添加到場景中递瑰。這將創(chuàng)建一個帶有LightProbeGroup
組件的游戲?qū)ο笏钌摻M件默認(rèn)包含6個立方體形狀的探針。當(dāng)Edit Light Probes
啟用時抖部,你可以移動说贝、復(fù)制和刪除單個探針,就像它們是游戲?qū)ο笠粯印?/p>
在一個場景中可以有多個探針組慎颗。Unity合并所有的探針乡恕,然后創(chuàng)建一個四面體網(wǎng)格連接它們。 每個動態(tài)對象最終都在一個四面體中俯萎。 在其頂點(diǎn)處的四個探針被插值傲宜,以應(yīng)用于物體的最終照明。 如果一個物體最終在探針覆蓋的區(qū)域之外夫啊,則使用最近的三角形代替函卒,這樣光照可能會顯得奇怪。
默認(rèn)情況下撇眯,當(dāng)一個動態(tài)對象被選擇時谆趾,將繪制線框來顯示影響該對象的探針,以及在其位置上的插值結(jié)果叛本』ε睿可以通過在Lighting
窗口的Debug Settings
下調(diào)整Light Probe Visualization
來改變這個。
放置探針的位置取決于場景来候。首先跷叉,只有動態(tài)對象所在的地方才需要它們。 第二,把它們放在光線有變化的地方云挟。 每個探針都是插值的終點(diǎn)梆砸,所以把它們放在照明過度的附近。 第三园欣,不要把它們放在烘焙的幾何體中帖世,因為它們最終會變成黑色。 最后沸枯,差值是通過物體的日矫,所以如果墻壁兩側(cè)的照明不同,就讓探針靠近墻壁的兩側(cè)绑榴,這樣就不會有物體在兩邊之間進(jìn)行插值哪轿。 除此之外,你必須進(jìn)行一些實驗用來確定效果翔怎。
3.2 采集探針(Sampling Probes)
插入的光照探針數(shù)據(jù)必須傳遞給每個對象的GPU窃诉。我們必須告訴Unity這樣做,這個需要PerObjectData.LightProbe
而不是PerObjectData.Lightmaps
赤套。我們需要啟用這兩個標(biāo)志飘痛,因此使用OR
運(yùn)算符合并它們。
perObjectData = PerObjectData.Lightmaps | PerObjectData.LightProbe
所需的UnityPerDraw
數(shù)據(jù)由7個float4向量組成容握。
···
CBUFFER_START(UnityPerDraw)
…
float4 unity_SHAr;
float4 unity_SHAg;
float4 unity_SHAb;
float4 unity_SHBr;
float4 unity_SHBg;
float4 unity_SHBb;
float4 unity_SHC;
CBUFFER_END
···
我們通過一個新的SampleLightProbe
函數(shù)對GI
中的光照探針進(jìn)行采樣宣脉。我們需要一個方向,所以給它一個世界空間Surface
參數(shù)唯沮。
如果這個對象使用光照貼圖,則返回0堪遂。 否則返回0和SampleSH9
之間的最大值介蛉。該函數(shù)需要探針數(shù)據(jù)和法向量作為參數(shù)。 探針數(shù)據(jù)必須以系數(shù)數(shù)組的形式提供溶褪。
float3 SampleLightProbe (Surface surfaceWS) {
#if defined(LIGHTMAP_ON)
return 0.0;
#else
float4 coefficients[7];
coefficients[0] = unity_SHAr;
coefficients[1] = unity_SHAg;
coefficients[2] = unity_SHAb;
coefficients[3] = unity_SHBr;
coefficients[4] = unity_SHBg;
coefficients[5] = unity_SHBb;
coefficients[6] = unity_SHC;
return max(0.0, SampleSH9(coefficients, surfaceWS.normal));
#endif
}
向GetGI
添加一個Surface
參數(shù)币旧,并讓它將光照探針采樣添加到漫反射中。
GI GetGI (float2 lightMapUV, Surface surfaceWS) {
GI gi;
gi.diffuse = SampleLightMap(lightMapUV) + SampleLightProbe(surfaceWS);
return gi;
}
最后猿妈,在LitPassFragment
中將Surface
傳遞給它吹菱。
GI gi = GetGI(GI_FRAGMENT_DATA(input), surface);
3.3 光照探針代理體(Light Probe Proxy Volumes)
光照探針適用于比較小的動態(tài)物體,因為光照是基于單點(diǎn)的彭则,所以不適用于較大的物體鳍刷。 例如,我在場景中添加了兩個拉伸的立方體俯抖。 因為它們的位置位于黑暗區(qū)域输瓜,立方體都是均勻的黑暗,盡管這顯然與光照不匹配。
我們可以通過使用一個光照探針代理體(Light Probe Proxy Volume尤揣,簡稱LPPV)來解決這個限制搔啊。 最簡單的方法是向每個立方體添加一個LightProbeProxyVolume
組件,然后將它們的Light Probes
模式設(shè)置為Use Proxy Volume
北戏。
為什么在場景視圖中看不到探針负芋?
.
當(dāng)LPPV的Refresh Mode
設(shè)置為Automatic
時,它們可能不會顯示嗜愈。 在這種情況下旧蛾,你可以臨時設(shè)置為Every Frame
。
3.4 采集光照探針代理體(Sampling LPPVs)
LPPV還需要將每個對象的數(shù)據(jù)發(fā)送給GPU芝硬,所以我們必須啟用PerObjectData.LightProbeProxyVolume
蚜点。
perObjectData =
PerObjectData.Lightmaps | PerObjectData.LightProbe |
PerObjectData.LightProbeProxyVolume
UnityPerDraw
需要添加四個額外的值:unity_ProbeVolumeParams
, unity_ProbeVolumeWorldToObject
, unity_ProbeVolumeSizeInv
和unity_ProbeVolumeMin
。 第二個是一個矩陣拌阴,其他都是四維向量绍绘。
CBUFFER_START(UnityPerDraw)
…
float4 unity_ProbeVolumeParams;
float4x4 unity_ProbeVolumeWorldToObject;
float4 unity_ProbeVolumeSizeInv;
float4 unity_ProbeVolumeMin;
CBUFFER_END
數(shù)據(jù)存儲在一個稱為unity_ProbeVolumeSH
的3D浮點(diǎn)紋理中。通過TEXTURE3D_FLOAT
宏添加到GI
迟赃,包括它的采樣狀態(tài)陪拘。
TEXTURE3D_FLOAT(unity_ProbeVolumeSH);
SAMPLER(samplerunity_ProbeVolumeSH);
使用LPPV或插值光照探針是通過unity_ProbeVolumeParams
的第一個分量進(jìn)行通信的。 如果被設(shè)置了纤壁,則必須通過SampleProbeVolumeSH4
函數(shù)對體積進(jìn)行采樣左刽。 我們必須傳遞紋理和采樣,然后是世界位置和法線酌媒。然后是矩陣欠痴、unity_ProbeVolumeParams
的Y和Z分量,然后是unity_ProbeVolumeMin
和unity_ProbeVolumeSizeInv
數(shù)據(jù)的XYZ分量秒咨。
if (unity_ProbeVolumeParams.x) {
return SampleProbeVolumeSH4(
TEXTURE3D_ARGS(unity_ProbeVolumeSH, samplerunity_ProbeVolumeSH),
surfaceWS.position, surfaceWS.normal,
unity_ProbeVolumeWorldToObject,
unity_ProbeVolumeParams.y, unity_ProbeVolumeParams.z,
unity_ProbeVolumeMin.xyz, unity_ProbeVolumeSizeInv.xyz
);
}
else {
float4 coefficients[7];
coefficients[0] = unity_SHAr;
coefficients[1] = unity_SHAg;
coefficients[2] = unity_SHAb;
coefficients[3] = unity_SHBr;
coefficients[4] = unity_SHBg;
coefficients[5] = unity_SHBb;
coefficients[6] = unity_SHC;
return max(0.0, SampleSH9(coefficients, surfaceWS.normal));
}
對LPPV進(jìn)行采樣需要對體積空間進(jìn)行變換喇辽,以及一些其他計算,包括體積紋理采樣和球諧函數(shù)的應(yīng)用雨席。 在這種情況下菩咨,只應(yīng)用L1球諧函數(shù),因此結(jié)果不太精確陡厘,但可以在單個物體的表面上變化抽米。
4. 元通道(Meta Pass)
因為間接漫反射光從表面反射,它應(yīng)該受到這些表面的漫反射率的影響糙置。目前這種情況還沒有發(fā)生云茸。 Unity將我們的表面視為統(tǒng)一的白色。Unity使用一個特殊的Meta Pass
來確定在烘焙時的反射光谤饭。 因為我們還沒有定義這樣的Pass, Unity使用默認(rèn)的Pass查辩,它最終是白色的胖笛。
4.1 統(tǒng)一的輸入(Unified Input)
添加另一個通道意味著我們必須再次定義著色器屬性。 讓我們從LitPass
中提取基本紋理和UnityPerMaterial
緩沖宜岛,并把它放在一個新的Shaders/LitInput.hlsl
文件中长踊。我們還將通過引入TransformBaseUV
、GetBase
萍倡、GetCutoff
身弊、GetMetallic
和GetSmoothness
函數(shù)來隱藏實例化代碼。 給它們一個基本的UV參數(shù)列敲,即使它沒有被使用阱佛。 值是否從映射中檢索也以這種方式隱藏。
#ifndef CUSTOM_LIT_INPUT_INCLUDED
#define CUSTOM_LIT_INPUT_INCLUDED
TEXTURE2D(_BaseMap);
SAMPLER(sampler_BaseMap);
UNITY_INSTANCING_BUFFER_START(UnityPerMaterial)
UNITY_DEFINE_INSTANCED_PROP(float4, _BaseMap_ST)
UNITY_DEFINE_INSTANCED_PROP(float4, _BaseColor)
UNITY_DEFINE_INSTANCED_PROP(float, _Cutoff)
UNITY_DEFINE_INSTANCED_PROP(float, _Metallic)
UNITY_DEFINE_INSTANCED_PROP(float, _Smoothness)
UNITY_INSTANCING_BUFFER_END(UnityPerMaterial)
float2 TransformBaseUV (float2 baseUV) {
float4 baseST = UNITY_ACCESS_INSTANCED_PROP(UnityPerMaterial, _BaseMap_ST);
return baseUV * baseST.xy + baseST.zw;
}
float4 GetBase (float2 baseUV) {
float4 map = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, baseUV);
float4 color = UNITY_ACCESS_INSTANCED_PROP(UnityPerMaterial, _BaseColor);
return map * color;
}
float GetCutoff (float2 baseUV) {
return UNITY_ACCESS_INSTANCED_PROP(UnityPerMaterial, _Cutoff);
}
float GetMetallic (float2 baseUV) {
return UNITY_ACCESS_INSTANCED_PROP(UnityPerMaterial, _Metallic);
}
float GetSmoothness (float2 baseUV) {
return UNITY_ACCESS_INSTANCED_PROP(UnityPerMaterial, _Smoothness);
}
#endif
為了在Lit
的所有通道中引用這個文件戴而,在Lit
的SubShader
塊的頂部添加一個HLSLINCLUDE
塊凑术。 在這里包含Common
,然后是LitInput
所意。 這段代碼將被插入到所有通道淮逊。
SubShader {
HLSLINCLUDE
#include "../ShaderLibrary/Common.hlsl"
#include "LitInput.hlsl"
ENDHLSL
…
}
從LitPass
中刪除現(xiàn)在重復(fù)的引用語句和聲明。
//#include "../ShaderLibrary/Common.hlsl"
…
//TEXTURE2D(_BaseMap);
//SAMPLER(sampler_BaseMap);
//UNITY_INSTANCING_BUFFER_START(UnityPerMaterial)
//…
//UNITY_INSTANCING_BUFFER_END(UnityPerMaterial)
在LitPassVertex
中使用TransformBaseUV
扶踊。
//float4 baseST = UNITY_ACCESS_INSTANCED_PROP(UnityPerMaterial, _BaseMap_ST);
output.baseUV = TransformBaseUV(input.baseUV);
以及在LitPassFragment
中檢索著色器屬性的相關(guān)函數(shù)泄鹏。
//float4 baseMap = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, input.baseUV);
//float4 baseColor = UNITY_ACCESS_INSTANCED_PROP(UnityPerMaterial, _BaseColor);
float4 base = GetBase(input.baseUV);
#if defined(_CLIPPING)
clip(base.a - GetCutoff(input.baseUV));
#endif
…
surface.metallic = GetMetallic(input.baseUV);
surface.smoothness = GetSmoothness(input.baseUV);
對ShadowCasterPass
同樣的處理。
4.2 無光照(Unlit)
讓我們對Unlit
著色器也這樣做秧耗。 復(fù)制LitInput
备籽。并將其重命名為UnlitInput.hlsl
。 然后從UnityPerMaterial
中移除_Metallic
和_Smoothness
分井。 保留GetMetallic
和GetSmoothness
函數(shù)车猬,并使它們返回0.0,表示一個非常平淡的漫反射表面尺锚。之后也給著色器一個HLSLINCLUDE
塊珠闰。
HLSLINCLUDE
#include "../ShaderLibrary/Common.hlsl"
#include "UnlitInput.hlsl"
ENDHLSL
轉(zhuǎn)換UnlitPass
,就像我們對LitPass
做的那樣缩麸。 請注意铸磅,ShadowCasterPass
對于兩個著色器都工作得很好赡矢,即使它以不同的輸入定義結(jié)束杭朱。
4.3 元通道模式(Meta Light Mode)
為Lit
和Unlit
著色器添加一個新的通道,將LightMode
設(shè)置為Meta
吹散。 這個通道要求裁剪總是關(guān)閉的弧械,可以通過添加Cull Off
來配置。 它將使用MetaPassVertex
和MetaPassFragment
函數(shù)空民,在一個新的MetaPass.hlsl
文件中定義刃唐。它不需要multi-compile
指令羞迷。
Pass {
Tags {
"LightMode" = "Meta"
}
Cull Off
HLSLPROGRAM
#pragma target 3.5
#pragma vertex MetaPassVertex
#pragma fragment MetaPassFragment
#include "MetaPass.hlsl"
ENDHLSL
}
我們需要知道表面的漫反射,所以我們必須在MetaPassFragment
中獲得它的BRDF
數(shù)據(jù)画饥。 因此衔瓮,我們必須引用BRDF
,以及Surface
抖甘,Shadows
和Light
热鞍。我們只需要知道對象空間的位置和基礎(chǔ)UV,最初將裁剪空間的位置設(shè)置為零衔彻。 Surface可以通過ZERO_INITIALIZE(Surface, surface)
初始化為0薇宠,之后我們只需要設(shè)置它的顏色、金屬度和平滑度值艰额。 這足以獲得BRDF數(shù)據(jù)澄港,但我們將從返回0開始。
#ifndef CUSTOM_META_PASS_INCLUDED
#define CUSTOM_META_PASS_INCLUDED
#include "../ShaderLibrary/Surface.hlsl"
#include "../ShaderLibrary/Shadows.hlsl"
#include "../ShaderLibrary/Light.hlsl"
#include "../ShaderLibrary/BRDF.hlsl"
struct Attributes {
float3 positionOS : POSITION;
float2 baseUV : TEXCOORD0;
};
struct Varyings {
float4 positionCS : SV_POSITION;
float2 baseUV : VAR_BASE_UV;
};
Varyings MetaPassVertex (Attributes input) {
Varyings output;
output.positionCS = 0.0;
output.baseUV = TransformBaseUV(input.baseUV);
return output;
}
float4 MetaPassFragment (Varyings input) : SV_TARGET {
float4 base = GetBase(input.baseUV);
Surface surface;
ZERO_INITIALIZE(Surface, surface);
surface.color = base.rgb;
surface.metallic = GetMetallic(input.baseUV);
surface.smoothness = GetSmoothness(input.baseUV);
BRDF brdf = GetBRDF(surface);
float4 meta = 0.0;
return meta;
}
#endif
一旦Unity用我們自己的元通道再次烘焙場景柄沮,所有的間接照明都將消失回梧,因為黑色表面不會反射任何東西。
4.4 光照貼圖坐標(biāo)(Light Map Coordinates)
就像對光照貼圖進(jìn)行采樣一樣铡溪,我們需要使用光照貼圖UV坐標(biāo)漂辐。 不同的是,這次我們在相反的方向上使用棕硫,將它們用于XY對象空間的位置髓涯。 在此之后,我們必須將其提供給TransformWorldToHClip
哈扮,盡管在本例中纬纪,該函數(shù)執(zhí)行的轉(zhuǎn)換與其名稱不匹配。
struct Attributes {
float3 positionOS : POSITION;
float2 baseUV : TEXCOORD0;
float2 lightMapUV : TEXCOORD1;
};
…
Varyings MetaPassVertex (Attributes input) {
Varyings output;
input.positionOS.xy =
input.lightMapUV * unity_LightmapST.xy + unity_LightmapST.zw;
output.positionCS = TransformWorldToHClip(input.positionOS);
output.baseUV = TransformBaseUV(input.baseUV);
return output;
}
我們?nèi)匀恍枰獙ο罂臻g頂點(diǎn)屬性作為輸入滑肉,因為著色器希望它存在包各。 事實上,除非OpenGL顯式地使用Z坐標(biāo)靶庙,否則它不會工作问畅。 我們將使用與Unity自己的元通道使用的相同的虛擬賦值,即input.positionOS.z > 0.0 ? FLT_MIN: 0.0
六荒。
input.positionOS.xy =
input.lightMapUV * unity_LightmapST.xy + unity_LightmapST.zw;
input.positionOS.z = input.positionOS.z > 0.0 ? FLT_MIN : 0.0;
4.5 漫反射率(Diffuse Reflectivity)
Meta Pass
可以用來生成不同的數(shù)據(jù)护姆。 請求的內(nèi)容通過bool4 unity_MetaFragmentControl
標(biāo)記向量來傳達(dá)。
bool4 unity_MetaFragmentControl;
如果設(shè)置了X標(biāo)記掏击,則請求漫反射卵皂,因此將其作為RGB分量的結(jié)果。 A分量應(yīng)該設(shè)置為1砚亭。
float4 meta = 0.0;
if (unity_MetaFragmentControl.x) {
meta = float4(brdf.diffuse, 1.0);
}
return meta;
這足以給反射光上色灯变,但Unity的元通道將結(jié)果提高了一些殴玛,通過增加一半的鏡面反射率和粗糙度進(jìn)行縮放。這背后的想法是強(qiáng)烈的高光但粗糙的材質(zhì)也傳遞一些間接的光添祸。
meta = float4(brdf.diffuse, 1.0);
meta.rgb += brdf.specular * brdf.roughness * 0.5;
在此之后滚粟,通過使用PositivePow
方法將結(jié)果提升到unity_OneOverOutputBoost
提供的功率,然后將其限制為unity_MaxOutputValue
刃泌,從而修改結(jié)果坦刀。
meta.rgb += brdf.specular * brdf.roughness * 0.5;
meta.rgb = min(
PositivePow(meta.rgb, unity_OneOverOutputBoost), unity_MaxOutputValue
);
這些值是作為浮點(diǎn)數(shù)提供的。
float unity_OneOverOutputBoost;
float unity_MaxOutputValue;
現(xiàn)在我們得到正確的間接照明顏色鲤遥,也可以將其應(yīng)用到GetLighting
。
float3 color = gi.diffuse * brdf.diffuse;
讓我們再次打開環(huán)境照明將亮度調(diào)回到1林艘。
最后盖奈,設(shè)置光源的模式為Mixed
。 這使它再次成為一個有這所有烘焙的間接漫反射光照的實時光狐援。
5. 自發(fā)光表面(Emissive Surfaces)
有些表面會發(fā)出自己的光钢坦,因此即使在沒有其他照明的情況下也能被看到。這可以通過簡單地在LitPassFragment
的末尾添加一些顏色來完成啥酱。 但這不是一個真正的光源爹凹,所以它不會影響其他表面。然而镶殷,這種效果有助于烘焙光照禾酱。
5.1 自發(fā)光(Emitted Light)
給Lit著色器添加兩個新屬性:一個自發(fā)光貼圖和自發(fā)光顏色,就像基礎(chǔ)貼圖和顏色一樣绘趋。我們將對兩者使用相同的坐標(biāo)轉(zhuǎn)換颤陶,因此我們不需要為自發(fā)光貼圖顯示單獨(dú)的控件。 它們可以通過賦予NoScaleOffset
屬性來隱藏陷遮。為了支持非常明亮的自發(fā)光滓走,添加HDR屬性到顏色中券腔。這使得通過面板配置亮度大于1的顏色成為可能鲤竹,顯示一個HDR顏色窗口而不是常規(guī)顏色窗口。
[NoScaleOffset] _EmissionMap("Emission", 2D) = "white" {}
[HDR] _EmissionColor("Emission", Color) = (0.0, 0.0, 0.0, 0.0)
將貼圖添加到LitInput
刺覆,并將自發(fā)光顏色添加到UnityPerMaterial
绽族。 然后添加一個GetEmission
函數(shù)姨涡,它的工作原理與GetBase
類似,只是它使用了另一個紋理和顏色项秉。
TEXTURE2D(_BaseMap);
TEXTURE2D(_EmissionMap);
SAMPLER(sampler_BaseMap);
UNITY_INSTANCING_BUFFER_START(UnityPerMaterial)
UNITY_DEFINE_INSTANCED_PROP(float4, _BaseMap_ST)
UNITY_DEFINE_INSTANCED_PROP(float4, _BaseColor)
UNITY_DEFINE_INSTANCED_PROP(float4, _EmissionColor)
…
UNITY_INSTANCING_BUFFER_END(UnityPerMaterial)
…
float3 GetEmission (float2 baseUV) {
float4 map = SAMPLE_TEXTURE2D(_EmissionMap, sampler_BaseMap, baseUV);
float4 color = UNITY_ACCESS_INSTANCED_PROP(UnityPerMaterial, _EmissionColor);
return map.rgb * color.rgb;
}
在LitPassFragment
的結(jié)尾添加自發(fā)光绣溜。
float3 color = GetLighting(surface, brdf, gi);
color += GetEmission(input.baseUV);
return float4(color, surface.alpha);
另外慷彤,在UnlitInput
中添加一個GetEmission
函數(shù)娄蔼。 在這個例子中怖喻,我們簡單地使它成為GetBase
的代理函數(shù)。 因此岁诉,如果你烘焙一個無光照的物體锚沸,它最終會發(fā)射完整的顏色。
float3 GetEmission (float2 baseUV) {
return GetBase(baseUV).rgb;
}
為了讓無光照的材質(zhì)發(fā)出非常明亮的光涕癣,我們可以將HDR屬性添加到無光照的基本顏色屬性中哗蜈。
[HDR] _BaseColor("Color", Color) = (1.0, 1.0, 1.0, 1.0)
最后,讓我們添加自發(fā)光顏色到PerObjectMaterialProperties
坠韩。在這種情況下距潘,我們可以通過配置字段的ColorUsage
屬性來允許HDR輸入。 我們需要傳遞兩個布爾值只搁。 第一個指示是否必須顯示alpha通道音比,這是我們不需要的。 第二個指示是否允許HDR值氢惋。
static int
baseColorId = Shader.PropertyToID("_BaseColor"),
cutoffId = Shader.PropertyToID("_Cutoff"),
metallicId = Shader.PropertyToID("_Metallic"),
smoothnessId = Shader.PropertyToID("_Smoothness"),
emissionColorId = Shader.PropertyToID("_EmissionColor");
…
[SerializeField, ColorUsage(false, true)]
Color emissionColor = Color.black;
…
void OnValidate () {
…
block.SetColor(emissionColorId, emissionColor);
GetComponent<Renderer>().SetPropertyBlock(block);
我在場景中添加了一些小的自發(fā)光立方體洞翩。 我讓它們服務(wù)于全局照明,并在Lightmap
中將其Scale
加倍焰望,以避免UV坐標(biāo)重疊的警告骚亿。當(dāng)頂點(diǎn)在光照貼圖中靠得太近時,就會發(fā)生這種情況熊赖,因此它們必須共享相同的texel来屠。
5.2 烘焙自發(fā)光(Baked Emission)
自發(fā)光通過單獨(dú)的通道烘烤震鹉。 當(dāng)unity_MetaFragmentControl
的Y
標(biāo)志被設(shè)置時的妖,MetaPassFragment
應(yīng)該返回發(fā)出的光,同樣將A組件設(shè)置為1足陨。
if (unity_MetaFragmentControl.x) {
…
}
else if (unity_MetaFragmentControl.y) {
meta = float4(GetEmission(input.baseUV), 1.0);
}
但這并不是自動發(fā)生的嫂粟。我們必須啟用每一種材質(zhì)的自發(fā)光烘焙。我們可以通過在PerObjectMaterialProperties.OnGUI
的編輯器上調(diào)用LightmapEmissionProperty
來顯示配置選項墨缘。
public override void OnGUI (
MaterialEditor materialEditor, MaterialProperty[] properties
) {
EditorGUI.BeginChangeCheck();
base.OnGUI(materialEditor, properties);
editor = materialEditor;
materials = materialEditor.targets;
this.properties = properties;
BakedEmission();
…
}
void BakedEmission () {
editor.LightmapEmissionProperty();
}
這會顯示一個Global Illumination
下拉菜單星虹,它最初被設(shè)置為None
。 除了名字镊讼,它只影響烘焙自發(fā)光宽涌。將它更改為Baked
會告訴光照貼圖為自發(fā)光使用一個單獨(dú)的通道。還有一個Realtime
選項蝶棋,但已棄用卸亮。
這仍然不能工作,因為Unity積極地試圖避免在烘焙時使用單獨(dú)的自發(fā)光通道玩裙。 如果材質(zhì)的自發(fā)光設(shè)置為零兼贸,那么它將被忽略段直。 然而,這并沒有考慮到每個對象的材質(zhì)屬性溶诞。當(dāng)自發(fā)光模式改變時鸯檬, 我們可以通過globalIlluminationFlags
禁用所有選定材質(zhì)的MaterialGlobalIlluminationFlags.EmissiveIsBlack
標(biāo)記。這意味著您應(yīng)該只在需要時啟用Baked選項螺垢。
void BakedEmission () {
EditorGUI.BeginChangeCheck();
editor.LightmapEmissionProperty();
if (EditorGUI.EndChangeCheck()) {
foreach (Material m in editor.targets) {
m.globalIlluminationFlags &=
~MaterialGlobalIlluminationFlags.EmissiveIsBlack;
}
}
}
6. 烘焙透明度(Baked Transparency)
也可以烘烤透明物體,但這需要一點(diǎn)額外的工作枉圃。
6.1 硬編碼屬性(Hard-Coded Properties)
不幸的是功茴,Unity的光照貼圖有一個硬編碼的透明方式。它會查看材質(zhì)的序列孽亲,以確定它是不透明的痊土、剪切的還是透明的。然后墨林,它通過將_MainTex
和_Color
屬性的alpha
分量相乘來確定透明度赁酝,并使用_Cutoff
屬性進(jìn)行透明度剪切。 我們的著色器有第三個方式旭等,但缺少前兩個酌呆。 目前唯一的方法是將預(yù)期的屬性添加到我們的著色器中,給它們HideInInspector
屬性搔耕,這樣它們就不會顯示在面板中隙袁。 Unity的SRP著色器也必須處理同樣的問題。
[HideInInspector] _MainTex("Texture for Lightmap", 2D) = "white" {}
[HideInInspector] _Color("Color for Lightmap", Color) = (0.5, 0.5, 0.5, 1.0)
6.2 拷貝屬性(Copying Properties)
我們必須確保_MainTex
屬性指向與_BaseMap
相同的紋理弃榨,并使用相同的UV轉(zhuǎn)換菩收。 這兩個顏色屬性也必須相同。 我們可以在CustomShaderGUI.OnGUIOnGUI
結(jié)束時調(diào)用一個新的CopyLightMappingProperties
方法來實現(xiàn)這一點(diǎn)鲸睛。 如果相關(guān)屬性存在娜饵,復(fù)制它們的值。
public override void OnGUI (
MaterialEditor materialEditor, MaterialProperty[] properties
) {
…
if (EditorGUI.EndChangeCheck()) {
SetShadowCasterPass();
CopyLightMappingProperties();
}
}
void CopyLightMappingProperties () {
MaterialProperty mainTex = FindProperty("_MainTex", properties, false);
MaterialProperty baseMap = FindProperty("_BaseMap", properties, false);
if (mainTex != null && baseMap != null) {
mainTex.textureValue = baseMap.textureValue;
mainTex.textureScaleAndOffset = baseMap.textureScaleAndOffset;
}
MaterialProperty color = FindProperty("_Color", properties, false);
MaterialProperty baseColor =
FindProperty("_BaseColor", properties, false);
if (color != null && baseColor != null) {
color.colorValue = baseColor.colorValue;
}
}
這也適用于裁剪材質(zhì)官辈。盡管有可能在MetaPassFragment
中裁剪片元是不需要的箱舞,因為透明度是單獨(dú)處理的。
不幸的是拳亿,這意味著烘焙的透明度只能依賴于單一的紋理晴股、顏色和裁剪屬性。 此外肺魁,光照貼圖只考慮材質(zhì)的屬性而忽略每個實例的屬性电湘。
7. 網(wǎng)格球體(Mesh Ball)
最后,我們?yōu)?code>MeshBall生成的實例添加了對全局照明的支持。 因為它的實例是在游戲模式下生成的寂呛,所以它們不能被烘焙怎诫,但如果多做一些工作,它們就可以通過光照探針接收烘焙的光照昧谊。
7.1 光照探針(Light Probes)
光照探針應(yīng)該通過調(diào)用DrawMeshInstanced
方法來使用,該方法需要另外5個參數(shù)酗捌。
using UnityEngine;
using UnityEngine.Rendering;
public class MeshBall : MonoBehaviour {
…
void Update () {
if (block == null) {
block = new MaterialPropertyBlock();
block.SetVectorArray(baseColorId, baseColors);
block.SetFloatArray(metallicId, metallic);
block.SetFloatArray(smoothnessId, smoothness);
}
Graphics.DrawMeshInstanced(
mesh, 0, material, matrices, 1023, block,
ShadowCastingMode.On, true, 0, null, LightProbeUsage.CustomProvided
);
}
我們必須手動為所有實例生成插值的光照探針呢诬,并將它們添加到材質(zhì)屬性塊。 這意味著我們在配置屬性塊時需要訪問實例的位置胖缤。 我們可以通過獲取它們的變換矩陣的最后一列來檢索它們尚镰,并將它們存儲在一個臨時數(shù)組中。
if (block == null) {
block = new MaterialPropertyBlock();
block.SetVectorArray(baseColorId, baseColors);
block.SetFloatArray(metallicId, metallic);
block.SetFloatArray(smoothnessId, smoothness);
var positions = new Vector3[1023];
for (int i = 0; i < matrices.Length; i++) {
positions[i] = matrices[i].GetColumn(3);
}
}
光照探針必須通過SphericalHarmonicsL2
類型的數(shù)組提供哪廓。它通過調(diào)用LightProbes.CalculateInterpolatedLightAndOcclusionProbes
來填充狗唉,使用位置和光探頭陣列作為參數(shù)。 還有第三個參數(shù)用于遮擋涡真,我們將使用null
分俯。
for (int i = 0; i < matrices.Length; i++) {
positions[i] = matrices[i].GetColumn(3);
}
var lightProbes = new SphericalHarmonicsL2[1023];
LightProbes.CalculateInterpolatedLightAndOcclusionProbes(
positions, lightProbes, null
);
我們不能在這里使用 List 嗎?
·
可以,有一個CalculateInterpolatedLightAndOcclusionProbes
的變體哆料。 但是我們只需要使用一次數(shù)據(jù)缸剪,所以在這種情況下,列表對我們沒有益處东亦。
之后杏节,我們可以通過CopySHCoefficientArraysFrom
將光照探針復(fù)制到block
中。
LightProbes.CalculateInterpolatedLightAndOcclusionProbes(
positions, lightProbes, null
);
block.CopySHCoefficientArraysFrom(lightProbes);
7.2 光照探針代理體(LPPV)
另一種方法是使用LPPV典阵。 這是有意義的奋渔,因為所有實例都存在于一個狹小的空間中。 這使我們不必計算和存儲插值的光照探針壮啊。此外嫉鲸,它使實例位置動畫化成為可能,而不必每幀提供新的光照探針數(shù)據(jù)歹啼,只要他們保持在探針體內(nèi)充坑。
添加一個LightProbeProxyVolume配置字段。 如果它在使用中染突,那么不要添加光照探針數(shù)據(jù)到塊中捻爷。然后將LightProbeUsage.UseProxyVolume
傳遞給DrawMeshInstanced
,而不是LightProbeUsage.CustomProvided
份企。 我們總是可以將體積作為附加參數(shù)提供也榄,即使它為null
且未被使用。
[SerializeField]
LightProbeProxyVolume lightProbeVolume = null;
…
void Update () {
if (block == null) {
…
if (!lightProbeVolume) {
var positions = new Vector3[1023];
…
block.CopySHCoefficientArraysFrom(lightProbes);
}
}
Graphics.DrawMeshInstanced(
mesh, 0, material, matrices, 1023, block,
ShadowCastingMode.On, true, 0, null,
lightProbeVolume ?
LightProbeUsage.UseProxyVolume : LightProbeUsage.CustomProvided,
lightProbeVolume
);
}
你可以添加一個LPPV組件到網(wǎng)格球或把它放在其他地方。 自定義邊界模式可用于LPPV所占用的世界空間區(qū)域甜紫。
下一個章節(jié)是 陰影遮罩(Shadow Masks)
- 文/潘曉璐 我一進(jìn)店門册养,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人压固,你說我怎么就攤上這事球拦。” “怎么了帐我?”我有些...
- 文/不壞的土叔 我叫張陵坎炼,是天一觀的道長。 經(jīng)常有香客問我拦键,道長点弯,這世上最難降的妖魔是什么? 我笑而不...
- 正文 為了忘掉前任矿咕,我火速辦了婚禮抢肛,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘碳柱。我一直安慰自己捡絮,他們只是感情好,可當(dāng)我...
- 文/花漫 我一把揭開白布莲镣。 她就那樣靜靜地躺著福稳,像睡著了一般。 火紅的嫁衣襯著肌膚如雪瑞侮。 梳的紋絲不亂的頭發(fā)上的圆,一...
- 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼阎抒!你這毒婦竟也來了酪我?” 一聲冷哼從身側(cè)響起,我...
- 正文 獨(dú)居荒郊野嶺守林人離奇死亡欺矫,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
- 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了掰担。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片汇陆。...
- 正文 年R本政府宣布,位于F島的核電站捏鱼,受9級特大地震影響执庐,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜导梆,卻給世界環(huán)境...
- 文/蒙蒙 一轨淌、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧看尼,春花似錦递鹉、人聲如沸。這莊子的主人今日做“春日...
- 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至狰域,卻和暖如春媳拴,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背兆览。 一陣腳步聲響...
- 正文 我出身青樓语婴,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子砰左,可洞房花燭夜當(dāng)晚...
推薦閱讀更多精彩內(nèi)容
- 本系列翻譯自Catlike Coding[https://catlikecoding.com/]的自定義渲染管線[...
- 這是一個有關(guān)于Unity-Custom SRP的基礎(chǔ)系列教程匿醒。 該教程涉及到一個簡單的渲染管線的搭建,各種光照和陰...
- 這一篇比較偏重于功能介紹缠导,具體的實例操作請參考其他文章:未完成 游戲場景中燈光照明的構(gòu)成 現(xiàn)實生活中的光線是有反射...
- 熱更新 在介紹tolua前廉羔,我們首先來了解一下在游戲開發(fā)中,熱更新的概念僻造。 熱更新是一種手游及App常用的更新方式...
- 移動平臺Unity3D 應(yīng)用性能優(yōu)化 文章比較長憋他,但是滿滿的是干貨。 一髓削、移動平臺硬件架構(gòu) 移動平臺無論是Andr...