聊聊Enum类型的存储技巧和HDRP的渲染架构

Enum类型的存储技巧

前言

在HDRP里经常在管线代码,或者在Shader里面会看到有将若干个Enum存储在一个uint或者一个int的操作,然后通过用位运算判断Enum,所以在这里记录一下,水一篇博文。

Enum 枚举类型

在平时使用Enum的时候,直接用if判断
例如:

    public enum LightCategory
    {
        Punctual,
        Area,
        Env,
        Decal,
        LocalVolumetricFog, // WARNING: Currently lightlistbuild.compute assumes Local Volumetric Fog is the last element in the LightCategory enum. Do not append new LightCategory types after LocalVolumetricFog. TODO: Fix .compute code.
        Count
    }

    void Test(){
        LightCategory m_LightCategory=LightCategory.Punctual;
        if(m_LightCategory==LightCategory.Punctual){
            .....
        }
    }

如果说在同一个上下文的情况下,好几个Enum一并使用,就会多好多个Enum变量,不太方便传参,当然也可以写成一个Struct,存储若干个Enum。
但是没有这个必要,因为有点浪费内存,可以把这几个Enum塞进一个uint里面,(一个uint有32位,int31位存数值剩余1位存储符号)

作用一:重要性排序

接下来以HDRP的灯光SortKey来举个实际一点的例子

    public enum LightCategory
    {
        Punctual,
        Area,
        Env,
        Decal,
        LocalVolumetricFog, // WARNING: Currently lightlistbuild.compute assumes Local Volumetric Fog is the last element in the LightCategory enum. Do not append new LightCategory types after LocalVolumetricFog. TODO: Fix .compute code.
        Count
    }
    public enum GPULightType
    {
        Directional,
        Point,
        Spot,
        ProjectorPyramid,
        ProjectorBox,

        // AreaLight
        Tube, // Keep Line lights before Rectangle. This is needed because of a compiler bug (see LightLoop.hlsl)
        Rectangle,
        // Currently not supported in real time (just use for reference)
        Disc,
        // Sphere,
    };
    public enum LightVolumeType
    {
        Cone,
        Sphere,
        Box,
        Count
    }
    
    //Packs a sort key for a light
    public static uint PackLightSortKey(LightCategory lightCategory, GPULightType gpuLightType, LightVolumeType lightVolumeType, int lightIndex)
    {
        //We sort directional lights to be in the beginning of the list.
        //This ensures that we can access directional lights very easily after we sort them.
        uint isDirectionalMSB = gpuLightType == GPULightType.Directional ? 0u : 1u;//确保平行光在数组的最开端0<<31=0
        uint sortKey = (uint)isDirectionalMSB << 31 | (uint)lightCategory << 27 | (uint)gpuLightType << 22 | (uint)lightVolumeType << 17 | (uint)lightIndex;
        return sortKey;
    }

可以看到这里有三个Enum,这里的三个Enum外加一个lightIndex通过与运算全塞进到这个sortKey里面

与运算例子:
0000 1000 0000 0000 0000 0000 0000 0000 ||  (32位)
0000 0000 0000 0000 0000 0000 0000 0001     (32位)
等于:
0000 1000 0000 0000 0000 0000 0000 0001     (32位)

