美文网首页
Unity自定义SRP(九):点光和聚光

Unity自定义SRP(九):点光和聚光

作者: Dragon_boy | 来源:发表于2021-01-18 18:08 被阅读0次

    https://catlikecoding.com/unity/tutorials/custom-srp/point-and-spot-lights/

    1 点光

    1.1 其它灯光数据

    ​ 除平行光外,我们至多模拟64个其它类型的光源。Lighting.cs中:

        const int maxDirLightCount = 4, maxOtherLightCount = 64;
    

    ​ 我们需要将其它光源的数量、颜色和位置等信息送往GPU:

        static int
            otherLightCountId = Shader.PropertyToID("_OtherLightCount"),
            otherLightColorsId = Shader.PropertyToID("_OtherLightColors"),
            otherLightPositionsId = Shader.PropertyToID("_OtherLightPositions");
    
        static Vector4[]
            otherLightColors = new Vector4[maxOtherLightCount],
            otherLightPositions = new Vector4[maxOtherLightCount];
    

    ​ 在SetupLights中,确保只有有光源的时候才将数据送往GPU:

        void SetupLights () 
        {
            NativeArray<VisibleLight> visibleLights = cullingResults.visibleLights;
            int dirLightCount = 0, otherLightCount = 0;
            for (int i = 0; i < visibleLights.Length; i++) 
            {
                …
            }
    
            buffer.SetGlobalInt(dirLightCountId, dirLightCount);
            if (dirLightCount > 0) 
            {
                buffer.SetGlobalVectorArray(dirLightColorsId, dirLightColors);
                buffer.SetGlobalVectorArray(dirLightDirectionsId, dirLightDirections);
                buffer.SetGlobalVectorArray(dirLightShadowDataId, dirLightShadowData);
            }
    
            buffer.SetGlobalInt(otherLightCountId, otherLightCount);
            if (otherLightCount > 0) 
            {
                buffer.SetGlobalVectorArray(otherLightColorsId, otherLightColors);
                buffer.SetGlobalVectorArray(
                    otherLightPositionsId, otherLightPositions
                );
            }
        }
    

    ​ shader层面,在Light.hlsl中定义相应的变量:

    #define MAX_DIRECTIONAL_LIGHT_COUNT 4
    #define MAX_OTHER_LIGHT_COUNT 64
    
    CBUFFER_START(_CustomLight)
        int _DirectionalLightCount;
        float4 _DirectionalLightColors[MAX_DIRECTIONAL_LIGHT_COUNT];
        float4 _DirectionalLightDirections[MAX_DIRECTIONAL_LIGHT_COUNT];
        float4 _DirectionalLightShadowData[MAX_DIRECTIONAL_LIGHT_COUNT];
    
        int _OtherLightCount;
        float4 _OtherLightColors[MAX_OTHER_LIGHT_COUNT];
        float4 _OtherLightPositions[MAX_OTHER_LIGHT_COUNT];
    CBUFFER_END
    

    ​ 定义一个获取其它灯光数量的方法:

    int GetOtherLightCount () 
    {
        return _OtherLightCount;
    }
    

    1.2 初始化点光

    ​ 在Lighting.cs中创建一个SetupPointLight方法,初始化一个点光。其位置可由局部到世界转换矩阵的第4列得到:

        void SetupPointLight (int index, ref VisibleLight visibleLight) 
        {
            otherLightColors[index] = visibleLight.finalColor;
            otherLightPositions[index] = visibleLight.localToWorldMatrix.GetColumn(3);
        }
    

    ​ 现在,我们需要修改SetupLights中的循环,根据光源的类型进行不同的选择:

            for (int i = 0; i < visibleLights.Length; i++) 
            {
                VisibleLight visibleLight = visibleLights[i];
                //if (visibleLight.lightType == LightType.Directional) 
                //  {
                //  SetupDirectionalLight(dirLightCount++, ref visibleLight);
                //  if (dirLightCount >= maxDirLightCount) 
                //  {
                //      break;
                //  }
                //}
                switch (visibleLight.lightType) 
                {
                    case LightType.Directional:
                        if (dirLightCount < maxDirLightCount) 
                        {
                            SetupDirectionalLight(dirLightCount++, ref visibleLight);
                        }
                        break;
                    case LightType.Point:
                        if (otherLightCount < maxOtherLightCount) 
                        {
                            SetupPointLight(otherLightCount++, ref visibleLight);
                        }
                        break;
                }
            }
    

    1.3 着色

    ​ 在Light.hlsl中创建一个获取其它光数据的GetOtherLight方法:

    Light GetOtherLight (int index, Surface surfaceWS, ShadowData shadowData) 
    {
        Light light;
        light.color = _OtherLightColors[index].rgb;
        float3 ray = _OtherLightPositions[index].xyz - surfaceWS.position;
        light.direction = normalize(ray);
        light.attenuation = 1.0;
        return light;
    }
    

    ​ 在GetLighting中添加其它光的循环:

    float3 GetLighting (Surface surfaceWS, BRDF brdf, GI gi) 
    {
        ShadowData shadowData = GetShadowData(surfaceWS);
        shadowData.shadowMask = gi.shadowMask;
        
        float3 color = IndirectBRDF(surfaceWS, brdf, gi.diffuse, gi.specular);
        for (int i = 0; i < GetDirectionalLightCount(); i++) 
        {
            Light light = GetDirectionalLight(i, surfaceWS, shadowData);
            color += GetLighting(surfaceWS, brdf, light);
        }
    
        for (int j = 0; j < GetOtherLightCount(); j++) 
        {
            Light light = GetOtherLight(j, surfaceWS, shadowData);
            color += GetLighting(surfaceWS, brdf, light);
        }
        return color;
    }
    

    1.4 基于距离的衰减

    ​ 这里我们进行尝试进行平方衰减,\frac {i^2} {d^2},其中i是强度,d是距离:

        float distanceSqr = max(dot(ray, ray), 0.00001);
        light.attenuation = 1.0 / distanceSqr;
    

    1.5 灯光范围

    ​ 灯光衰减到一定距离就很难察觉,这时可直接将衰减置为0,也就是说我们需要一个最大可影响范围,但在到达范围时不能立即设置为0。Unity的URP使用max(0,1-(\frac {d^2} {r^2})^2)^2来得到范围衰减,其中r是灯光的范围(球半径)。

    ​ 我们在灯光位置的第四个组件处存储灯光范围\frac {1} {r^2}

        void SetupPointLight (int index, ref VisibleLight visibleLight) 
        {
            otherLightColors[index] = visibleLight.finalColor;
            Vector4 position = visibleLight.localToWorldMatrix.GetColumn(3);
            position.w =
                1f / Mathf.Max(visibleLight.range * visibleLight.range, 0.00001f);
            otherLightPositions[index] = position;
        }
    

    ​ 在GetOtherLight中重新计算衰减:

        float distanceSqr = max(dot(ray, ray), 0.00001);
        float rangeAttenuation = Square(
            saturate(1.0 - Square(distanceSqr * _OtherLightPositions[index].w))
        );
        light.attenuation = rangeAttenuation / distanceSqr;
    

    2 聚光

    2.1 方向

    ​ 聚光除位置等外,还需要方向:

        static int
            otherLightCountId = Shader.PropertyToID("_OtherLightCount"),
            otherLightColorsId = Shader.PropertyToID("_OtherLightColors"),
            otherLightPositionsId = Shader.PropertyToID("_OtherLightPositions"),
            otherLightDirectionsId = Shader.PropertyToID("_OtherLightDirections");
    
        static Vector4[]
            otherLightColors = new Vector4[maxOtherLightCount],
            otherLightPositions = new Vector4[maxOtherLightCount],
            otherLightDirections = new Vector4[maxOtherLightCount];
    

    ​ 我们需要将相应的数据送往GPU。

    ​ 创建一个SetupSpotLight方法,灯光的方向由局部到世界矩阵的第三列取反得到:

        void SetupSpotLight (int index, ref VisibleLight visibleLight) 
        {
            otherLightColors[index] = visibleLight.finalColor;
            Vector4 position = visibleLight.localToWorldMatrix.GetColumn(3);
            position.w =
                1f / Mathf.Max(visibleLight.range * visibleLight.range, 0.00001f);
            otherLightPositions[index] = position;
            otherLightDirections[index] =
                -visibleLight.localToWorldMatrix.GetColumn(2);
        }
    

    ​ 在SetupLights的循环中加入聚光支持:

                    case LightType.Spot:
                        if (otherLightCount < maxOtherLightCount) 
                        {
                            SetupSpotLight(otherLightCount++, ref visibleLight);
                        }
                        break;
    

    ​ shader方面,加入新数据:

        float4 _OtherLightDirections[MAX_OTHER_LIGHT_COUNT];
    

    ​ 衰减方面,我们暂时先用聚光方向和光源到片元方向的点积来模拟:

        float spotAttenuation =
            saturate(dot(_OtherLightDirections[index].xyz, light.direction));
        light.attenuation = spotAttenuation * rangeAttenuation / distanceSqr;
    

    2.2 聚光角

    ​ 锥形光,外角控制范围,内角来控制灯光衰减的开始角度。URP在使用saturate前先缩放点积并加上一些东西,然后对结果开方,saturate(da+b)^2,其中d是点积,a=\frac {1} {cos(\frac {r_i} {2}) - cos(\frac {r_o} {2})}b = -cos(\frac {r_o} {2})ar_ir_o是内角和外角。

    ​ 我们可以提前计算好ab,先声明变量:

        static int
            otherLightCountId = Shader.PropertyToID("_OtherLightCount"),
            otherLightColorsId = Shader.PropertyToID("_OtherLightColors"),
            otherLightPositionsId = Shader.PropertyToID("_OtherLightPositions"),
            otherLightDirectionsId = Shader.PropertyToID("_OtherLightDirections"),
            otherLightSpotAnglesId = Shader.PropertyToID("_OtherLightSpotAngles");
    
        static Vector4[]
            otherLightColors = new Vector4[maxOtherLightCount],
            otherLightPositions = new Vector4[maxOtherLightCount],
            otherLightDirections = new Vector4[maxOtherLightCount],
            otherLightSpotAngles = new Vector4[maxOtherLightCount];
    

    ​ 在SetupSpotLight中,外角可由VisibleLight.spotAngle获得,而内角可由VisibleLight.light.innerSpotAngle获得:

        void SetupSpotLight (int index, ref VisibleLight visibleLight) 
        {
            …
    
            Light light = visibleLight.light;
            float innerCos = Mathf.Cos(Mathf.Deg2Rad * 0.5f * light.innerSpotAngle);
            float outerCos = Mathf.Cos(Mathf.Deg2Rad * 0.5f * visibleLight.spotAngle);
            float angleRangeInv = 1f / Mathf.Max(innerCos - outerCos, 0.001f);
            otherLightSpots[index] = new Vector4(
                angleRangeInv, -outerCos * angleRangeInv
            );
        }
    

    ​ shader层面,我们应用等式得到聚光衰减:

        float4 spotAngles = _OtherLightSpotAngles[index];
        float spotAttenuation = Square(
            saturate(dot(_OtherLightDirections[index].xyz, light.direction) *
            spotAngles.x + spotAngles.y)
        );
        light.attenuation = spotAttenuation * rangeAttenuation / distanceSqr;
    

    ​ 为确保点光不会被聚光衰减影响,我们将聚光角分别设置为0和1:

        void SetupPointLight (int index, ref VisibleLight visibleLight) 
        {
            …
            otherLightSpotAngles[index] = new Vector4(0f, 1f);
        }
    

    2.3 配置内角

    ​ Unity默认的灯光组件不能配置聚光内角,为此,我们可以自定义灯光编辑器,新的类需继承LightEditor。加上[CanEditMultipleObjects]确保可修改多个灯光。加上[CustomEditorForRenderPipeline]属性,第一个参数是灯光的类型,第二个参数必须是自定义RP资产的类型:

    using UnityEngine;
    using UnityEditor;
    
    [CanEditMultipleObjects]
    [CustomEditorForRenderPipeline(typeof(Light), typeof(CustomRenderPipelineAsset))]
    public class CustomLightEditor : LightEditor {}
    

    ​ 然后重写OnInspectorGUI方法,先调用默认的GUI:

        public override void OnInspectorGUI() 
        {
            base.OnInspectorGUI();
        }
    

    ​ 我们可通过settings来获得相应的属性,如果只是聚光灯我们就调整内角和外角:

            base.OnInspectorGUI();
            if (
                !settings.lightType.hasMultipleDifferentValues &&
                (LightType)settings.lightType.enumValueIndex == LightType.Spot
            )
            {
                settings.DrawInnerAndOuterSpotAngle();
                settings.ApplyModifiedProperties();
            }
    

    3 烘培光和阴影

    3.1 完全烘培

    ​ 烘培点光和聚光的话会发现比实时光亮不少,这是因为Unity默认使用的光衰减不正确。

    3.2 灯光代理

    ​ 我们可以让Unity使用不同的光衰减值,在Unity于编辑器内执行lightmapping前为本该调用的方法提供一个代理值,为此,我们将CustomRenderPipeline变为一个partial类,构造方法中调用编辑器专属的InitializeForEditor方法:

    public partial class CustomRenderPipeline : RenderPipeline 
    {
    
        …
    
        public CustomRenderPipeline (
            bool useDynamicBatching, bool useGPUInstancing, bool useSRPBatcher,
            ShadowSettings shadowSettings
        ) 
        {
            …
            InitializeForEditor();
        }
    
        …
    }
    

    ​ 定义另一个编辑器版本的partial类:

    using Unity.Collections;
    using UnityEngine;
    using UnityEngine.Experimental.GlobalIllumination;
    using LightType = UnityEngine.LightType;
    
    public partial class CustomRenderPipeline 
    {
    
        partial void InitializeForEditor ();
    }
    

    ​ 我们重写Lightmapper初始化灯光数据的方法,提供一个方法代理,将来自输入Light数组的数据传输到NativeArray<LightDataGI>输出中,代理类型为Lightmapping.RequestLightsDelegate,这里我们使用lambda表达式来定义方法:

    partial void InitializeForEditor ();
        
    #if UNITY_EDITOR
    
        static Lightmapping.RequestLightsDelegate lightsDelegate =
            (Light[] lights, NativeArray<LightDataGI> output) => {};
    
    #endif
    

    ​ 我们需要为每个灯光配置LightDataGI结构体,并添加到输出中,对每种灯光类型,我们进行不同的操作,默认情况下不烘培光:

        static Lightmapping.RequestLightsDelegate lightsDelegate =
            (Light[] lights, NativeArray<LightDataGI> output) => {
                var lightData = new LightDataGI();
                for (int i = 0; i < lights.Length; i++) 
                {
                    Light light = lights[i];
                    switch (light.type) 
                    {
                        default:
                            lightData.InitNoBake(light.GetInstanceID());
                            break;
                    }
                    output[i] = lightData;
                }
            };
    

    ​ 对每种类型的光,我们调用LightmapperUtils.Extract方法获得灯光结构体数据,然后调用灯光数据的Init方法初始化:

                    switch (light.type) 
                    {
                        case LightType.Directional:
                            var directionalLight = new DirectionalLight();
                            LightmapperUtils.Extract(light, ref directionalLight);
                            lightData.Init(ref directionalLight);
                            break;
                        case LightType.Point:
                            var pointLight = new PointLight();
                            LightmapperUtils.Extract(light, ref pointLight);
                            lightData.Init(ref pointLight);
                            break;
                        case LightType.Spot:
                            var spotLight = new SpotLight();
                            LightmapperUtils.Extract(light, ref spotLight);
                            lightData.Init(ref spotLight);
                            break;
                        case LightType.Area:
                            var rectangleLight = new RectangleLight();
                            LightmapperUtils.Extract(light, ref rectangleLight);
                            rectangleLight.mode = LightMode.Baked;
                            lightData.Init(ref rectangleLight);
                            break;
                        default:
                            lightData.InitNoBake(light.GetInstanceID());
                            break;
                    }
    

    ​ 最后,我们修改衰减类型:

                    lightData.falloff = FalloffType.InverseSquared;
                    output[i] = lightData;
    

    ​ 在InitializeForForEditor中我们设置灯光代理:

        partial void InitializeForEditor ();
        
    #if UNITY_EDITOR
    
        partial void InitializeForEditor () 
    {
            Lightmapping.SetDelegate(lightsDelegate);
        }
    

    ​ 我们还需要重写Dispose方法:

        protected override void Dispose (bool disposing) 
        {
            base.Dispose(disposing);
            Lightmapping.ResetDelegate();
        }
    

    3.3 阴影遮罩

    ​ 我们添加ReserveOtherShadow方法,目前只支持阴影这招模式,且只考虑配置阴影强度和遮罩通道:

        public Vector4 ReserveOtherShadows (Light light, int visibleLightIndex) {
            if (light.shadows != LightShadows.None && light.shadowStrength > 0f) 
            {
                LightBakingOutput lightBaking = light.bakingOutput;
                if (
                    lightBaking.lightmapBakeType == LightmapBakeType.Mixed &&
                    lightBaking.mixedLightingMode == MixedLightingMode.Shadowmask
                ) 
                {
                    useShadowMask = true;
                    return new Vector4(
                        light.shadowStrength, 0f, 0f,
                        lightBaking.occlusionMaskChannel
                    );
                }
            }
            return new Vector4(0f, 0f, 0f, -1f);
        }
    

    ​ 在SetupPointLightSetupSpotLight中配置数据:

        void SetupPointLight (int index, ref VisibleLight visibleLight) 
        {
            …
            Light light = visibleLight.light;
            otherLightShadowData[index] = shadows.ReserveOtherShadows(light, index);
        }
    
        void SetupSpotLight (int index, ref VisibleLight visibleLight) 
        {
            …
            otherLightShadowData[index] = shadows.ReserveOtherShadows(light, index);
        }
    

    ​ shader层面,在Shadows.hlsl中创建OtherShadowData结构体和GetOtherShadowAttenuation方法,目前先使用和平行光阴影相同的处理过程:

    struct OtherShadowData 
    {
        float strength;
        int shadowMaskChannel;
    };
    
    float GetOtherShadowAttenuation (
        OtherShadowData other, ShadowData global, Surface surfaceWS
    ) 
    {
        #if !defined(_RECEIVE_SHADOWS)
            return 1.0;
        #endif
        
        float shadow;
        if (other.strength > 0.0) 
        {
            shadow = GetBakedShadow(
                global.shadowMask, other.shadowMaskChannel, other.strength
            );
        }
        else 
        {
            shadow = 1.0;
        }
        return shadow;
    }
    

    Light.hlsl中,加入GetOtherShadowData方法,并在GetOtherLight中配置:

    CBUFFER_START(_CustomLight)
        …
        float4 _OtherLightShadowData[MAX_OTHER_LIGHT_COUNT];
    CBUFFER_END
    
    …           
    
    OtherShadowData GetOtherShadowData (int lightIndex) 
    {
        OtherShadowData data;
        data.strength = _OtherLightShadowData[lightIndex].x;
        data.shadowMaskChannel = _OtherLightShadowData[lightIndex].w;
        return data;
    }
    
    Light GetOtherLight (int index, Surface surfaceWS, ShadowData shadowData) 
    {
        …
        
        OtherShadowData otherShadowData = GetOtherShadowData(index);
        light.attenuation =
            GetOtherShadowAttenuation(otherShadowData, shadowData, surfaceWS) *
            spotAttenuation * rangeAttenuation / distanceSqr;
        return light;
    }
    

    4 逐物体灯光

    ​ 目前的光照计算都是逐片元的,这对于平行光来说可以接受,但对于其它类型的灯光来说就消耗太大了,毕竟数量很多,而且大部分片元不会受某一光源影响。因此,我们需要减少哪些逐片元计算的光源数量,最简单的方法是使用Unity的逐物体灯光索引。

    ​ Unity决定哪些灯光影响每个物体,然后将信息送往GPU,这样对于每个物体我们就只用考虑几个光源,这对于小物体来说很不错,但对于较大的物体来说,会丢失一些光源影响。

    4.1 逐物体灯光数据

    ​ 在CameraRenderer.DrawVisibleGeometry中,添加一个布尔参数来指示是否使用逐物体灯光模式。如果是则开启PerObejctData.LightDataPerObjectData.LightIndices:

        void DrawVisibleGeometry (
            bool useDynamicBatching, bool useGPUInstancing, bool useLightsPerObject
        ) 
        {
            PerObjectData lightsPerObjectFlags = useLightsPerObject ?
                PerObjectData.LightData | PerObjectData.LightIndices :
                PerObjectData.None;
            var sortingSettings = new SortingSettings(camera) {
                criteria = SortingCriteria.CommonOpaque
            };
            var drawingSettings = new DrawingSettings(
                unlitShaderTagId, sortingSettings
            ) {
                enableDynamicBatching = useDynamicBatching,
                enableInstancing = useGPUInstancing,
                perObjectData =
                    PerObjectData.ReflectionProbes |
                    PerObjectData.Lightmaps | PerObjectData.ShadowMask |
                    PerObjectData.LightProbe | PerObjectData.OcclusionProbe |
                    PerObjectData.LightProbeProxyVolume |
                    PerObjectData.OcclusionProbeProxyVolume |
                    lightsPerObjectFlags
            };
            …
        }
    

    CameraRenderer.RenderCustomRenderPipeline中我们都加上对应的参数。

    ​ 在CustomRenderPipelineAsset中我们加上对应的选项:

        [SerializeField]
        bool
            useDynamicBatching = true,
            useGPUInstancing = true,
            useSRPBatcher = true,
            useLightsPerObject = true;
    
        [SerializeField]
        ShadowSettings shadows = default;
    
        protected override RenderPipeline CreatePipeline () 
        {
            return new CustomRenderPipeline(
                useDynamicBatching, useGPUInstancing, useSRPBatcher,
                useLightsPerObject, shadows
            );
        }
    

    4.2 优化灯光索引

    ​ Unity为每个物体创建一个灯光列表,按照重要性排序,列表中不管可不可见都会包含,并且包含平行光,因此我们要进行优化,只留下那些可见的非平行光。在SetupLights中进行。在进入循环前,先调用GetLightIndexMap获得灯光索引表:

            NativeArray<int> indexMap = useLightsPerObject ?
                cullingResults.GetLightIndexMap(Allocator.Temp) : default;
            NativeArray<VisibleLight> visibleLights = cullingResults.visibleLights;
    

    ​ 我们只需要点光和聚光的索引,不要的灯光的索引设为-1,并修改剩余灯光的索引:

            for (int i = 0; i < visibleLights.Length; i++) 
            {
                int newIndex = -1;
                VisibleLight visibleLight = visibleLights[i];
                switch (visibleLight.lightType) {
                    …
                    case LightType.Point:
                        if (otherLightCount < maxOtherLightCount) 
                        {
                            newIndex = otherLightCount;
                            SetupPointLight(otherLightCount++, ref visibleLight);
                        }
                        break;
                    case LightType.Spot:
                        if (otherLightCount < maxOtherLightCount) 
                        {
                            newIndex = otherLightCount;
                            SetupSpotLight(otherLightCount++, ref visibleLight);
                        }
                        break;
                }
                if (useLightsPerObject) 
                {
                    indexMap[i] = newIndex;
                }
            }
    

    ​ 不可见光的索引也剔除,在第一次循环后进行(从未重新设置的索引处开始):

            if (useLightsPerObject) 
            {
                for (; i < indexMap.Length; i++) 
                {
                    indexMap[i] = -1;
                }
            }
    

    ​ 完成后,我们要修改索引表,然后删除不再使用的表:

            if (useLightsPerObject) 
            {
                for (; i < indexMap.Length; i++) 
                {
                    indexMap[i] = -1;
                }
                cullingResults.SetLightIndexMap(indexMap);
                indexMap.Dispose();
            }
    

    ​ 记得设置对应的关键字:

        static string lightsPerObjectKeyword = "_LIGHTS_PER_OBJECT";
        
        …
        
        void SetupLights (bool useLightsPerObject) 
        {
            …
    
            if (useLightsPerObject) 
            {
                for (; i < indexMap.Length; i++) 
                {
                    indexMap[i] = -1;
                }
                cullingResults.SetLightIndexMap(indexMap);
                indexMap.Dispose();
                Shader.EnableKeyword(lightsPerObjectKeyword);
            }
            else 
            {
                Shader.DisableKeyword(lightsPerObjectKeyword);
            }
            
            …
        }
    

    4.3 使用索引

    ​ 使用multi_compile:

                #pragma multi_compile _ _LIGHTS_PER_OBJECT
    

    ​ 在UnityPerDraw缓冲中定义灯光数据和灯光索引,unity_LightData的y组件包含灯光数量,unity_LightIndices的两个vector的每个通道包含一个灯光索引:

        real4 unity_WorldTransformParams;
    
        real4 unity_LightData;
        real4 unity_LightIndices[2];
    

    ​ 在GetLighting中,如果定义了_LIGHTS_PER_OBJECT,遍历所有的逐物体光:

        #if defined(_LIGHTS_PER_OBJECT)
            for (int j = 0; j < min(unity_LightData.y, 8); j++) 
            {
                int lightIndex = unity_LightIndices[j / 4][j % 4];
                Light light = GetOtherLight(lightIndex, surfaceWS, shadowData);
                color += GetLighting(surfaceWS, brdf, light);
            }
        #else
            for (int j = 0; j < GetOtherLightCount(); j++) 
            {
                Light light = GetOtherLight(j, surfaceWS, shadowData);
                color += GetLighting(surfaceWS, brdf, light);
            }
        #endif
    

    相关文章

      网友评论

          本文标题:Unity自定义SRP(九):点光和聚光

          本文链接:https://www.haomeiwen.com/subject/eobxzktx.html