天天看點

unity中的球諧光照

unity3D的内置shader庫中定義了若幹工具函數,可以用來計算球諧光照。

unity3D使用了3階的伴随勒讓德多項式作為基函數,即l的最大取值為2。

直角坐标系下3階球諧函數的系數如表7-6所示 ,共9個,其中:

unity中的球諧光照
unity中的球諧光照

最終的光照函數就是上表中,每一項Yl,m和基函數系數Cl,m的疊加,又因為球諧光照所讨論的球面空間是在一個機關球空間中的,是以r項等于1。是以在考察的機關球面上的位置點(x,y,z)組成的向量也是一個機關化的向量。

在重建光照過程中 ,因為使用的光照顔色是RGB顔色,每一個分量都需要和這9個系數進行運算操作,并且因為每個光顔色分量的波長不同,是以對應的Cl,m也不同。是以需要27個數字去做這些操作,這27個數字存儲在7個float變量中,這7個float4變量在檔案中定義,由引擎在程式運作時傳遞進來。

  1. 球諧光照要用到的系數

    UnityShaderVariables.cginc

    定義如下:

// SH lighting environment
half4 unity_SHAr;
half4 unity_SHAg;
half4 unity_SHAb;
half4 unity_SHBr;
half4 unity_SHBg;
half4 unity_SHBb;
half4 unity_SHC;
           

unity_SHAr前三個分量對應表7-6中l=1時的各項Yl,m與紅色分量對應的Cl,m的乘積;最後一個分量則對應于l=0時Yl,m常數值與對應的Cl,m的乘積;

unity_SHAg對應于綠色分量;

unity_SHAb對應于藍色分量。

  1. SHEvalLinearL0L1函數
// normal should be normalized, w=1.0
half3 SHEvalLinearL0L1 (half4 normal)
{
    half3 x;
    // Linear (L1) + constant (L0) polynomial terms
    x.r = dot(unity_SHAr,normal);
    x.g = dot(unity_SHAg,normal);
    x.b = dot(unity_SHAb,normal);
    return x;
}
           

補充:因為normal.w的值為1,是以unity_SHAr最後一個分量和normal.w的乘積和傳入球面上的點的位置無關系。最後一項乘出來的結果是l=0時的多項式Y(m,l)的值。

而前三個分量與normal的乘積是l=1時的多項式Y(m,l)的值。兩者合并起來就是l=0和l=1時的累加重建效果。

實質就是把每個待計算的球面上的某個點傳遞給分别執行表中l=1時的各項多項式進行計算操作,得到各項的乘積之後,再疊加起來傳回。

  1. SHEvalLinearL2函數
// normal should be normalized, w=1.0
half3 SHEvalLinearL2 (half4 normal)
{
    half3 x1, x2;
    // 4 of the quadratic (L2) polynomials
    half4 vB = normal.xyzz * normal.yzzx;
    x1.r = dot(unity_SHBr,vB);
    x1.g = dot(unity_SHBg,vB);
    x1.b = dot(unity_SHBb,vB);

    // Final (5th) quadratic (L2) polynomial
    half vC = normal.x*normal.x - normal.y*normal.y;
    x2 = unity_SHC.rgb * vC;

    return x1 + x2;
}

           

函數SHEvalLinearL2的功能則是計算l=2時的各個對應值。

上面代碼中的half4 vB = normal.xyzz * normal.yzzx,就是構造出表7-6中l=2時左數前4項的多項式中的xy,yz,z平方,xz。變量 half vC = normal.xnormal.x - normal.ynormal.y就對應表7-6中l=2右數第一項中的x平方-y平方。

有了l=0,1,2時的光照結果的函數,便可以把他們組合起來,提供3階的球諧光照計算,函數ShadeSH9就用于實作該功能。

  1. ShadeSH9函數
// normal should be normalized, w=1.0
// output in active color space
half3 ShadeSH9 (half4 normal)
{
    // Linear + constant polynomial terms
    half3 res = SHEvalLinearL0L1 (normal);
    // Quadratic polynomials
    res += SHEvalLinearL2 (normal);=
	#ifdef UNITY_COLORSPACE_GAMMA
        res = LinearToGammaSpace (res);
	#endif
    return res;
}
           

函數ShadeSH9是将SHEvalLinearL0L1和SHEvalLinearL2兩個函數累加起來的結果。然後根據gamma空間的宏決定是否轉換到gamma空間。

  1. ShadeSH3Order函數