比较明显的一点,在英文的注释里说,希望通过isDirectionalMSB确保平行光在数组的最开端0<<31=0
而对这个SortKey数组排序的过程是在HDProcessedVisibleLightsBuilder.LightLoop.cs的Build函数的SortLightKeys()

    private void SortLightKeys()
    {
        using (new ProfilingScope(null, ProfilingSampler.Get(HDProfileId.SortVisibleLights)))
        {
            //Tunning against ps4 console,
            //32 items insertion sort has a workst case of 3 micro seconds.
            //200 non recursive merge sort has around 23 micro seconds.
            //From 200 and more, Radix sort beats everything.
            var sortSize = sortedLightCounts;
            //public int sortedLightCounts => m_ProcessVisibleLightCounts[(int)ProcessLightsCountSlots.ProcessedLights];
            //排序灯光数组长度,会根据实际处理了多少灯光的实体来排序数组(如果不出意外应该是m_SortKeys数组长度等于sortSize)
            if (sortSize <= 32)
                CoreUnsafeUtils.InsertionSort(m_SortKeys, sortSize);
            else if (m_Size <= 200)
                CoreUnsafeUtils.MergeSort(m_SortKeys, sortSize, ref m_SortSupportArray);
            else
                CoreUnsafeUtils.RadixSort(m_SortKeys, sortSize, ref m_SortSupportArray);
        }
    }

    private static unsafe void InsertionSort(uint* arr, int length)
    {
        for (int i = 0; i < length; ++i)
        {
            for (int j = i; j >= 1; --j)
            {
                if (arr[j] >= arr[j - 1])
                    break;

                var tmp = arr[j];
                arr[j] = arr[j - 1];
                arr[j - 1] = tmp;
            }
        }
    }
    private static unsafe void MergeSort(uint* array, uint* support, int length)
    {
        for (int k = 1; k < length; k *= 2)
        {
            for (int left = 0; left + k < length; left += k * 2)
            {
                int right = left + k;
                int rightend = right + k;
                if (rightend > length)
                    rightend = length;
                int m = left;
                int i = left;
                int j = right;
                while (i < right && j < rightend)
                {
                    if (array[i] <= array[j])
                    {
                        support[m] = array[i++];
                    }
                    else
                    {
                        support[m] = array[j++];
                    }
                    m++;
                }
                while (i < right)
                {
                    support[m] = array[i++];
                    m++;
                }
                while (j < rightend)
                {
                    support[m] = array[j++];
                    m++;
                }
                for (m = left; m < rightend; m++)
                {
                    array[m] = support[m];
                }
            }
        }
    }
    private static unsafe void RadixSort(uint* array, uint* support, int radixBits, int bitStates, int length)
    {
        uint mask = (uint)(bitStates - 1);
        CalculateRadixSortSupportArrays(bitStates, length, support, out uint* bucketIndices, out uint* bucketSizes, out uint* bucketPrefix, out uint* arrayOutput);

        int buckets = (sizeof(uint) * 8) / radixBits;
        uint* targetBuffer = arrayOutput;
        uint* inputBuffer = array;
        for (int b = 0; b < buckets; ++b)
        {
            int shift = b * radixBits;
            for (int s = 0; s < 3 * bitStates; ++s)
                bucketIndices[s] = 0;//bucketSizes and bucketPrefix get zeroed, since we walk 3x the bit states

            for (int i = 0; i < length; ++i)
                bucketSizes[((inputBuffer[i] >> shift) & mask)]++;

            for (int s = 1; s < bitStates; ++s)
                bucketPrefix[s] = bucketPrefix[s - 1] + bucketSizes[s - 1];

            for (int i = 0; i < length; ++i)
            {
                uint val = inputBuffer[i];
                uint bucket = (val >> shift) & mask;
                targetBuffer[bucketPrefix[bucket] + bucketIndices[bucket]++] = val;
            }

            uint* tmp = inputBuffer;
            inputBuffer = targetBuffer;
            targetBuffer = tmp;
        }
    }

这里是三种排序分别是归并排序,插入排序,桶排序,这里就不再展开说了,可以看到在插入排序里面\(arr[j] >= arr[j - 1]\)
也就是说只要位于前面的那个数组元素\(a[j-1]\)小于后面的那个元素\(a[j]\)就不用进行后面的互换位置的操作。
所以得出来的数组确实如之前的英文注释说的是从小到大的,所以上面的m_SortKeys数组排完序之后,平行光的光源会排在前面。
注:(uint isDirectionalMSB = m_GPULightType == GPULightType.Directional ? 0u : 1u;)
而其他标志位如:LightCategory,GPULightType,LightVolumeType也会按照大小进行排序
所以上面这里可以看到我们可以通过对存储在uint中,可以作重要性排序。

那么我们应该怎么去阅读这些位操作的代码呢?
我做了几个测试:

uint isDirectionalMSB = m_GPULightType == GPULightType.Directional ? 0u : 1u;//确保平行光在数组的最开端0<<31=0
uint sortKey = (uint)isDirectionalMSB << 31 | (uint)m_LightCategory << 27 | (uint)m_GPULightType << 22 | (uint)m_LightVolumeType << 17 | (uint)lightIndex;
Debug.Log("isDirectionalMSB:"+Convert.ToString((uint)isDirectionalMSB << 31,2));1
Debug.Log("m_LightCategory:"+Convert.ToString((uint)m_LightCategory << 27,2));4
Debug.Log("m_GPULightType:"+Convert.ToString((uint)m_GPULightType << 22,2));5
Debug.Log("m_LightVolumeType:"+Convert.ToString((uint)m_LightVolumeType << 17,2));5
Debug.Log(Convert.ToString(sortKey,2));

normal:
0000 0000 0000 0000 0000 0000 0000 0000

isDirectionalMSB:0
m_LightCategory:5 =Count 101
m_GPULightType:0 =directional
m_LightVolumeType:0 =Cone
index:1
0010 1000 0000 0000 0000 0000 0000 0000
0|010 1|000 0000 0000 0000 0000 0000 0001

