Unity3D CustomSRP[譯].5.烘焙光[Baked Light]

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.

場景被一個簡單的混合模式的光以及一些自發(fā)光物體照明

(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è)置


那么實時全局照明呢?
.
它還烘焙平行光數(shù)據(jù)袒哥,這使得法線貼圖能夠影響入射的烘烤光缩筛。因為我們目前還不支持法線映射,所以沒有理由啟用它堡称。


1.2 靜態(tài)對象(Static Objects)

為了演示烘焙光照瞎抛,我創(chuàng)建了一個場景,用一個綠色的平面作為地面却紧,放置了一些盒子和球體桐臊,中間放了一個只有一面沒有墻壁的房間,所以它的內(nèi)部都會產(chǎn)生陰影啄寡。

房間黑暗的內(nèi)部

同樣的場景但房間沒有天花板

場景有一個簡單的平行光豪硅,其模式設(shè)置為混合。這告訴Unity它應(yīng)該烘焙這個光的間接照明挺物。除此之外懒浮,這盞燈仍然像普通的實時光一樣工作。

混合模式

我還將地面和所以立方體识藤,包括組成房間的立方體砚著,都納入到了烘焙過程。它們將是光線反射的對象痴昧,因此要變成間接的稽穆。這是通過啟用MeshRenderer組件的Contribute Global Illumination開關(guān)來實現(xiàn)的。啟用這一功能也會自動將它們的Receive Global Illumination模式切換到Lightmaps赶撰,這意味著到達(dá)它們表面的間接光會被烘焙到光照貼圖中舌镶。 您也可以通過從對象的Static下拉列表中啟用Contribute GI來啟用這個模式,或者使它完全靜態(tài)化豪娜。

啟用烘焙GI

一旦啟用餐胀,場景的照明將再次烘焙,假設(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;
}

LitPassLighting之前引用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中將DrawingSettingsperObjectData屬性設(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_DATAGI_VARYINGS_DATATRANSFER_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

光照貼圖坐標(biāo)

所有靜態(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;

轉(zhuǎn)換后的光照貼圖坐標(biāo)


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è)置為零來禁用它。 這使得我們能夠聚焦于單一方向的光待侵。

環(huán)境強(qiáng)度設(shè)置為0

注意丢早,房間內(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北戏。

使用LPPV


為什么在場景視圖中看不到探針负芋?
.
當(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_ProbeVolumeSizeInvunity_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_ProbeVolumeMinunity_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

對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文件中长踊。我們還將通過引入TransformBaseUVGetBase萍倡、GetCutoff身弊、GetMetallicGetSmoothness函數(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的所有通道中引用這個文件戴而,在LitSubShader塊的頂部添加一個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分井。 保留GetMetallicGetSmoothness函數(shù)车猬,并使它們返回0.0,表示一個非常平淡的漫反射表面尺锚。之后也給著色器一個HLSLINCLUDE塊珠闰。

        HLSLINCLUDE
        #include "../ShaderLibrary/Common.hlsl"
        #include "UnlitInput.hlsl"
        ENDHLSL

轉(zhuǎn)換UnlitPass,就像我們對LitPass做的那樣缩麸。 請注意铸磅,ShadowCasterPass對于兩個著色器都工作得很好赡矢,即使它以不同的輸入定義結(jié)束杭朱。


4.3 元通道模式(Meta Light Mode)

LitUnlit著色器添加一個新的通道,將LightMode設(shè)置為Meta吹散。 這個通道要求裁剪總是關(guān)閉的弧械,可以通過添加Cull Off來配置。 它將使用MetaPassVertexMetaPassFragment函數(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抖甘,ShadowsLight热鞍。我們只需要知道對象空間的位置和基礎(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;
適當(dāng)?shù)暮姹汗庹罩?/div>

讓我們再次打開環(huán)境照明將亮度調(diào)回到1林艘。

使用環(huán)境照明

最后盖奈,設(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)

自發(fā)光顏色設(shè)置為白色的材質(zhì)

將貼圖添加到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ā)光設(shè)置為HDR-黃色

我在場景中添加了一些小的自發(fā)光立方體洞翩。 我讓它們服務(wù)于全局照明,并在Lightmap中將其Scale加倍焰望,以避免UV坐標(biāo)重疊的警告骚亿。當(dāng)頂點(diǎn)在光照貼圖中靠得太近時,就會發(fā)生這種情況熊赖,因此它們必須共享相同的texel来屠。

自發(fā)光立方體;沒有環(huán)境光


5.2 烘焙自發(fā)光(Baked Emission)

自發(fā)光通過單獨(dú)的通道烘烤震鹉。 當(dāng)unity_MetaFragmentControlY標(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選項蝶棋,但已棄用卸亮。

自發(fā)光設(shè)置為烘焙

這仍然不能工作,因為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;
            }
        }
    }

烘焙的自發(fā)光喧务,有喝沒有平行光


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生成的實例添加了對全局照明的支持。 因為它的實例是在游戲模式下生成的寂呛,所以它們不能被烘焙怎诫,但如果多做一些工作,它們就可以通過光照探針接收烘焙的光照昧谊。

網(wǎng)格球體和全烘焙照明


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);

使用關(guān)照探針


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ū)域甜紫。