// OBSOLETE: for backwards compatibility with 5.0
half3 ShadeSH3Order(half4 normal)
{
    // Quadratic polynomials
    half3 res = SHEvalLinearL2 (normal);

	#ifdef UNITY_COLORSPACE_GAMMA
        res = LinearToGammaSpace (res);
	#endif
    return res;
}
           

ShadeSH3Order函數則是隻保留l=2時重建光照計算部分的ShadeSH9函數。這個是unity 5.x版本之前的函數,為了相容使用。後面的版本不再使用。

  1. ShadeSH12Order函數
// normal should be normalized, w=1.0
half3 ShadeSH12Order (half4 normal)
{
    // Linear + constant polynomial terms
    half3 res = SHEvalLinearL0L1 (normal);
	#ifdef UNITY_COLORSPACE_GAMMA
        res = LinearToGammaSpace (res);
	#endif
    return res;
}
           

ShadeSH12Order函數的功能是保留l=0,1時的重建光照計算部分的ShadeSH9函數的内容。

  1. SHEvalLinearL0L1_SampleProbeVolume函數
#if UNITY_LIGHT_PROBE_PROXY_VOLUME
// normal should be normalized, w=1.0
half3 SHEvalLinearL0L1_SampleProbeVolume (half4 normal, float3 worldPos)
{
    const float transformToLocal = unity_ProbeVolumeParams.y;
    const float texelSizeX = unity_ProbeVolumeParams.z;

    //The SH coefficients textures and probe occlusion are packed into 1 atlas.
    //-------------------------
    //| ShR | ShG | ShB | Occ |
    //-------------------------

    float3 position = (transformToLocal == 1.0f) ? mul(unity_ProbeVolumeWorldToObject, float4(worldPos, 1.0)).xyz : worldPos;
    float3 texCoord = (position - unity_ProbeVolumeMin.xyz) * unity_ProbeVolumeSizeInv.xyz;
    texCoord.x = texCoord.x * 0.25f;

    // We need to compute proper X coordinate to sample.
    // Clamp the coordinate otherwize we'll have leaking between RGB coefficients
    float texCoordX = clamp(texCoord.x, 0.5f * texelSizeX, 0.25f - 0.5f * texelSizeX);

    // sampler state comes from SHr (all SH textures share the same sampler)
    texCoord.x = texCoordX;
    half4 SHAr = UNITY_SAMPLE_TEX3D_SAMPLER(unity_ProbeVolumeSH, unity_ProbeVolumeSH, texCoord);

    texCoord.x = texCoordX + 0.25f;
    half4 SHAg = UNITY_SAMPLE_TEX3D_SAMPLER(unity_ProbeVolumeSH, unity_ProbeVolumeSH, texCoord);

    texCoord.x = texCoordX + 0.5f;
    half4 SHAb = UNITY_SAMPLE_TEX3D_SAMPLER(unity_ProbeVolumeSH, unity_ProbeVolumeSH, texCoord);

    // Linear + constant polynomial terms
    half3 x1;
    x1.r = dot(SHAr, normal);
    x1.g = dot(SHAg, normal);
    x1.b = dot(SHAb, normal);

    return x1;
}
#endif
           

Cg/HLSL代碼中聲明為Texture3D類型的紋理是一種異于一般二維的稱為立方體紋理(volume texture)的紋理。立體紋理是傳統二維紋理在邏輯上的擴充。二維紋理是一張簡單的位圖檔,用來給三維模型提供表面顔色值。而一個三維紋理可以認為是由多張二維紋理“堆疊而成”的,用于描述三維空間資料的圖檔。立體紋理通過三維紋理坐标進行通路。三維紋理可以有一系列細節級别,每一級都較上一級縮小1/2,如圖所示:

unity中的球諧光照

unity3D還提供了對立體紋理進行采樣操作的宏,依據使用不同的着色器編譯和目标平台,這個采樣操作宏對應着不同的實作。

如:

在預計算階段,光探針把空間某一點的球諧函數系數編碼進一個立體紋理中,在運作時再從立體紋理中采樣得到系數,然後根據該點的法線值重建出光照效果。SHEvalLinearL0L1_SampleProbeVolume函數就是實作該功能。如函數名所示,該函數從紋理中采樣得到的顔色資訊,其數值就是當l=1和l=0時的各項Yl,m與對應的Cl,m的乘積。

SHEvalLinearL0L1_SampleProbeVolume函數把對紋理進行采樣時使用的紋理映射坐标按R/G/B分段壓縮到長度為0.25的一個段中,在對顔色取樣時就需要重新計算出各R/G/B分量對應的紋理映射坐标,代碼中已經注釋如何計算。該計算方法可以參考下圖:

unity中的球諧光照
PBR

繼續閱讀