isDirectionalMSB:0
m_LightCategory:5 =Count 101
m_GPULightType: [Num]1/7 = [Enum]Point/Disc = [Bit]1/111
m_LightVolumeType:0 =Cone
1|010 1|000 01|00 0000 0000 0000 0000 0001
1|010 1|001 11|00 0000 0000 0000 0000 0001

isDirectionalMSB:0
m_LightCategory:5 =Count 101
m_GPULightType:7 = directional
m_LightVolumeType:[Num]0/4 = [Enum]Cone/Count = [Bit]0/11
1|010 1|001 11|00 000|0 0000 0000 0000 0001
1|010 1|001 11|00 011|0 0000 0000 0000 0001

可以看到32位就是用来标识是不是平行光,所以左移了31位,而后面的GPULightType左移了27位,说明用了4位(27[左移的位数]+4+1=32,31-27=4)
使用如此类推,如果要关注一个uint的Flag的分布就只要看左移间隔了多少位。

作用二:Flag Mask(主要用来作为标识位作判断)

另外一种作用体比较明显的是在HDRP的Shader里面针对不同的MaterialFeature进行采样,填充参数,当然上面的SortKey最前面的那一位也是可以作为标识位来判断是不是平行光。

在说这个MaterialFeature之前先说一下HDRP管线架构的一个特性,就是对于不同光照模型(不同材质,Lit/Hair/Fabic,Eye)它们的采样要用到的函数都会不一样,为了做统一做抽象,共用同一套LightLoop函数,同一套Frag(前向渲染的ShaderPassForward,DepthOnly,DepthNormal....)都统一做出了规定,不同材质必需定义各自的SurfaceData,BSDFData,PreLightData...需要实现各自的ConvertSurfaceDataToBSDFData/GetPreLightData...
当然如果你要自己写Shader也是可以不用按照他的框架走的,只是说HDRP的这个框架已经把能公用的部分基本都抽离出来了,就不用每个Shader单独写LightLoop了。除非是NPR,需要高度自定义Lighting修改。

而这个MaterialFeature就位于SurfaceData
举个例子:Lit.cs.hlsl

//
// This file was automatically generated. Please don't edit by hand. Execute Editor command [ Edit > Rendering > Generate Shader Includes ] instead
//

#ifndef LIT_CS_HLSL
#define LIT_CS_HLSL
//
// UnityEngine.Rendering.HighDefinition.Lit+MaterialFeatureFlags:  static fields
//
#define MATERIALFEATUREFLAGS_LIT_STANDARD (1)
#define MATERIALFEATUREFLAGS_LIT_SPECULAR_COLOR (2)
#define MATERIALFEATUREFLAGS_LIT_SUBSURFACE_SCATTERING (4)
#define MATERIALFEATUREFLAGS_LIT_TRANSMISSION (8)
#define MATERIALFEATUREFLAGS_LIT_ANISOTROPY (16)
#define MATERIALFEATUREFLAGS_LIT_IRIDESCENCE (32)
#define MATERIALFEATUREFLAGS_LIT_CLEAR_COAT (64)

.....
struct SurfaceData
{
    uint materialFeatures;
    real3 baseColor;
    ...
};

struct BSDFData
{
    uint materialFeatures;
    real3 diffuseColor;
    ....
};