使用一個LPPV


下一個章節(jié)是 陰影遮罩(Shadow Masks)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末降宅,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子囚霸,更是在濱河造成了極大的恐慌腰根,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,284評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件拓型,死亡現(xiàn)場離奇詭異额嘿,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)劣挫,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,115評論 3 395
  • 文/潘曉璐 我一進(jìn)店門册养,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人压固,你說我怎么就攤上這事球拦。” “怎么了帐我?”我有些...
    開封第一講書人閱讀 164,614評論 0 354
  • 文/不壞的土叔 我叫張陵坎炼,是天一觀的道長。 經(jīng)常有香客問我拦键,道長点弯,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,671評論 1 293
  • 正文 為了忘掉前任矿咕,我火速辦了婚禮抢肛,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘碳柱。我一直安慰自己捡絮,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,699評論 6 392
  • 文/花漫 我一把揭開白布莲镣。 她就那樣靜靜地躺著福稳,像睡著了一般。 火紅的嫁衣襯著肌膚如雪瑞侮。 梳的紋絲不亂的頭發(fā)上的圆,一...
    開封第一講書人閱讀 51,562評論 1 305
  • 那天,我揣著相機(jī)與錄音半火,去河邊找鬼越妈。 笑死,一個胖子當(dāng)著我的面吹牛钮糖,可吹牛的內(nèi)容都是我干的梅掠。 我是一名探鬼主播酌住,決...
    沈念sama閱讀 40,309評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼阎抒!你這毒婦竟也來了酪我?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,223評論 0 276
  • 序言:老撾萬榮一對情侶失蹤且叁,失蹤者是張志新(化名)和其女友劉穎都哭,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體逞带,經(jīng)...
    沈念sama閱讀 45,668評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡欺矫,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,859評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了掰担。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片汇陆。...
    茶點(diǎn)故事閱讀 39,981評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡怒炸,死狀恐怖带饱,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情阅羹,我是刑警寧澤勺疼,帶...
    沈念sama閱讀 35,705評論 5 347
  • 正文 年R本政府宣布,位于F島的核電站捏鱼,受9級特大地震影響执庐,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜导梆,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,310評論 3 330
  • 文/蒙蒙 一轨淌、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧看尼,春花似錦递鹉、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,904評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至狰域,卻和暖如春媳拴,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背兆览。 一陣腳步聲響...
    開封第一講書人閱讀 33,023評論 1 270
  • 我被黑心中介騙來泰國打工屈溉, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人抬探。 一個月前我還...
    沈念sama閱讀 48,146評論 3 370
  • 正文 我出身青樓语婴,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子砰左,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,933評論 2 355

推薦閱讀更多精彩內(nèi)容