可以看到这里前面有一段注释,大概的意思是说这个hlsl文件是由脚本生成的,不要手动修改,而这个hlsl文件是由Lit.cs生成的。

    partial class Lit : RenderPipelineMaterial
    {
        // Currently we have only one materialId (Standard GGX), so it is not store in the GBuffer and we don't test for it

        // If change, be sure it match what is done in Lit.hlsl: MaterialFeatureFlagsFromGBuffer
        // Material bit mask must match the size define LightDefinitions.s_MaterialFeatureMaskFlags value
        [GenerateHLSL(PackingRules.Exact)]
        public enum MaterialFeatureFlags
        {
            LitStandard = 1 << 0,   // For material classification we need to identify that we are indeed use as standard material, else we are consider as sky/background element
            LitSpecularColor = 1 << 1,   // LitSpecularColor is not use statically but only dynamically
            LitSubsurfaceScattering = 1 << 2,
            LitTransmission = 1 << 3,
            LitAnisotropy = 1 << 4,
            LitIridescence = 1 << 5,
            LitClearCoat = 1 << 6
        };

        //-----------------------------------------------------------------------------
        // SurfaceData
        //-----------------------------------------------------------------------------

        // Main structure that store the user data (i.e user input of master node in material graph)
        [GenerateHLSL(PackingRules.Exact, false, false, true, 1000)]
        public struct SurfaceData
        {
            [SurfaceDataAttributes("Material Features")]
            public uint materialFeatures;

            // Standard
            [MaterialSharedPropertyMapping(MaterialSharedProperty.Albedo)]
            [SurfaceDataAttributes("Base Color", false, true, FieldPrecision.Real)]
            public Vector3 baseColor;
            ....
        };
        
        [GenerateHLSL(PackingRules.Exact, false, false, true, 1050)]
        public struct BSDFData
        {
            public uint materialFeatures;

            [SurfaceDataAttributes("", false, true, FieldPrecision.Real)]
            public Vector3 diffuseColor;
            ...
        };

可以看到这两段代码结构体基本差不多。MaterialFeatureFlags生成了 MATERIALFEATUREFLAGS_LIT_XXXXX的MeterialFeatureFlag宏定义,将Standarad定义成1,SpecularColor定义成2,SSS定义成4...跟上面的Enum定义一致。然后再Shader代码里面是怎么判断当前的光照模型执行对应的采样函数呢?
答案就是对materialFeatures作位运算

在Lit.shader我们能够看到有以下Shader Feature的宏

    // MaterialFeature are used as shader feature to allow compiler to optimize properly
    #pragma shader_feature_local_fragment _MATERIAL_FEATURE_SUBSURFACE_SCATTERING
    #pragma shader_feature_local_fragment _MATERIAL_FEATURE_TRANSMISSION
    #pragma shader_feature_local_fragment _MATERIAL_FEATURE_ANISOTROPY
    #pragma shader_feature_local_fragment _MATERIAL_FEATURE_CLEAR_COAT
    #pragma shader_feature_local_fragment _MATERIAL_FEATURE_IRIDESCENCE
    #pragma shader_feature_local_fragment _MATERIAL_FEATURE_SPECULAR_COLOR
    #pragma shader_feature_local_raytracing _MATERIAL_FEATURE_SUBSURFACE_SCATTERING
    #pragma shader_feature_local_raytracing _MATERIAL_FEATURE_TRANSMISSION
    #pragma shader_feature_local_raytracing _MATERIAL_FEATURE_ANISOTROPY
    #pragma shader_feature_local_raytracing _MATERIAL_FEATURE_CLEAR_COAT
    #pragma shader_feature_local_raytracing _MATERIAL_FEATURE_IRIDESCENCE
    #pragma shader_feature_local_raytracing _MATERIAL_FEATURE_SPECULAR_COLOR

在LitData.hlsl的GetSurfaceAndBuiltinData中的GetSurfaceData(在LitDataIndividualLayer.hlsl的ADD_IDX(GetSurfaceData))
(说个题外话,HDRP的多层材质[LayeredLitData]是用这个ADD_IDX宏来与普通的Lit共用一份的GetSurfaceData函数定义。实际在使用的时候是调用GetSurfaceData0/1/2/3)

LitDataIndividualLayer.hlsl
// This part of the code is not used in case of layered shader but we keep the same macro system for simplicity
#if !defined(LAYERED_LIT_SHADER)

// These static material feature allow compile time optimization
surfaceData.materialFeatures = MATERIALFEATUREFLAGS_LIT_STANDARD;

#ifdef _MATERIAL_FEATURE_SUBSURFACE_SCATTERING
    surfaceData.materialFeatures |= MATERIALFEATUREFLAGS_LIT_SUBSURFACE_SCATTERING;
#endif
#ifdef _MATERIAL_FEATURE_TRANSMISSION
    surfaceData.materialFeatures |= MATERIALFEATUREFLAGS_LIT_TRANSMISSION;
#endif
#ifdef _MATERIAL_FEATURE_ANISOTROPY
    surfaceData.materialFeatures |= MATERIALFEATUREFLAGS_LIT_ANISOTROPY;
#endif
#ifdef _MATERIAL_FEATURE_CLEAR_COAT
    surfaceData.materialFeatures |= MATERIALFEATUREFLAGS_LIT_CLEAR_COAT;
#endif
#ifdef _MATERIAL_FEATURE_IRIDESCENCE
    surfaceData.materialFeatures |= MATERIALFEATUREFLAGS_LIT_IRIDESCENCE;
#endif
#ifdef _MATERIAL_FEATURE_SPECULAR_COLOR
    surfaceData.materialFeatures |= MATERIALFEATUREFLAGS_LIT_SPECULAR_COLOR;
#endif
==============================

(LayeredLitData.hlsl)
// Layered shader support SSS and Transmission features
surfaceData.materialFeatures = MATERIALFEATUREFLAGS_LIT_STANDARD;

#ifdef _MATERIAL_FEATURE_SUBSURFACE_SCATTERING
    surfaceData.materialFeatures |= MATERIALFEATUREFLAGS_LIT_SUBSURFACE_SCATTERING;
#endif
#ifdef _MATERIAL_FEATURE_TRANSMISSION
    surfaceData.materialFeatures |= MATERIALFEATUREFLAGS_LIT_TRANSMISSION;
#endif

可以看到上面的materialFeature赋值是普通Lit的Shader用的#if !defined(LAYERED_LIT_SHADER)
下面的则是分层材质使用的,不过只支持SSS和透射材质
通过这里的与运算,把对应的位设置成1,然后ConvertSurfaceDataToBSDFData/GetPreLightData中只需要判断HasFlag(surfaceData.materialFeatures, MATERIALFEATUREFLAGS_LIT_XXXXX)执行对应的函数。

(SRP.Core-Common.hlsl)
// Simple function to test a bitfield
bool HasFlag(uint bitfield, uint flag)
{
    return (bitfield & flag) != 0;
}

如果光照模型跟标准的LightLoop要有自定义的修改就需要自己定义类似操作,用宏控制,让LightLoop插入自定义修改函数

(Lit.hlsl)
#define MODIFY_BAKED_DIFFUSE_LIGHTING

// This function allow to modify the content of (back) baked diffuse lighting when we gather builtinData
// This is use to apply lighting model specific code, like pre-integration, transmission etc...
// It is up to the lighting model implementer to chose if the modification are apply here or in PostEvaluateBSDF
void ModifyBakedDiffuseLighting(float3 V, PositionInputs posInput, PreLightData preLightData, BSDFData bsdfData, inout BuiltinData builtinData)
{
    // In case of deferred, all lighting model operation are done before storage in GBuffer, as we store emissive with bakeDiffuseLighting

    // Add GI transmission contribution to bakeDiffuseLighting, we then drop backBakeDiffuseLighting (i.e it is not used anymore, this save VGPR in forward and in deferred we can't store it anyway)
    if (HasFlag(bsdfData.materialFeatures, MATERIALFEATUREFLAGS_LIT_TRANSMISSION))
    {
        builtinData.bakeDiffuseLighting += builtinData.backBakeDiffuseLighting * bsdfData.transmittance;
    }

    // For SSS we need to take into account the state of diffuseColor
    if (HasFlag(bsdfData.materialFeatures, MATERIALFEATUREFLAGS_LIT_SUBSURFACE_SCATTERING))
    {
        bsdfData.diffuseColor = GetModifiedDiffuseColorForSSS(bsdfData);
    }

    // Premultiply (back) bake diffuse lighting information with DisneyDiffuse pre-integration
    // Note: When baking reflection probes, we approximate the diffuse with the fresnel0
    builtinData.bakeDiffuseLighting *= preLightData.diffuseFGD * GetDiffuseOrDefaultColor(bsdfData, _ReplaceDiffuseForIndirect).rgb;
}


(LightLoop.hlsl)
#ifdef MODIFY_BAKED_DIFFUSE_LIGHTING
#ifdef DEBUG_DISPLAY
            // When the lux meter is enabled, we don't want the albedo of the material to modify the diffuse baked lighting
            if (_DebugLightingMode != DEBUGLIGHTINGMODE_LUX_METER)
#endif
                ModifyBakedDiffuseLighting(V, posInput, preLightData, bsdfData, tempBuiltinData);
#endif

同样的在HDRP里面Lit.hlsl有一个静态常量uint数组kFeatureVariantFlags,长度定义为NUM_FEATURE_VARIANTS,而NUM_FEATURE_VARIANTS这个宏的定义也同样是用脚本生成的。

(LightLoop.cs)
[GenerateHLSL]
internal enum LightFeatureFlags
{
    // Light bit mask must match LightDefinitions.s_LightFeatureMaskFlags value
    Punctual = 1 << 12,
    Area = 1 << 13,
    Directional = 1 << 14,
    Env = 1 << 15,
    Sky = 1 << 16,
    SSRefraction = 1 << 17,
    SSReflection = 1 << 18,
    // If adding more light be sure to not overflow LightDefinitions.s_LightFeatureMaskFlags
}

[GenerateHLSL]
class LightDefinitions
{
    public static int s_MaxNrBigTileLightsPlusOne = 512;      // may be overkill but the footprint is 2 bits per pixel using uint16.
    public static float s_ViewportScaleZ = 1.0f;
    public static int s_UseLeftHandCameraSpace = 1;

    public static int s_TileSizeFptl = 16;
    public static int s_TileSizeClustered = 32;
    public static int s_TileSizeBigTile = 64;

    // Tile indexing constants for indirect dispatch deferred pass : [2 bits for eye index | 15 bits for tileX | 15 bits for tileY]
    public static int s_TileIndexMask = 0x7FFF;
    public static int s_TileIndexShiftX = 0;
    public static int s_TileIndexShiftY = 15;
    public static int s_TileIndexShiftEye = 30;

    // feature variants
    public static int s_NumFeatureVariants = 29;

    // light list limits
    public static int s_LightListMaxCoarseEntries = 64;
    public static int s_LightListMaxPrunedEntries = 24;
    public static int s_LightClusterMaxCoarseEntries = 128;

    // Following define the maximum number of bits use in each feature category.
    public static uint s_LightFeatureMaskFlags = 0xFFF000;
    public static uint s_LightFeatureMaskFlagsOpaque = 0xFFF000 & ~((uint)LightFeatureFlags.SSRefraction); // Opaque don't support screen space refraction
    public static uint s_LightFeatureMaskFlagsTransparent = 0xFFF000 & ~((uint)LightFeatureFlags.SSReflection); // Transparent don't support screen space reflection
    public static uint s_MaterialFeatureMaskFlags = 0x000FFF;   // don't use all bits just to be safe from signed and/or float conversions :/

    // Screen space shadow flags
    public static uint s_RayTracedScreenSpaceShadowFlag = 0x1000;
    public static uint s_ScreenSpaceColorShadowFlag = 0x100;
    public static uint s_InvalidScreenSpaceShadow = 0xff;
    public static uint s_ScreenSpaceShadowIndexMask = 0xff;
}

对应生成的hlsl文件

#define LIGHTFEATUREFLAGS_PUNCTUAL (4096)
#define LIGHTFEATUREFLAGS_AREA (8192)
#define LIGHTFEATUREFLAGS_DIRECTIONAL (16384)
#define LIGHTFEATUREFLAGS_ENV (32768)
#define LIGHTFEATUREFLAGS_SKY (65536)
#define LIGHTFEATUREFLAGS_SSREFRACTION (131072)
#define LIGHTFEATUREFLAGS_SSREFLECTION (262144)

....
#define NUM_FEATURE_VARIANTS (29)
....

说到这个数组是用来干什么用的,先看看这个数组长啥样

// Combination need to be define in increasing "comlexity" order as define by FeatureFlagsToTileVariant
static const uint kFeatureVariantFlags[NUM_FEATURE_VARIANTS] =
{
    // Precomputed illumination (no dynamic lights) with standard
    /*  0 */ LIGHTFEATUREFLAGS_SKY | LIGHTFEATUREFLAGS_ENV | LIGHTFEATUREFLAGS_SSREFLECTION | MATERIALFEATUREFLAGS_LIT_STANDARD,
    // Precomputed illumination (no dynamic lights) with standard, SSS and transmission
    /*  1 */ LIGHTFEATUREFLAGS_SKY | LIGHTFEATUREFLAGS_ENV | LIGHTFEATUREFLAGS_SSREFLECTION | MATERIALFEATUREFLAGS_LIT_SUBSURFACE_SCATTERING | MATERIALFEATUREFLAGS_LIT_TRANSMISSION | MATERIALFEATUREFLAGS_LIT_STANDARD,
    // Precomputed illumination (no dynamic lights) for all material types
    /*  2 */ LIGHTFEATUREFLAGS_SKY | LIGHTFEATUREFLAGS_ENV | LIGHTFEATUREFLAGS_SSREFLECTION | MATERIAL_FEATURE_MASK_FLAGS,

    /*  3 */ LIGHTFEATUREFLAGS_SKY | LIGHTFEATUREFLAGS_DIRECTIONAL | LIGHTFEATUREFLAGS_PUNCTUAL | MATERIALFEATUREFLAGS_LIT_STANDARD,
    /*  4 */ LIGHTFEATUREFLAGS_SKY | LIGHTFEATUREFLAGS_DIRECTIONAL | LIGHTFEATUREFLAGS_AREA | MATERIALFEATUREFLAGS_LIT_STANDARD,
    /*  5 */ LIGHTFEATUREFLAGS_SKY | LIGHTFEATUREFLAGS_DIRECTIONAL | LIGHTFEATUREFLAGS_ENV | LIGHTFEATUREFLAGS_SSREFLECTION | MATERIALFEATUREFLAGS_LIT_STANDARD,
    /*  6 */ LIGHTFEATUREFLAGS_SKY | LIGHTFEATUREFLAGS_DIRECTIONAL | LIGHTFEATUREFLAGS_PUNCTUAL | LIGHTFEATUREFLAGS_ENV | LIGHTFEATUREFLAGS_SSREFLECTION | MATERIALFEATUREFLAGS_LIT_STANDARD,
    /*  7 */ LIGHT_FEATURE_MASK_FLAGS_OPAQUE | MATERIALFEATUREFLAGS_LIT_STANDARD,

    // Standard with SSS and Transmission
    /*  8 */ LIGHTFEATUREFLAGS_SKY | LIGHTFEATUREFLAGS_DIRECTIONAL | LIGHTFEATUREFLAGS_PUNCTUAL | MATERIALFEATUREFLAGS_LIT_SUBSURFACE_SCATTERING | MATERIALFEATUREFLAGS_LIT_TRANSMISSION | MATERIALFEATUREFLAGS_LIT_STANDARD,
    /*  9 */ LIGHTFEATUREFLAGS_SKY | LIGHTFEATUREFLAGS_DIRECTIONAL | LIGHTFEATUREFLAGS_AREA | MATERIALFEATUREFLAGS_LIT_SUBSURFACE_SCATTERING | MATERIALFEATUREFLAGS_LIT_TRANSMISSION | MATERIALFEATUREFLAGS_LIT_STANDARD,
    /* 10 */ LIGHTFEATUREFLAGS_SKY | LIGHTFEATUREFLAGS_DIRECTIONAL | LIGHTFEATUREFLAGS_ENV | LIGHTFEATUREFLAGS_SSREFLECTION | MATERIALFEATUREFLAGS_LIT_SUBSURFACE_SCATTERING | MATERIALFEATUREFLAGS_LIT_TRANSMISSION | MATERIALFEATUREFLAGS_LIT_STANDARD,
    /* 11 */ LIGHTFEATUREFLAGS_SKY | LIGHTFEATUREFLAGS_DIRECTIONAL | LIGHTFEATUREFLAGS_PUNCTUAL | LIGHTFEATUREFLAGS_ENV | LIGHTFEATUREFLAGS_SSREFLECTION | MATERIALFEATUREFLAGS_LIT_SUBSURFACE_SCATTERING | MATERIALFEATUREFLAGS_LIT_TRANSMISSION | MATERIALFEATUREFLAGS_LIT_STANDARD,
    /* 12 */ LIGHT_FEATURE_MASK_FLAGS_OPAQUE | MATERIALFEATUREFLAGS_LIT_SUBSURFACE_SCATTERING | MATERIALFEATUREFLAGS_LIT_TRANSMISSION | MATERIALFEATUREFLAGS_LIT_STANDARD,

    // Anisotropy
    /* 13 */ LIGHTFEATUREFLAGS_SKY | LIGHTFEATUREFLAGS_DIRECTIONAL | LIGHTFEATUREFLAGS_PUNCTUAL | MATERIALFEATUREFLAGS_LIT_ANISOTROPY | MATERIALFEATUREFLAGS_LIT_STANDARD,
    /* 14 */ LIGHTFEATUREFLAGS_SKY | LIGHTFEATUREFLAGS_DIRECTIONAL | LIGHTFEATUREFLAGS_AREA | MATERIALFEATUREFLAGS_LIT_ANISOTROPY | MATERIALFEATUREFLAGS_LIT_STANDARD,
    /* 15 */ LIGHTFEATUREFLAGS_SKY | LIGHTFEATUREFLAGS_DIRECTIONAL | LIGHTFEATUREFLAGS_ENV | LIGHTFEATUREFLAGS_SSREFLECTION | MATERIALFEATUREFLAGS_LIT_ANISOTROPY | MATERIALFEATUREFLAGS_LIT_STANDARD,
    /* 16 */ LIGHTFEATUREFLAGS_SKY | LIGHTFEATUREFLAGS_DIRECTIONAL | LIGHTFEATUREFLAGS_PUNCTUAL | LIGHTFEATUREFLAGS_ENV | LIGHTFEATUREFLAGS_SSREFLECTION | MATERIALFEATUREFLAGS_LIT_ANISOTROPY | MATERIALFEATUREFLAGS_LIT_STANDARD,
    /* 17 */ LIGHT_FEATURE_MASK_FLAGS_OPAQUE | MATERIALFEATUREFLAGS_LIT_ANISOTROPY | MATERIALFEATUREFLAGS_LIT_STANDARD,

    // Standard with clear coat
    /* 18 */ LIGHTFEATUREFLAGS_SKY | LIGHTFEATUREFLAGS_DIRECTIONAL | LIGHTFEATUREFLAGS_PUNCTUAL | MATERIALFEATUREFLAGS_LIT_CLEAR_COAT | MATERIALFEATUREFLAGS_LIT_STANDARD,
    /* 19 */ LIGHTFEATUREFLAGS_SKY | LIGHTFEATUREFLAGS_DIRECTIONAL | LIGHTFEATUREFLAGS_AREA | MATERIALFEATUREFLAGS_LIT_CLEAR_COAT | MATERIALFEATUREFLAGS_LIT_STANDARD,
    /* 20 */ LIGHTFEATUREFLAGS_SKY | LIGHTFEATUREFLAGS_DIRECTIONAL | LIGHTFEATUREFLAGS_ENV | LIGHTFEATUREFLAGS_SSREFLECTION | MATERIALFEATUREFLAGS_LIT_CLEAR_COAT | MATERIALFEATUREFLAGS_LIT_STANDARD,
    /* 21 */ LIGHTFEATUREFLAGS_SKY | LIGHTFEATUREFLAGS_DIRECTIONAL | LIGHTFEATUREFLAGS_PUNCTUAL | LIGHTFEATUREFLAGS_ENV | LIGHTFEATUREFLAGS_SSREFLECTION | MATERIALFEATUREFLAGS_LIT_CLEAR_COAT | MATERIALFEATUREFLAGS_LIT_STANDARD,
    /* 22 */ LIGHT_FEATURE_MASK_FLAGS_OPAQUE | MATERIALFEATUREFLAGS_LIT_CLEAR_COAT | MATERIALFEATUREFLAGS_LIT_STANDARD,

    // Standard with Iridescence
    /* 23 */ LIGHTFEATUREFLAGS_SKY | LIGHTFEATUREFLAGS_DIRECTIONAL | LIGHTFEATUREFLAGS_PUNCTUAL | MATERIALFEATUREFLAGS_LIT_IRIDESCENCE | MATERIALFEATUREFLAGS_LIT_STANDARD,
    /* 24 */ LIGHTFEATUREFLAGS_SKY | LIGHTFEATUREFLAGS_DIRECTIONAL | LIGHTFEATUREFLAGS_AREA | MATERIALFEATUREFLAGS_LIT_IRIDESCENCE | MATERIALFEATUREFLAGS_LIT_STANDARD,
    /* 25 */ LIGHTFEATUREFLAGS_SKY | LIGHTFEATUREFLAGS_DIRECTIONAL | LIGHTFEATUREFLAGS_ENV | LIGHTFEATUREFLAGS_SSREFLECTION | MATERIALFEATUREFLAGS_LIT_IRIDESCENCE | MATERIALFEATUREFLAGS_LIT_STANDARD,
    /* 26 */ LIGHTFEATUREFLAGS_SKY | LIGHTFEATUREFLAGS_DIRECTIONAL | LIGHTFEATUREFLAGS_PUNCTUAL | LIGHTFEATUREFLAGS_ENV | LIGHTFEATUREFLAGS_SSREFLECTION | MATERIALFEATUREFLAGS_LIT_IRIDESCENCE | MATERIALFEATUREFLAGS_LIT_STANDARD,
    /* 27 */ LIGHT_FEATURE_MASK_FLAGS_OPAQUE | MATERIALFEATUREFLAGS_LIT_IRIDESCENCE | MATERIALFEATUREFLAGS_LIT_STANDARD,

    /* 28 */ LIGHT_FEATURE_MASK_FLAGS_OPAQUE | MATERIAL_FEATURE_MASK_FLAGS, // Catch all case with MATERIAL_FEATURE_MASK_FLAGS is needed in case we disable material classification
};

uint FeatureFlagsToTileVariant(uint featureFlags)
{
    for (int i = 0; i < NUM_FEATURE_VARIANTS; i++)
    {
        if ((featureFlags & kFeatureVariantFlags[i]) == featureFlags)
            return i;
    }
    return NUM_FEATURE_VARIANTS - 1;
}

#ifdef USE_INDIRECT

uint TileVariantToFeatureFlags(uint variant, uint tileIndex)
{
    if (variant == NUM_FEATURE_VARIANTS - 1)
    {
        // We don't have any compile-time feature information.
        // Therefore, we load the feature classification data at runtime to avoid
        // entering every single branch based on feature flags.
        return g_TileFeatureFlags[tileIndex];
    }
    else
    {
        // Return the compile-time feature flags.
        return kFeatureVariantFlags[variant];
    }
}
#endif // USE_INDIRECT

如果你接着搜索FeatureFlagsToTileVariant和TileVariantToFeatureFlags其实是能够看出来跟HDRP的DefferLighting有关系的。
未完待续

上一篇:matplotlib库(4)


下一篇:Azure Front Door添加自定义域名