void RenderMainLightCascadeShadowmap(ref ScriptableRenderContext context, ref CullingResults cullResults, ref LightData lightData, ref ShadowData shadowData)
        {
            int shadowLightIndex = lightData.mainLightIndex;

            if (shadowLightIndex == -1)
            {
                return;
            }

            VisibleLight shadowLight = lightData.visibleLights[shadowLightIndex];

            CommandBuffer cmd = CommandBufferPool.Get(m_ProfilerTag);

            using (new ProfilingSample(cmd, m_ProfilerTag))
            {
                var settings = new ShadowDrawingSettings(cullResults, shadowLightIndex);

                for (int cascadeIndex = 0; cascadeIndex < m_ShadowCasterCascadesCount; ++cascadeIndex)
                {
                    var splitData = settings.splitData;
                    splitData.cullingSphere = m_CascadeSplitDistances[cascadeIndex];
                    settings.splitData      = splitData;
                    Vector4 shadowBias = ShadowUtils.GetShadowBias(ref shadowLight, shadowLightIndex, ref shadowData, m_CascadeSlices[cascadeIndex].projectionMatrix, m_CascadeSlices[cascadeIndex].resolution);
                    ShadowUtils.SetupShadowCasterConstantBuffer(cmd, ref shadowLight, shadowBias);
                    ShadowUtils.RenderShadowSlice(cmd, ref context, ref m_CascadeSlices[cascadeIndex],
                                                  ref settings, m_CascadeSlices[cascadeIndex].projectionMatrix, m_CascadeSlices[cascadeIndex].viewMatrix);
                }

                SetupMainLightShadowReceiverConstants(cmd, ref shadowData, shadowLight);
            }

            CoreUtils.SetKeyword(cmd, ShaderKeywordStrings.MainLightShadows, true);
            CoreUtils.SetKeyword(cmd, ShaderKeywordStrings.MainLightShadowCascades, shadowData.mainLightShadowCascadesCount > 1);
            CoreUtils.SetKeyword(cmd, ShaderKeywordStrings.SoftShadows, shadowLight.light.shadows == LightShadows.Soft && shadowData.supportsSoftShadows);
            context.ExecuteCommandBuffer(cmd);
            CommandBufferPool.Release(cmd);
        }
Exemple #2
0
    void SetLighting(ScriptableRenderContext context, Camera camera)
    {
        int lightIndex = -1;

        for (int i = 0; i < cullingResults.visibleLights.Length; ++i)
        {
            VisibleLight curLight = cullingResults.visibleLights[i];
            if (curLight.lightType != LightType.Directional)
            {
                continue;
            }

            lightIndex = i;
            break;
        }

        if (lightIndex < 0)
        {
            CmdBuff.DisableShaderKeyword("ENABLE_DIRECTIONAL_LIGHT");
            context.ExecuteCommandBuffer(CmdBuff);
            CmdBuff.Clear();
            return;
        }

        CmdBuff.EnableShaderKeyword("ENABLE_DIRECTIONAL_LIGHT");
        CmdBuff.SetGlobalColor(LightColorID, cullingResults.visibleLights[lightIndex].finalColor);

        Vector4 lightDirection = cullingResults.visibleLights[lightIndex].localToWorldMatrix.GetColumn(2); // cullingResults.visibleLights[lightIndex].light.transform.forward

        lightDirection.x *= -1f;
        lightDirection.y *= -1f;
        lightDirection.z *= -1f;
        CmdBuff.SetGlobalVector(LightDirectionID, lightDirection);

        context.ExecuteCommandBuffer(CmdBuff);
        CmdBuff.Clear();
    }
Exemple #3
0
        static Matrix4x4 ExtractPointLightMatrix(VisibleLight vl, uint faceIdx, float nearPlane, float guardAngle, out Matrix4x4 view, out Matrix4x4 proj, out Matrix4x4 deviceProj, out Matrix4x4 vpinverse, out Vector4 lightDir, out ShadowSplitData splitData)
        {
            if (faceIdx > (uint)CubemapFace.NegativeZ)
            {
                Debug.LogError("Tried to extract cubemap face " + faceIdx + ".");
            }

            splitData = new ShadowSplitData();
            splitData.cullingSphere.Set(0.0f, 0.0f, 0.0f, float.NegativeInfinity);

            // get lightDir
            lightDir = vl.light.transform.forward;
            // calculate the view matrices
            Vector3 lpos = vl.light.transform.position;

            view = kCubemapFaces[faceIdx];
            Vector3 inverted_viewpos = kCubemapFaces[faceIdx].MultiplyPoint(-lpos);

            view.SetColumn(3, new Vector4(inverted_viewpos.x, inverted_viewpos.y, inverted_viewpos.z, 1.0f));

            float nearZ = Mathf.Max(nearPlane, k_MinShadowNearPlane);

            proj = Matrix4x4.Perspective(90.0f + guardAngle, 1.0f, nearZ, vl.range);
            // and the compound (deviceProj will potentially inverse-Z)
            deviceProj = GL.GetGPUProjectionMatrix(proj, false);
            proj       = GL.GetGPUProjectionMatrix(proj, true);
            InvertPerspective(ref deviceProj, ref view, out vpinverse);

            GeometryUtility.CalculateFrustumPlanes(proj * view, s_CachedPlanes);
            splitData.cullingPlaneCount = 6;
            for (int i = 0; i < 6; i++)
            {
                splitData.SetCullingPlane(i, s_CachedPlanes[i]);
            }

            return(deviceProj * view);
        }
        public bool Setup(ScriptableRenderer renderer, ref RenderingData renderingData)
        {
            int shadowLightIndex = renderingData.lightData.mainLightIndex;

            if (shadowLightIndex == -1)
            {
                return(false);
            }


            VisibleLight shadowLight = renderingData.lightData.visibleLights[shadowLightIndex];
            Light        light       = shadowLight.light;

            if (light.shadows == LightShadows.None)
            {
                return(false);
            }


            if (shadowLight.lightType != LightType.Directional)
            {
                Debug.LogWarning("Only directional lights are supported as main light.");
            }

            Bounds bounds;

            if (!renderingData.cullResults.GetShadowCasterBounds(shadowLightIndex, out bounds))
            {
                return(false);
            }

            //TODO : More branches
            _CountBuffer = renderer.GetBuffer(ComputeBufferHandle.DeepShadowMapsCount);
            _DataBuffer  = renderer.GetBuffer(ComputeBufferHandle.DeepShadowMapsData);

            return(true);
        }
    void SetupShadowPass(ref ScriptableRenderContext context, ref CullingResults cullingResults, int shadowMapSize)
    {
        shadowData = new ShadowData();
        shadowData.shadowMapSize = shadowMapSize;

        if (cullingResults.visibleLights.Length <= 0)
        {
            shadowData.lights     = null;
            shadowData.shadowMaps = null;
            return;
        }

        shadowData.lights                 = new ShadowLight[cullingResults.visibleLights.Length];
        shadowData.shadowMaps             = RenderTexture.GetTemporary(shadowMapSize, shadowMapSize, 16, RenderTextureFormat.Shadowmap);
        shadowData.shadowMaps.dimension   = TextureDimension.Tex2DArray;
        shadowData.shadowMaps.volumeDepth = cullingResults.visibleLights.Length;
        shadowData.shadowMaps.filterMode  = FilterMode.Bilinear;
        shadowData.shadowMaps.wrapMode    = TextureWrapMode.Clamp;

        for (int i = 0; i < cullingResults.visibleLights.Length; i++)
        {
            VisibleLight visibleLight = cullingResults.visibleLights[i];
            switch (cullingResults.visibleLights[i].lightType)
            {
            case LightType.Directional:
                SetupDirectionalShadow(ref context, ref cullingResults, shadowMapSize, i, ref visibleLight);
                break;

            // case LightType.Point:
            //     SetupPointShadow(ref context, ref cullingResults, shadowMapSize, i, ref visibleLight);
            //     break;
            case LightType.Spot:
                SetupSpotShadow(ref context, ref cullingResults, shadowMapSize, i, ref visibleLight);
                break;
            }
        }
    }
 void ConfigureLights()
 {
     if (cull.visibleLights == null)
     {
         return;
     }
     for (int i = 0; i < maxVisibleLights; i++)
     {
         visibleLightColors[i] = Color.clear;
     }
     for (int i = 0; i < cull.visibleLights.Count; i++)
     {
         if (i == maxVisibleLights)
         {
             break;
         }
         VisibleLight light = cull.visibleLights[i];
         visibleLightColors[i] = light.finalColor;
         Vector4 attenuation = Vector4.zero;
         if (light.lightType == LightType.Directional)
         {
             //获取方向,矩阵第3列是Z轴方向
             Vector4 v = light.localToWorld.GetColumn(2);
             v.x = -v.x;
             v.y = -v.y;
             v.z = -v.z;
             visibleLightDirectionsOrPositions[i] = v;
         }
         else
         {
             visibleLightDirectionsOrPositions[i] = light.localToWorld.GetColumn(3);
             //光源的范围衰减
             attenuation.x = 1f / Mathf.Max(light.range * light.range, 0.00001f);
         }
         visibleLightAttenuations[i] = attenuation;
     }
 }
Exemple #7
0
    public override void Execute(ScriptableRenderer renderer, ScriptableRenderContext context, ref RenderingData renderingData)
    {
        if (renderingData.lightData.mainLightIndex == -1)
        {
            return;
        }

        var cmd = commandBufferPool.Get(k_CollectShadowTag);

        cmd.GetTemporaryRT(colorAttachmentHandle.id, desc, FilterMode.Bilinear);

        VisibleLight shadowLight = renderingData.lightData.visibleLights[renderingData.lightData.mainLightIndex];

        SetShadowCollectPassKeywords(cmd, ref shadowLight, ref renderingData.shadowData);

        RenderTargetIdentifier screenSpaceOcclusionTexture = colorAttachmentHandle.Identifier();

        SetRenderTarget(cmd, screenSpaceOcclusionTexture, RenderBufferLoadAction.DontCare, RenderBufferStoreAction.Store,
                        ClearFlag.All, Color.white, desc.dimension);
        cmd.Blit(screenSpaceOcclusionTexture, screenSpaceOcclusionTexture, renderer.GetMaterial(MaterialHandles.ScreenSpacceShadow));

        context.ExecuteCommandBuffer(cmd);
        commandBufferPool.Release(cmd);
    }
Exemple #8
0
        private void SetupSpotLight(
            int index, int visibleIndex, 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);

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

            otherLightSpotAngles[index] = new Vector4(
                angleRangeInv, -outerCos * angleRangeInv
                );
            otherLightShadowData[index] =
                shadows.ReserveOtherShadows(light, visibleIndex);
        }
Exemple #9
0
    void SetupLights()
    {
        NativeArray <VisibleLight> visibleLights = cullingResults.visibleLights;

        int dirLightCount = 0;

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

        buffer.SetGlobalInt(dirLightCountId, visibleLights.Length);
        buffer.SetGlobalVectorArray(dirLightColorsId, dirLightColors);
        buffer.SetGlobalVectorArray(dirLightDirectionsId, dirLightDirections);
        buffer.SetGlobalVectorArray(dirLightShadowDataId, dirLightShadowData);
    }
        public static Matrix4x4 ExtractPointLightMatrix(VisibleLight vl, uint faceIdx, float fovBias, out Matrix4x4 view, out Matrix4x4 proj, out Matrix4x4 vpinverse, out Vector4 lightDir, out ShadowSplitData splitData)
        {
            Debug.Assert(faceIdx <= (uint)CubemapFace.NegativeZ, "Tried to extract cubemap face " + faceIdx + ".");

            splitData = new ShadowSplitData();
            splitData.cullingSphere.Set(0.0f, 0.0f, 0.0f, float.NegativeInfinity);
            splitData.cullingPlaneCount = 4;
            // get lightDir
            lightDir = vl.light.transform.forward;
            // calculate the view matrices
            Vector3 lpos = vl.light.transform.position;

            view = ShadowUtilsConstants.kCubemapFaces[faceIdx];
            Vector3 inverted_viewpos = ShadowUtilsConstants.kCubemapFaces[faceIdx].MultiplyPoint(-lpos);

            view.SetColumn(3, new Vector4(inverted_viewpos.x, inverted_viewpos.y, inverted_viewpos.z, 1.0f));

            for (int i = 0; i < 4; ++i)
            {
                ShadowUtilsConstants.CubemapEdge cubemapEdge = ShadowUtilsConstants.kCubemapEdgesPerFace[faceIdx, i];
                Vector3 cullingPlaneDirection = ShadowUtilsConstants.kCubemapEdgeDirections[(int)cubemapEdge];
                splitData.SetCullingPlane(i, new Plane(cullingPlaneDirection, lpos));
            }
            // following code is from SharedLightData::GetNearPlaneMinBound
            float percentageBound = 0.01f * vl.light.range;
            float fixedBound      = 0.1f;
            float nearmin         = fixedBound <= percentageBound ? fixedBound : percentageBound;
            // calculate projection
            float farPlane  = vl.range;
            float nearPlane = vl.light.shadowNearPlane >= nearmin ? vl.light.shadowNearPlane : nearmin;

            proj = Matrix4x4.Perspective(90.0f + fovBias, 1.0f, nearPlane, farPlane);
            // and the compound
            InvertPerspective(ref proj, ref view, out vpinverse);
            return(proj * view);
        }
        void SetupAdditionalLightConstants(CommandBuffer cmd, ref LightData lightData)
        {
            int maxVisibleAdditionalLights = LightweightRenderPipeline.maxVisibleAdditionalLights;
            var lights = lightData.visibleLights;

            if (lightData.additionalLightsCount > 0)
            {
                int additionalLightsCount = 0;
                for (int i = 0; i < lights.Length && additionalLightsCount < maxVisibleAdditionalLights; ++i)
                {
                    VisibleLight light = lights[i];
                    if (light.lightType != LightType.Directional)
                    {
                        InitializeLightConstants(lights, i, out m_AdditionalLightPositions[additionalLightsCount],
                                                 out m_AdditionalLightColors[additionalLightsCount],
                                                 out m_AdditionalLightAttenuations[additionalLightsCount],
                                                 out m_AdditionalLightSpotDirections[additionalLightsCount],
                                                 out m_AdditionalLightOcclusionProbeChannels[additionalLightsCount]);
                        additionalLightsCount++;
                    }
                }

                cmd.SetGlobalVector(LightConstantBuffer._AdditionalLightsCount, new Vector4(lightData.maxPerObjectAdditionalLightsCount,
                                                                                            0.0f, 0.0f, 0.0f));
            }
            else
            {
                cmd.SetGlobalVector(LightConstantBuffer._AdditionalLightsCount, Vector4.zero);
            }

            cmd.SetGlobalVectorArray(LightConstantBuffer._AdditionalLightsPosition, m_AdditionalLightPositions);
            cmd.SetGlobalVectorArray(LightConstantBuffer._AdditionalLightsColor, m_AdditionalLightColors);
            cmd.SetGlobalVectorArray(LightConstantBuffer._AdditionalLightsAttenuation, m_AdditionalLightAttenuations);
            cmd.SetGlobalVectorArray(LightConstantBuffer._AdditionalLightsSpotDir, m_AdditionalLightSpotDirections);
            cmd.SetGlobalVectorArray(LightConstantBuffer._AdditionalLightOcclusionProbeChannel, m_AdditionalLightOcclusionProbeChannels);
        }
        void SetupMainLightConstants(CommandBuffer cmd, ref LightData lightData)
        {
            Vector4             lightPos, lightColor, lightDistanceAttenuation, lightSpotDir, lightSpotAttenuation;
            List <VisibleLight> lights = lightData.visibleLights;

            InitializeLightConstants(lightData.visibleLights, lightData.mainLightIndex, out lightPos, out lightColor, out lightDistanceAttenuation, out lightSpotDir, out lightSpotAttenuation);

            if (lightData.mainLightIndex >= 0)
            {
                VisibleLight mainLight    = lights[lightData.mainLightIndex];
                Light        mainLightRef = mainLight.light;

                if (LightweightPipeline.IsSupportedCookieType(mainLight.lightType) && mainLightRef.cookie != null)
                {
                    Matrix4x4 lightCookieMatrix;
                    LightweightPipeline.GetLightCookieMatrix(mainLight, out lightCookieMatrix);
                    cmd.SetGlobalTexture(LightConstantBuffer._MainLightCookie, mainLightRef.cookie);
                    cmd.SetGlobalMatrix(LightConstantBuffer._WorldToLight, lightCookieMatrix);
                }
            }

            cmd.SetGlobalVector(LightConstantBuffer._MainLightPosition, new Vector4(lightPos.x, lightPos.y, lightPos.z, lightDistanceAttenuation.w));
            cmd.SetGlobalVector(LightConstantBuffer._MainLightColor, lightColor);
        }
        // Main Light is always a directional light
        int GetMainLight(List<VisibleLight> visibleLights)
        {
            int totalVisibleLights = visibleLights.Count;

            if (totalVisibleLights == 0 || pipelineAsset.maxPixelLights == 0)
                return -1;

            for (int i = 0; i < totalVisibleLights; ++i)
            {
                VisibleLight currLight = visibleLights[i];

                // Particle system lights have the light property as null. We sort lights so all particles lights
                // come last. Therefore, if first light is particle light then all lights are particle lights.
                // In this case we either have no main light or already found it.
                if (currLight.light == null)
                    break;

                // In case no shadow light is present we will return the brightest directional light
                if (currLight.lightType == LightType.Directional)
                    return i;
            }

            return -1;
        }
        public void RenderLights(ScriptableRenderContext context, CullResults cullResults, LightData lightData)
        {
            CommandBuffer       cmd           = CommandBufferPool.Get("Render Deferred Lights");
            List <VisibleLight> visibleLights = lightData.visibleLights;

            for (int i = 0; i < visibleLights.Count; ++i)
            {
                VisibleLight currLight = visibleLights[i];
                if (currLight.lightType != LightType.Directional)
                {
                    continue;
                }

                Vector4 lightDirection = -currLight.localToWorld.GetRow(2);
                Vector4 lightColor     = currLight.finalColor;
                m_LightPropertiesBlock.Clear();
                m_LightPropertiesBlock.SetVector("_MainLightPosition", lightDirection);
                m_LightPropertiesBlock.SetVector("_MainLightColor", lightColor);
                cmd.DrawMesh(LightweightPipeline.fullscreenMesh, Matrix4x4.identity, m_DeferredShadingMaterial, 0, 0, m_LightPropertiesBlock);
            }

            context.ExecuteCommandBuffer(cmd);
            CommandBufferPool.Release(cmd);
        }
        public static void ExtractDirectionalLightData(VisibleLight visibleLight, Vector2 viewportSize, uint cascadeIndex, int cascadeCount, float[] cascadeRatios, float nearPlaneOffset, CullResults cullResults, int lightIndex, out Matrix4x4 view, out Matrix4x4 invViewProjection, out Matrix4x4 projection, out Matrix4x4 deviceProjection, out ShadowSplitData splitData)
        {
            Vector4 lightDir;

            Debug.Assert((uint)viewportSize.x == (uint)viewportSize.y, "Currently the cascaded shadow mapping code requires square cascades.");
            splitData = new ShadowSplitData();
            splitData.cullingSphere.Set(0.0f, 0.0f, 0.0f, float.NegativeInfinity);
            splitData.cullingPlaneCount = 0;
            // get lightDir
            lightDir = visibleLight.light.transform.forward;
            // TODO: At some point this logic should be moved to C#, then the parameters cullResults and lightIndex can be removed as well
            //       For directional lights shadow data is extracted from the cullResults, so that needs to be somehow provided here.
            //       Check ScriptableShadowsUtility.cpp ComputeDirectionalShadowMatricesAndCullingPrimitives(...) for details.
            Vector3 ratios = new Vector3();

            for (int i = 0, cnt = cascadeRatios.Length < 3 ? cascadeRatios.Length : 3; i < cnt; i++)
            {
                ratios[i] = cascadeRatios[i];
            }
            cullResults.ComputeDirectionalShadowMatricesAndCullingPrimitives(lightIndex, (int)cascadeIndex, cascadeCount, ratios, (int)viewportSize.x, nearPlaneOffset, out view, out projection, out splitData);
            // and the compound (deviceProjection will potentially inverse-Z)
            deviceProjection = GL.GetGPUProjectionMatrix(projection, false);
            InvertOrthographic(ref deviceProjection, ref view, out invViewProjection);
        }
Exemple #16
0
    void SetShadow(ScriptableRenderContext context, Camera camera)
    {
        if (camera.cameraType != CameraType.Game)
        {
            return;
        }

        int lightIndex = -1;

        for (int i = 0; i < cullingResults.visibleLights.Length; ++i)
        {
            VisibleLight curLight = cullingResults.visibleLights[i];
            if (curLight.lightType != LightType.Directional)
            {
                continue;
            }

            if (curLight.light.shadows == LightShadows.None)
            {
                continue;
            }
            if (curLight.light.shadowStrength <= 0f)
            {
                continue;
            }

            Bounds bounds;
            if (cullingResults.GetShadowCasterBounds(i, out bounds) == false)
            {
                continue;
            }

            lightIndex = i;
            break;
        }

        if (lightIndex < 0)
        {
            CmdBuff.SetGlobalTexture(ShadowMapID, Texture2D.whiteTexture);
            context.ExecuteCommandBuffer(CmdBuff);
            CmdBuff.Clear();
            return;
        }

        CmdBuff.SetGlobalFloat(ShadowBiasID, cullingResults.visibleLights[lightIndex].light.shadowBias);
        CmdBuff.SetGlobalFloat(ShadowStrengthID, cullingResults.visibleLights[lightIndex].light.shadowStrength);
        CmdBuff.SetGlobalVector(ShadowMapSizeID, new Vector4(invShadowMapSize, invShadowMapSize, shadowMapSize, shadowMapSize));

        if (cullingResults.visibleLights[lightIndex].light.shadows == LightShadows.Soft)
        {
            CmdBuff.EnableShaderKeyword("_SHADOWS_SOFT");
        }
        else
        {
            CmdBuff.DisableShaderKeyword("_SHADOWS_SOFT");
        }

        Matrix4x4       viewMatrix;
        Matrix4x4       projMatrix;
        ShadowSplitData shadowSplitData;

        bool res = cullingResults.ComputeDirectionalShadowMatricesAndCullingPrimitives(
            lightIndex,                                     // int activeLightIndex,
            0,                                              // int splitIndex,
            1,                                              // int splitCount,
            new Vector3(1.0f, 0.0f, 0.0f),                  // Vector3 splitRatio,
            (int)setting.shadow.atlasSize,                  // int shadowResolution,
            QualitySettings.shadowNearPlaneOffset,          // float shadowNearPlaneOffset,
            out viewMatrix,                                 // out Matrix4x4 viewMatrix,
            out projMatrix,                                 // out Matrix4x4 projMatrix,
            out shadowSplitData                             // out Experimental.Rendering.ShadowSplitData shadowSplitData
            );

        if (res == false)
        {
            CmdBuff.SetGlobalTexture(ShadowMapID, Texture2D.whiteTexture);
            context.ExecuteCommandBuffer(CmdBuff);
            CmdBuff.Clear();
            return;
        }

        CoreUtils.SetRenderTarget(CmdBuff, renderBuffers[3], RenderBufferLoadAction.DontCare, RenderBufferStoreAction.Store, ClearFlag.Depth);

        CmdBuff.SetViewProjectionMatrices(viewMatrix, projMatrix);

        context.ExecuteCommandBuffer(CmdBuff);
        CmdBuff.Clear();

        ShadowDrawingSettings shadowDrawingSettings = new ShadowDrawingSettings(cullingResults, lightIndex);

        shadowDrawingSettings.splitData = shadowSplitData;
        context.DrawShadows(ref shadowDrawingSettings);


        // https://docs.unity3d.com/2019.2/Documentation/Manual/SL-PlatformDifferences.html
        if (SystemInfo.usesReversedZBuffer)
        {
            projMatrix.m20 = -projMatrix.m20;
            projMatrix.m21 = -projMatrix.m21;
            projMatrix.m22 = -projMatrix.m22;
            projMatrix.m23 = -projMatrix.m23;
        }

        // [-1,1] remap to [0, 1]
        var scaleOffset = Matrix4x4.identity;

        scaleOffset.m00 = scaleOffset.m11 = scaleOffset.m22 = 0.5f;
        scaleOffset.m03 = scaleOffset.m13 = scaleOffset.m23 = 0.5f;

        CmdBuff.SetGlobalTexture(ShadowMapID, renderBuffers[3]);
        CmdBuff.SetGlobalMatrix(ShadowMatrixID, scaleOffset * (projMatrix * viewMatrix));

        context.ExecuteCommandBuffer(CmdBuff);
        CmdBuff.Clear();
    }
        public bool Setup(ref RenderingData renderingData)
        {
            Clear();

            m_ShadowmapWidth  = renderingData.shadowData.additionalLightsShadowmapWidth;
            m_ShadowmapHeight = renderingData.shadowData.additionalLightsShadowmapHeight;

            var visibleLights         = renderingData.lightData.visibleLights;
            int additionalLightsCount = renderingData.lightData.additionalLightsCount;

            if (m_AdditionalLightSlices == null || m_AdditionalLightSlices.Length < additionalLightsCount)
            {
                m_AdditionalLightSlices = new ShadowSliceData[additionalLightsCount];
            }

            if (m_AdditionalLightsShadowData == null || m_AdditionalLightsShadowData.Length < additionalLightsCount)
            {
                m_AdditionalLightsShadowData = new ShaderInput.ShadowData[additionalLightsCount];
            }

            // By default visible lights do not have shadow light indices.
            for (int i = 0; i < visibleLights.Length; ++i)
            {
                m_ShadowCastingLightIndicesMap.Add(-1);
            }

            int  validShadowCastingLights = 0;
            bool supportsSoftShadows      = renderingData.shadowData.supportsSoftShadows;

            for (int i = 0; i < visibleLights.Length && m_AdditionalShadowCastingLightIndices.Count < additionalLightsCount; ++i)
            {
                VisibleLight shadowLight = visibleLights[i];

                // Skip main directional light as it is not packed into the shadow atlas
                if (i == renderingData.lightData.mainLightIndex)
                {
                    continue;
                }

                int  shadowCastingLightIndex = m_AdditionalShadowCastingLightIndices.Count;
                bool isValidShadowSlice      = false;
                if (renderingData.cullResults.GetShadowCasterBounds(i, out var bounds))
                {
                    // We need to iterate the lights even though additional lights are disabled because
                    // cullResults.GetShadowCasterBounds() does the fence sync for the shadow culling jobs.
                    if (!renderingData.shadowData.supportsAdditionalLightShadows)
                    {
                        continue;
                    }

                    if (IsValidShadowCastingLight(ref renderingData.lightData, i))
                    {
                        bool success = ShadowUtils.ExtractSpotLightMatrix(ref renderingData.cullResults,
                                                                          ref renderingData.shadowData,
                                                                          i,
                                                                          out var shadowTransform,
                                                                          out m_AdditionalLightSlices[shadowCastingLightIndex].viewMatrix,
                                                                          out m_AdditionalLightSlices[shadowCastingLightIndex].projectionMatrix);

                        if (success)
                        {
                            m_AdditionalShadowCastingLightIndices.Add(i);
                            var     light          = shadowLight.light;
                            float   shadowStrength = light.shadowStrength;
                            float   softShadows    = (supportsSoftShadows && light.shadows == LightShadows.Soft) ? 1.0f : 0.0f;
                            Vector4 shadowParams   = new Vector4(shadowStrength, softShadows, 0.0f, 0.0f);
                            if (m_UseStructuredBuffer)
                            {
                                m_AdditionalLightsShadowData[shadowCastingLightIndex].worldToShadowMatrix = shadowTransform;
                                m_AdditionalLightsShadowData[shadowCastingLightIndex].shadowParams        = shadowParams;
                            }
                            else
                            {
                                m_AdditionalLightsWorldToShadow[shadowCastingLightIndex] = shadowTransform;
                                m_AdditionalLightsShadowParams[shadowCastingLightIndex]  = shadowParams;
                            }
                            isValidShadowSlice = true;
                            validShadowCastingLights++;
                        }
                    }
                }

                if (m_UseStructuredBuffer)
                {
                    // When using StructuredBuffers all the valid shadow casting slices data
                    // are stored in a the ShadowData buffer and then we setup a index map to
                    // map from light indices to shadow buffer index. A index map of -1 means
                    // the light is not a valid shadow casting light and there's no data for it
                    // in the shadow buffer.
                    int indexMap = (isValidShadowSlice) ? shadowCastingLightIndex : -1;
                    m_AdditionalShadowCastingLightIndicesMap.Add(indexMap);
                }
                else if (!isValidShadowSlice)
                {
                    // When NOT using structured buffers we have no performant way to sample the
                    // index map as int[]. Unity shader compiler converts int[] to float4[] to force memory alignment.
                    // This makes indexing int[] arrays very slow. So, in order to avoid indexing shadow lights we
                    // setup slice data and reserve shadow map space even for invalid shadow slices.
                    // The data is setup with zero shadow strength. This has the same visual effect of no shadow
                    // attenuation contribution from this light.
                    // This makes sampling shadow faster but introduces waste in shadow map atlas.
                    // The waste increases with the amount of additional lights to shade.
                    // Therefore Universal RP try to keep the limit at sane levels when using uniform buffers.
                    Matrix4x4 identity = Matrix4x4.identity;
                    m_AdditionalShadowCastingLightIndices.Add(i);
                    m_AdditionalLightsWorldToShadow[shadowCastingLightIndex]          = identity;
                    m_AdditionalLightsShadowParams[shadowCastingLightIndex]           = Vector4.zero;
                    m_AdditionalLightSlices[shadowCastingLightIndex].viewMatrix       = identity;
                    m_AdditionalLightSlices[shadowCastingLightIndex].projectionMatrix = identity;
                }

                m_ShadowCastingLightIndicesMap[i] = isValidShadowSlice ? shadowCastingLightIndex : -1;
            }

            // Lights that need to be rendered in the shadow map atlas
            if (validShadowCastingLights == 0)
            {
                return(false);
            }

            int atlasWidth      = renderingData.shadowData.additionalLightsShadowmapWidth;
            int atlasHeight     = renderingData.shadowData.additionalLightsShadowmapHeight;
            int sliceResolution = ShadowUtils.GetMaxTileResolutionInAtlas(atlasWidth, atlasHeight, validShadowCastingLights);

            // In the UI we only allow for square shadow map atlas. Here we check if we can fit
            // all shadow slices into half resolution of the atlas and adjust height to have tighter packing.
            int maximumSlices = (m_ShadowmapWidth / sliceResolution) * (m_ShadowmapHeight / sliceResolution);

            if (validShadowCastingLights <= (maximumSlices / 2))
            {
                m_ShadowmapHeight /= 2;
            }

            int   shadowSlicesPerRow = (atlasWidth / sliceResolution);
            float oneOverAtlasWidth  = 1.0f / m_ShadowmapWidth;
            float oneOverAtlasHeight = 1.0f / m_ShadowmapHeight;

            int       sliceIndex = 0;
            int       shadowCastingLightsBufferCount = m_AdditionalShadowCastingLightIndices.Count;
            Matrix4x4 sliceTransform = Matrix4x4.identity;

            sliceTransform.m00 = sliceResolution * oneOverAtlasWidth;
            sliceTransform.m11 = sliceResolution * oneOverAtlasHeight;

            for (int i = 0; i < shadowCastingLightsBufferCount; ++i)
            {
                // we can skip the slice if strength is zero. Some slices with zero
                // strength exists when using uniform array path.
                if (!m_UseStructuredBuffer && Mathf.Approximately(m_AdditionalLightsShadowParams[i].x, 0.0f))
                {
                    continue;
                }

                m_AdditionalLightSlices[i].offsetX    = (sliceIndex % shadowSlicesPerRow) * sliceResolution;
                m_AdditionalLightSlices[i].offsetY    = (sliceIndex / shadowSlicesPerRow) * sliceResolution;
                m_AdditionalLightSlices[i].resolution = sliceResolution;

                sliceTransform.m03 = m_AdditionalLightSlices[i].offsetX * oneOverAtlasWidth;
                sliceTransform.m13 = m_AdditionalLightSlices[i].offsetY * oneOverAtlasHeight;

                // We bake scale and bias to each shadow map in the atlas in the matrix.
                // saves some instructions in shader.
                if (m_UseStructuredBuffer)
                {
                    m_AdditionalLightsShadowData[i].worldToShadowMatrix = sliceTransform * m_AdditionalLightsShadowData[i].worldToShadowMatrix;
                }
                else
                {
                    m_AdditionalLightsWorldToShadow[i] = sliceTransform * m_AdditionalLightsWorldToShadow[i];
                }
                sliceIndex++;
            }

            return(true);
        }
        void RenderAdditionalShadowmapAtlas(ref ScriptableRenderContext context, ref CullingResults cullResults, ref LightData lightData, ref ShadowData shadowData)
        {
            NativeArray <VisibleLight> visibleLights = lightData.visibleLights;

            bool additionalLightHasSoftShadows = false;
            // NOTE: Do NOT mix ProfilingScope with named CommandBuffers i.e. CommandBufferPool.Get("name").
            // Currently there's an issue which results in mismatched markers.
            CommandBuffer cmd = CommandBufferPool.Get();

            using (new ProfilingScope(cmd, m_ProfilingSampler))
            {
                bool anyShadowSliceRenderer = false;
                int  shadowSlicesCount      = m_AdditionalShadowCastingLightIndices.Count;
                for (int i = 0; i < shadowSlicesCount; ++i)
                {
                    // we do the shadow strength check here again here because when using
                    // the uniform array path we might have zero strength shadow lights.
                    // In that case we need the shadow data buffer but we can skip
                    // rendering them to shadowmap.
                    if (!m_UseStructuredBuffer && Mathf.Approximately(m_AdditionalLightsShadowParams[i].x, 0.0f))
                    {
                        continue;
                    }

                    // Index of the VisibleLight
                    int          shadowLightIndex = m_AdditionalShadowCastingLightIndices[i];
                    VisibleLight shadowLight      = visibleLights[shadowLightIndex];

                    ShadowSliceData shadowSliceData = m_AdditionalLightSlices[i];

                    var     settings   = new ShadowDrawingSettings(cullResults, shadowLightIndex);
                    Vector4 shadowBias = ShadowUtils.GetShadowBias(ref shadowLight, shadowLightIndex,
                                                                   ref shadowData, shadowSliceData.projectionMatrix, shadowSliceData.resolution);
                    ShadowUtils.SetupShadowCasterConstantBuffer(cmd, ref shadowLight, shadowBias);
                    ShadowUtils.RenderShadowSlice(cmd, ref context, ref shadowSliceData, ref settings);
                    additionalLightHasSoftShadows |= shadowLight.light.shadows == LightShadows.Soft;
                    anyShadowSliceRenderer         = true;
                }

                // We share soft shadow settings for main light and additional lights to save keywords.
                // So we check here if pipeline supports soft shadows and either main light or any additional light has soft shadows
                // to enable the keyword.
                // TODO: In PC and Consoles we can upload shadow data per light and branch on shader. That will be more likely way faster.
                bool mainLightHasSoftShadows = shadowData.supportsMainLightShadows &&
                                               lightData.mainLightIndex != -1 &&
                                               visibleLights[lightData.mainLightIndex].light.shadows ==
                                               LightShadows.Soft;

                bool softShadows = shadowData.supportsSoftShadows &&
                                   (mainLightHasSoftShadows || additionalLightHasSoftShadows);

                CoreUtils.SetKeyword(cmd, ShaderKeywordStrings.AdditionalLightShadows, anyShadowSliceRenderer);
                CoreUtils.SetKeyword(cmd, ShaderKeywordStrings.SoftShadows, softShadows);

                if (anyShadowSliceRenderer)
                {
                    SetupAdditionalLightsShadowReceiverConstants(cmd, ref shadowData, softShadows);
                }
            }

            context.ExecuteCommandBuffer(cmd);
            CommandBufferPool.Release(cmd);
        }
Exemple #19
0
    // Setup lighting variables for shader to use

    private static void SetupLightShaderVariables(List <VisibleLight> lights, ScriptableRenderContext context)
    {
        // We only support up to 8 visible lights here. More complex approaches would
        // be doing some sort of per-object light setups, but here we go for simplest possible
        // approach.
        const int kMaxLights = 8;
        // Just take first 8 lights. Possible improvements: sort lights by intensity or distance
        // to the viewer, so that "most important" lights in the scene are picked, and not the 8
        // that happened to be first.
        int lightCount = Mathf.Min(lights.Count, kMaxLights);

        // Prepare light data
        Vector4[] lightColors         = new Vector4[kMaxLights];
        Vector4[] lightPositions      = new Vector4[kMaxLights];
        Vector4[] lightSpotDirections = new Vector4[kMaxLights];
        Vector4[] lightAtten          = new Vector4[kMaxLights];
        for (var i = 0; i < lightCount; ++i)
        {
            VisibleLight light = lights[i];
            lightColors[i] = light.finalColor;
            if (light.lightType == LightType.Directional)
            {
                // light position for directional lights is: (-direction, 0)
                var dir = light.localToWorld.GetColumn(2);
                lightPositions[i] = new Vector4(-dir.x, -dir.y, -dir.z, 0);
            }
            else
            {
                // light position for point/spot lights is: (position, 1)
                var pos = light.localToWorld.GetColumn(3);
                lightPositions[i] = new Vector4(pos.x, pos.y, pos.z, 1);
            }
            // attenuation set in a way where distance attenuation can be computed:
            //  float lengthSq = dot(toLight, toLight);
            //  float atten = 1.0 / (1.0 + lengthSq * LightAtten[i].z);
            // and spot cone attenuation:
            //  float rho = max (0, dot(normalize(toLight), SpotDirection[i].xyz));
            //  float spotAtt = (rho - LightAtten[i].x) * LightAtten[i].y;
            //  spotAtt = saturate(spotAtt);
            // and the above works for all light types, i.e. spot light code works out
            // to correct math for point & directional lights as well.

            float rangeSq = light.range * light.range;

            float quadAtten = (light.lightType == LightType.Directional) ? 0.0f : 25.0f / rangeSq;

            // spot direction & attenuation
            if (light.lightType == LightType.Spot)
            {
                var dir = light.localToWorld.GetColumn(2);
                lightSpotDirections[i] = new Vector4(-dir.x, -dir.y, -dir.z, 0);

                float radAngle = Mathf.Deg2Rad * light.spotAngle;
                float cosTheta = Mathf.Cos(radAngle * 0.25f);
                float cosPhi   = Mathf.Cos(radAngle * 0.5f);
                float cosDiff  = cosTheta - cosPhi;
                lightAtten[i] = new Vector4(cosPhi, (cosDiff != 0.0f) ? 1.0f / cosDiff : 1.0f, quadAtten, rangeSq);
            }
            else
            {
                // non-spot light
                lightSpotDirections[i] = new Vector4(0, 0, 1, 0);
                lightAtten[i]          = new Vector4(-1, 1, quadAtten, rangeSq);
            }
        }

        // ambient lighting spherical harmonics values
        const int kSHCoefficients = 7;

        Vector4[]            shConstants = new Vector4[kSHCoefficients];
        SphericalHarmonicsL2 ambientSH   = RenderSettings.ambientProbe * RenderSettings.ambientIntensity;

        GetShaderConstantsFromNormalizedSH(ref ambientSH, shConstants);

        // setup global shader variables to contain all the data computed above
        CommandBuffer cmd = CommandBufferPool.Get();

        cmd.SetGlobalVectorArray("globalLightColor", lightColors);
        cmd.SetGlobalVectorArray("globalLightPos", lightPositions);
        cmd.SetGlobalVectorArray("globalLightSpotDir", lightSpotDirections);
        cmd.SetGlobalVectorArray("globalLightAtten", lightAtten);
        cmd.SetGlobalVector("globalLightCount", new Vector4(lightCount, 0, 0, 0));
        cmd.SetGlobalVectorArray("globalSH", shConstants);
        context.ExecuteCommandBuffer(cmd);
        CommandBufferPool.Release(cmd);
    }
        // TODO: box spot and pyramid spots with non 1 aspect ratios shadow are incorrectly culled, see when scriptable culling will be here
        public static void ExtractSpotLightData(HDCamera camera, LightType lightType, SpotLightShape shape, float nearPlane, float aspectRatio, float shapeWidth, float shapeHeight, VisibleLight visibleLight, Vector2 viewportSize, float normalBiasMax, out Matrix4x4 view, out Matrix4x4 invViewProjection, out Matrix4x4 projection, out Matrix4x4 deviceProjection, out ShadowSplitData splitData)
        {
            Vector4 lightDir;

            // There is no aspect ratio for non pyramid spot lights
            if (shape != SpotLightShape.Pyramid)
            {
                aspectRatio = 1.0f;
            }

            float guardAngle = CalcGuardAnglePerspective(visibleLight.light.spotAngle, viewportSize.x, GetPunctualFilterWidthInTexels(camera, lightType), normalBiasMax, 180.0f - visibleLight.light.spotAngle);

            ExtractSpotLightMatrix(visibleLight, nearPlane, guardAngle, aspectRatio, out view, out projection, out deviceProjection, out invViewProjection, out lightDir, out splitData);

            if (shape == SpotLightShape.Box)
            {
                float nearZ = Mathf.Max(nearPlane, k_MinShadowNearPlane);
                projection       = Matrix4x4.Ortho(-shapeWidth / 2, shapeWidth / 2, -shapeHeight / 2, shapeHeight / 2, nearZ, visibleLight.range);
                deviceProjection = GL.GetGPUProjectionMatrix(projection, false);
                InvertOrthographic(ref projection, ref view, out invViewProjection);
            }
        }
        public static void ExtractPointLightData(HDCamera camera, LightType lightType, VisibleLight visibleLight, Vector2 viewportSize, float nearPlane, float normalBiasMax, uint faceIndex, out Matrix4x4 view, out Matrix4x4 invViewProjection, out Matrix4x4 projection, out Matrix4x4 deviceProjection, out ShadowSplitData splitData)
        {
            Vector4 lightDir;

            float guardAngle = CalcGuardAnglePerspective(90.0f, viewportSize.x, GetPunctualFilterWidthInTexels(camera, lightType), normalBiasMax, 79.0f);

            ExtractPointLightMatrix(visibleLight, faceIndex, nearPlane, guardAngle, out view, out projection, out deviceProjection, out invViewProjection, out lightDir, out splitData);
        }
Exemple #22
0
    void SetupLights(bool useLightsPerObject, int renderingLayerMask)
    {
        NativeArray <int>          indexMap = useLightsPerObject ? cullingResults.GetLightIndexMap(Allocator.Temp) : default;
        NativeArray <VisibleLight> visibleLights = cullingResults.visibleLights;
        int dirLightCount = 0, otherLightCount = 0;
        int i;

        for (i = 0; i < visibleLights.Length; i++)
        {
            int          newIndex     = -1;
            VisibleLight visibleLight = visibleLights[i];
            Light        light        = visibleLight.light;
            if ((light.renderingLayerMask & renderingLayerMask) != 0)
            {
                switch (visibleLight.lightType)
                {
                case LightType.Directional:
                    if (dirLightCount < maxDirLightCount)
                    {
                        SetupDirectionalLight(dirLightCount++, i, ref visibleLight, light);
                    }
                    break;

                case LightType.Point:
                    if (otherLightCount < maxOtherLightCount)
                    {
                        newIndex = otherLightCount;
                        SetupPointLight(otherLightCount++, i, ref visibleLight, light);
                    }
                    break;

                case LightType.Spot:
                    if (otherLightCount < maxOtherLightCount)
                    {
                        newIndex = otherLightCount;
                        SetupSpotLight(otherLightCount++, i, ref visibleLight, light);
                    }
                    break;
                }
            }
            if (useLightsPerObject)
            {
                indexMap[i] = newIndex;
            }
        }

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

        buffer.SetGlobalInt(dirLightCountId, dirLightCount);
        if (dirLightCount > 0)
        {
            buffer.SetGlobalVectorArray(dirLightColorsId, dirLightColors);
            buffer.SetGlobalVectorArray(dirLightDirectionsId, dirLightDirectionsAndMasks);
            buffer.SetGlobalVectorArray(dirLightShadowDataId, dirLightShadowData);
        }

        buffer.SetGlobalInt(otherLightCountId, otherLightCount);
        if (otherLightCount > 0)
        {
            buffer.SetGlobalVectorArray(otherLightColorsId, otherLightColors);
            buffer.SetGlobalVectorArray(otherLightPositionsId, otherLightPositions);
            buffer.SetGlobalVectorArray(otherLightDirectionsId, otherLightDirectionsAndMasks);
            buffer.SetGlobalVectorArray(otherLightSpotAnglesId, otherLightSpotAngles);
            buffer.SetGlobalVectorArray(otherLightShadowDataId, otherLightShadowData);
        }
    }
Exemple #23
0
        public static Vector4 GetShadowBias(ref VisibleLight shadowLight, int shadowLightIndex, ref ShadowData shadowData, Matrix4x4 lightProjectionMatrix, float shadowResolution)
        {
            if (shadowLightIndex < 0 || shadowLightIndex >= shadowData.bias.Count)
            {
                Debug.LogWarning(string.Format("{0} is not a valid light index.", shadowLightIndex));
                return(Vector4.zero);
            }

            float frustumSize;

            if (shadowLight.lightType == LightType.Directional)
            {
                // Frustum size is guaranteed to be a cube as we wrap shadow frustum around a sphere
                frustumSize = 2.0f / lightProjectionMatrix.m00;
            }
            else if (shadowLight.lightType == LightType.Spot)
            {
                // For perspective projections, shadow texel size varies with depth
                // It will only work well if done in receiver side in the pixel shader. Currently UniversalRP
                // do bias on caster side in vertex shader. When we add shader quality tiers we can properly
                // handle this. For now, as a poor approximation we do a constant bias and compute the size of
                // the frustum as if it was orthogonal considering the size at mid point between near and far planes.
                // Depending on how big the light range is, it will be good enough with some tweaks in bias
                frustumSize = Mathf.Tan(shadowLight.spotAngle * 0.5f * Mathf.Deg2Rad) * shadowLight.range; // half-width (in world-space units) of shadow frustum's "far plane"
            }
            else if (shadowLight.lightType == LightType.Point)
            {
                // [Copied from above case:]
                // "For perspective projections, shadow texel size varies with depth
                //  It will only work well if done in receiver side in the pixel shader. Currently UniversalRP
                //  do bias on caster side in vertex shader. When we add shader quality tiers we can properly
                //  handle this. For now, as a poor approximation we do a constant bias and compute the size of
                //  the frustum as if it was orthogonal considering the size at mid point between near and far planes.
                //  Depending on how big the light range is, it will be good enough with some tweaks in bias"
                // Note: HDRP uses normalBias both in HDShadowUtils.CalcGuardAnglePerspective and HDShadowAlgorithms/EvalShadow_NormalBias (receiver bias)
                float fovBias = Internal.AdditionalLightsShadowCasterPass.GetPointLightShadowFrustumFovBiasInDegrees((int)shadowResolution, (shadowLight.light.shadows == LightShadows.Soft));
                // Note: the same fovBias was also used to compute ShadowUtils.ExtractPointLightMatrix
                float cubeFaceAngle = 90 + fovBias;
                frustumSize = Mathf.Tan(cubeFaceAngle * 0.5f * Mathf.Deg2Rad) * shadowLight.range; // half-width (in world-space units) of shadow frustum's "far plane"
            }
            else
            {
                Debug.LogWarning("Only point, spot and directional shadow casters are supported in universal pipeline");
                frustumSize = 0.0f;
            }

            // depth and normal bias scale is in shadowmap texel size in world space
            float texelSize  = frustumSize / shadowResolution;
            float depthBias  = -shadowData.bias[shadowLightIndex].x * texelSize;
            float normalBias = -shadowData.bias[shadowLightIndex].y * texelSize;

            // The current implementation of NormalBias in Universal RP is the same as in Unity Built-In RP (i.e moving shadow caster vertices along normals when projecting them to the shadow map).
            // This does not work well with Point Lights, which is why NormalBias value is hard-coded to 0.0 in Built-In RP (see value of unity_LightShadowBias.z in FrameDebugger, and native code that sets it: https://github.cds.internal.unity3d.com/unity/unity/blob/a9c916ba27984da43724ba18e70f51469e0c34f5/Runtime/Camera/Shadows.cpp#L1686 )
            // We follow the same convention in Universal RP:
            if (shadowLight.lightType == LightType.Point)
            {
                normalBias = 0.0f;
            }

            if (shadowData.supportsSoftShadows && shadowLight.light.shadows == LightShadows.Soft)
            {
                // TODO: depth and normal bias assume sample is no more than 1 texel away from shadowmap
                // This is not true with PCF. Ideally we need to do either
                // cone base bias (based on distance to center sample)
                // or receiver place bias based on derivatives.
                // For now we scale it by the PCF kernel size of non-mobile platforms (5x5)
                const float kernelRadius = 2.5f;
                depthBias  *= kernelRadius;
                normalBias *= kernelRadius;
            }

            return(new Vector4(depthBias, normalBias, 0.0f, 0.0f));
        }
Exemple #24
0
        void InitializeLightConstants(NativeArray <VisibleLight> lights, int lightIndex, out Vector4 lightPos, out Vector4 lightColor, out Vector4 lightAttenuation, out Vector4 lightSpotDir, out Vector4 lightOcclusionProbeChannel)
        {
            lightPos                   = k_DefaultLightPosition;
            lightColor                 = k_DefaultLightColor;
            lightAttenuation           = k_DefaultLightAttenuation;
            lightSpotDir               = k_DefaultLightSpotDirection;
            lightOcclusionProbeChannel = k_DefaultLightsProbeChannel;

            // When no lights are visible, main light will be set to -1.
            // In this case we initialize it to default values and return
            if (lightIndex < 0)
            {
                return;
            }

            VisibleLight lightData = lights[lightIndex];

            if (lightData.lightType == LightType.Directional)
            {
                Vector4 dir = -lightData.localToWorldMatrix.GetColumn(2);
                lightPos = new Vector4(dir.x, dir.y, dir.z, 0.0f);
            }
            else
            {
                Vector4 pos = lightData.localToWorldMatrix.GetColumn(3);
                lightPos = new Vector4(pos.x, pos.y, pos.z, 1.0f);
            }

            // VisibleLight.finalColor already returns color in active color space
            lightColor = lightData.finalColor;

            // Directional Light attenuation is initialize so distance attenuation always be 1.0
            if (lightData.lightType != LightType.Directional)
            {
                // Light attenuation in universal matches the unity vanilla one.
                // attenuation = 1.0 / distanceToLightSqr
                // We offer two different smoothing factors.
                // The smoothing factors make sure that the light intensity is zero at the light range limit.
                // The first smoothing factor is a linear fade starting at 80 % of the light range.
                // smoothFactor = (lightRangeSqr - distanceToLightSqr) / (lightRangeSqr - fadeStartDistanceSqr)
                // We rewrite smoothFactor to be able to pre compute the constant terms below and apply the smooth factor
                // with one MAD instruction
                // smoothFactor =  distanceSqr * (1.0 / (fadeDistanceSqr - lightRangeSqr)) + (-lightRangeSqr / (fadeDistanceSqr - lightRangeSqr)
                //                 distanceSqr *           oneOverFadeRangeSqr             +              lightRangeSqrOverFadeRangeSqr

                // The other smoothing factor matches the one used in the Unity lightmapper but is slower than the linear one.
                // smoothFactor = (1.0 - saturate((distanceSqr * 1.0 / lightrangeSqr)^2))^2
                float lightRangeSqr                 = lightData.range * lightData.range;
                float fadeStartDistanceSqr          = 0.8f * 0.8f * lightRangeSqr;
                float fadeRangeSqr                  = (fadeStartDistanceSqr - lightRangeSqr);
                float oneOverFadeRangeSqr           = 1.0f / fadeRangeSqr;
                float lightRangeSqrOverFadeRangeSqr = -lightRangeSqr / fadeRangeSqr;
                float oneOverLightRangeSqr          = 1.0f / Mathf.Max(0.0001f, lightData.range * lightData.range);

                // On mobile and Nintendo Switch: Use the faster linear smoothing factor (SHADER_HINT_NICE_QUALITY).
                // On other devices: Use the smoothing factor that matches the GI.
                lightAttenuation.x = Application.isMobilePlatform || SystemInfo.graphicsDeviceType == GraphicsDeviceType.Switch ? oneOverFadeRangeSqr : oneOverLightRangeSqr;
                lightAttenuation.y = lightRangeSqrOverFadeRangeSqr;
            }

            if (lightData.lightType == LightType.Spot)
            {
                Vector4 dir = lightData.localToWorldMatrix.GetColumn(2);
                lightSpotDir = new Vector4(-dir.x, -dir.y, -dir.z, 0.0f);

                // Spot Attenuation with a linear falloff can be defined as
                // (SdotL - cosOuterAngle) / (cosInnerAngle - cosOuterAngle)
                // This can be rewritten as
                // invAngleRange = 1.0 / (cosInnerAngle - cosOuterAngle)
                // SdotL * invAngleRange + (-cosOuterAngle * invAngleRange)
                // If we precompute the terms in a MAD instruction
                float cosOuterAngle = Mathf.Cos(Mathf.Deg2Rad * lightData.spotAngle * 0.5f);
                // We neeed to do a null check for particle lights
                // This should be changed in the future
                // Particle lights will use an inline function
                float cosInnerAngle;
                if (lightData.light != null)
                {
                    cosInnerAngle = Mathf.Cos(lightData.light.innerSpotAngle * Mathf.Deg2Rad * 0.5f);
                }
                else
                {
                    cosInnerAngle = Mathf.Cos((2.0f * Mathf.Atan(Mathf.Tan(lightData.spotAngle * 0.5f * Mathf.Deg2Rad) * (64.0f - 18.0f) / 64.0f)) * 0.5f);
                }
                float smoothAngleRange = Mathf.Max(0.001f, cosInnerAngle - cosOuterAngle);
                float invAngleRange    = 1.0f / smoothAngleRange;
                float add = -cosOuterAngle * invAngleRange;
                lightAttenuation.z = invAngleRange;
                lightAttenuation.w = add;
            }

            Light light = lightData.light;

            // Set the occlusion probe channel.
            int occlusionProbeChannel = light != null ? light.bakingOutput.occlusionMaskChannel : -1;

            // If we have baked the light, the occlusion channel is the index we need to sample in 'unity_ProbesOcclusion'
            // If we have not baked the light, the occlusion channel is -1.
            // In case there is no occlusion channel is -1, we set it to zero, and then set the second value in the
            // input to one. We then, in the shader max with the second value for non-occluded lights.
            lightOcclusionProbeChannel.x = occlusionProbeChannel == -1 ? 0f : occlusionProbeChannel;
            lightOcclusionProbeChannel.y = occlusionProbeChannel == -1 ? 1f : 0f;

            // TODO: Add support to shadow mask
            if (light != null && light.bakingOutput.mixedLightingMode == MixedLightingMode.Subtractive && light.bakingOutput.lightmapBakeType == LightmapBakeType.Mixed)
            {
                if (lightData.light.shadows != LightShadows.None)
                {
                    m_MixedLightingSetup = MixedLightingSetup.Subtractive;
                }
            }
            if (light != null && light.bakingOutput.mixedLightingMode == MixedLightingMode.Shadowmask && light.bakingOutput.lightmapBakeType == LightmapBakeType.Mixed)
            {
                if (lightData.light.shadows != LightShadows.None)
                {
                    m_MixedLightingSetup = MixedLightingSetup.ShadowMask;
                }
                int channel = light.bakingOutput.occlusionMaskChannel;
                lightSpotDir.w = channel + 1;
            }
        }
Exemple #25
0
    private void SetUpRealtimeLightingVariables(Camera cam, ScriptableRenderContext context, CullingResults cull)
    {
        for (var i = 0; i < lightCount; i++)
        {
            lightColor[i]   = Vector4.zero;
            lightData[i]    = Vector4.zero;
            lightSpotDir[i] = Vector4.zero;

            if (i >= cull.visibleLights.Length)
            {
                continue;
            }
            VisibleLight light = cull.visibleLights[i];

            if (light.lightType == LightType.Directional)
            {
                lightData[i]    = light.localToWorldMatrix.MultiplyVector(Vector3.back);
                lightColor[i]   = light.finalColor;
                lightColor[i].w = -1;                                             //for identifying it is a directional light in shader

                SetUpRealtimeShadowVariables(cam, context, cull, light.light, i); //setup shadow
            }
            else if (light.lightType == LightType.Point)
            {
                lightData[i]    = light.localToWorldMatrix.GetColumn(3);
                lightData[i].w  = light.range;
                lightColor[i]   = light.finalColor;
                lightColor[i].w = -2; //for identifying it is a point light in shader
            }
            else if (light.lightType == LightType.Spot)
            {
                lightData[i]   = light.localToWorldMatrix.GetColumn(3);
                lightData[i].w = 1f / Mathf.Max(light.range * light.range, 0.00001f);

                lightSpotDir[i]   = light.localToWorldMatrix.GetColumn(2);
                lightSpotDir[i].x = -lightSpotDir[i].x;
                lightSpotDir[i].y = -lightSpotDir[i].y;
                lightSpotDir[i].z = -lightSpotDir[i].z;
                lightColor[i]     = light.finalColor;

                float outerRad   = Mathf.Deg2Rad * 0.5f * light.spotAngle;
                float outerCos   = Mathf.Cos(outerRad);
                float outerTan   = Mathf.Tan(outerRad);
                float innerCos   = Mathf.Cos(Mathf.Atan(((46f / 64f) * outerTan)));
                float angleRange = Mathf.Max(innerCos - outerCos, 0.001f);

                //Spotlight attenuation
                lightSpotDir[i].w = 1f / angleRange;
                lightColor[i].w   = -outerCos * lightSpotDir[i].w;
            }
            else
            {
                // If it's not a point / directional / spot light, we ignore the light.
                continue;
            }
        }

        CommandBuffer cmdLight = CommandBufferPool.Get("Set-up Light Buffer");

        cmdLight.SetGlobalVectorArray(lightDataID, lightData);
        cmdLight.SetGlobalVectorArray(lightColorID, lightColor);
        cmdLight.SetGlobalVectorArray(lightSpotDirID, lightSpotDir);
        context.ExecuteCommandBuffer(cmdLight);
        CommandBufferPool.Release(cmdLight);
    }
        void InitializeLightConstants(List <VisibleLight> lights, int lightIndex, out Vector4 lightPos, out Vector4 lightColor, out Vector4 lightDistanceAttenuation, out Vector4 lightSpotDir,
                                      out Vector4 lightSpotAttenuation)
        {
            lightPos   = k_DefaultLightPosition;
            lightColor = k_DefaultLightColor;
            lightDistanceAttenuation = k_DefaultLightSpotAttenuation;
            lightSpotDir             = k_DefaultLightSpotDirection;
            lightSpotAttenuation     = k_DefaultLightAttenuation;

            // When no lights are visible, main light will be set to -1.
            // In this case we initialize it to default values and return
            if (lightIndex < 0)
            {
                return;
            }

            VisibleLight lightData = lights[lightIndex];

            if (lightData.lightType == LightType.Directional)
            {
                Vector4 dir = -lightData.localToWorld.GetColumn(2);
                lightPos = new Vector4(dir.x, dir.y, dir.z, 0.0f);
            }
            else
            {
                Vector4 pos = lightData.localToWorld.GetColumn(3);
                lightPos = new Vector4(pos.x, pos.y, pos.z, 1.0f);
            }

            // VisibleLight.finalColor already returns color in active color space
            lightColor = lightData.finalColor;

            // Directional Light attenuation is initialize so distance attenuation always be 1.0
            if (lightData.lightType != LightType.Directional)
            {
                // Light attenuation in lightweight matches the unity vanilla one.
                // attenuation = 1.0 / 1.0 + distanceToLightSqr * quadraticAttenuation
                // then a smooth factor is applied to linearly fade attenuation to light range
                // the attenuation smooth factor starts having effect at 80% of light range
                // smoothFactor = (lightRangeSqr - distanceToLightSqr) / (lightRangeSqr - fadeStartDistanceSqr)
                // We rewrite smoothFactor to be able to pre compute the constant terms below and apply the smooth factor
                // with one MAD instruction
                // smoothFactor =  distanceSqr * (1.0 / (fadeDistanceSqr - lightRangeSqr)) + (-lightRangeSqr / (fadeDistanceSqr - lightRangeSqr)
                //                 distanceSqr *           oneOverFadeRangeSqr             +              lightRangeSqrOverFadeRangeSqr
                float lightRangeSqr                 = lightData.range * lightData.range;
                float fadeStartDistanceSqr          = 0.8f * 0.8f * lightRangeSqr;
                float fadeRangeSqr                  = (fadeStartDistanceSqr - lightRangeSqr);
                float oneOverFadeRangeSqr           = 1.0f / fadeRangeSqr;
                float lightRangeSqrOverFadeRangeSqr = -lightRangeSqr / fadeRangeSqr;
                float quadAtten = 25.0f / lightRangeSqr;
                lightDistanceAttenuation = new Vector4(quadAtten, oneOverFadeRangeSqr, lightRangeSqrOverFadeRangeSqr, 1.0f);
            }

            if (lightData.lightType == LightType.Spot)
            {
                Vector4 dir = lightData.localToWorld.GetColumn(2);
                lightSpotDir = new Vector4(-dir.x, -dir.y, -dir.z, 0.0f);

                // Spot Attenuation with a linear falloff can be defined as
                // (SdotL - cosOuterAngle) / (cosInnerAngle - cosOuterAngle)
                // This can be rewritten as
                // invAngleRange = 1.0 / (cosInnerAngle - cosOuterAngle)
                // SdotL * invAngleRange + (-cosOuterAngle * invAngleRange)
                // If we precompute the terms in a MAD instruction
                float cosOuterAngle = Mathf.Cos(Mathf.Deg2Rad * lightData.spotAngle * 0.5f);
                // We neeed to do a null check for particle lights
                // This should be changed in the future
                // Particle lights will use an inline function
                float cosInnerAngle;
                if (lightData.light != null)
                {
                    cosInnerAngle = Mathf.Cos(LightmapperUtils.ExtractInnerCone(lightData.light) * 0.5f);
                }
                else
                {
                    cosInnerAngle = Mathf.Cos((2.0f * Mathf.Atan(Mathf.Tan(lightData.spotAngle * 0.5f * Mathf.Deg2Rad) * (64.0f - 18.0f) / 64.0f)) * 0.5f);
                }
                float smoothAngleRange = Mathf.Max(0.001f, cosInnerAngle - cosOuterAngle);
                float invAngleRange    = 1.0f / smoothAngleRange;
                float add = -cosOuterAngle * invAngleRange;
                lightSpotAttenuation = new Vector4(invAngleRange, add, 0.0f);
            }

            Light light = lightData.light;

            // TODO: Add support to shadow mask
            if (light != null && light.bakingOutput.mixedLightingMode == MixedLightingMode.Subtractive && light.bakingOutput.lightmapBakeType == LightmapBakeType.Mixed)
            {
                if (m_MixedLightingSetup == MixedLightingSetup.None && lightData.light.shadows != LightShadows.None)
                {
                    m_MixedLightingSetup       = MixedLightingSetup.Subtractive;
                    lightDistanceAttenuation.w = 0.0f;
                }
            }
        }
Exemple #27
0
    void ConfigureLights()
    {
        mainLightExists = false;
        bool shadowmaskExists    = false;
        bool subtractiveLighting = false;

        shadowTileCount = 0;

        for (int i = 0; i < cull.visibleLights.Count; i++)
        {
            if (i == maxVisibleLights)
            {
                break;
            }
            VisibleLight light = cull.visibleLights[i];
            visibleLightColors[i] = light.finalColor;

            Vector4 attenuation = Vector4.zero;
            attenuation.w = 1f;   //if no spot light, factor =1

            Vector4 shadow = Vector4.zero;

            //shadowmask settings
            LightBakingOutput baking = light.light.bakingOutput;
            visibleLightOcclusionMasks[i] =
                occlusionMasks[baking.occlusionMaskChannel + 1];
            if (baking.lightmapBakeType == LightmapBakeType.Mixed)
            {
                shadowmaskExists |=
                    baking.mixedLightingMode == MixedLightingMode.Shadowmask;

                if (baking.mixedLightingMode == MixedLightingMode.Subtractive)
                {
                    subtractiveLighting = true;
                    cameraBuffer.SetGlobalColor(
                        subtractiveShadowColorId,
                        RenderSettings.subtractiveShadowColor.linear
                        );
                }
            }


            //light settings
            if (light.lightType == LightType.Directional)
            {
                Vector4 v = light.localToWorld.GetColumn(2);
                v.x = -v.x;
                v.y = -v.y;
                v.z = -v.z;  //change to vector surfaceToLight
                visibleLightDirectionsOrPositions[i] = v;

                shadow   = ConfigureShadows(i, light.light);
                shadow.z = 1f;

                //directional light index =0, cast shadow, use cascades
                if (i == 0 && shadow.x > 0f && shadowCascades > 0)
                {
                    mainLightExists  = true;
                    shadowTileCount -= 1;   //cascaded shadow map is not included in tiled shadow map
                }
            }
            else
            {
                visibleLightDirectionsOrPositions[i] = light.localToWorld.GetColumn(3);
                attenuation.x = 1f /
                                Mathf.Max(light.range * light.range, 0.00001f);

                if (light.lightType == LightType.Spot)
                {
                    Vector4 v = light.localToWorld.GetColumn(2);
                    v.x = -v.x;
                    v.y = -v.y;
                    v.z = -v.z;
                    visibleLightSpotDirections[i] = v;

                    float outerRad = Mathf.Deg2Rad * 0.5f * light.spotAngle;
                    float outerCos = Mathf.Cos(outerRad);
                    float outerTan = Mathf.Tan(outerRad);
                    float innerCos =
                        Mathf.Cos(Mathf.Atan(((46f / 64f) * outerTan)));
                    float angleRange = Mathf.Max(innerCos - outerCos, 0.001f);
                    attenuation.z = 1f / angleRange;
                    attenuation.w = -outerCos * attenuation.z;

                    //buffer shadowData
                    shadow = ConfigureShadows(i, light.light);
                }
                else
                {
                    //indicator of pointlight
                    visibleLightSpotDirections[i] = Vector4.one;
                }
            }

            visibleLightAttenuations[i] = attenuation;
            shadowData[i] = shadow;
        }

        //number of lights exceeds upper limit
        if (mainLightExists || cull.visibleLights.Count > maxVisibleLights)
        {
            int[] lightIndices = cull.GetLightIndexMap();
            if (mainLightExists)
            {
                //remove main light from diffuseLight render, avoid a second render
                lightIndices[0] = -1;
            }
            for (int i = maxVisibleLights; i < cull.visibleLights.Count; i++)
            {
                lightIndices[i] = -1;
            }
            cull.SetLightIndexMap(lightIndices);
        }

        bool useDistanceShadowmask =
            QualitySettings.shadowmaskMode == ShadowmaskMode.DistanceShadowmask;

        //enable shadowmask keyword
        CoreUtils.SetKeyword(cameraBuffer, shadowmaskKeyword,
                             shadowmaskExists && !useDistanceShadowmask); //if use shadowmask mode
        CoreUtils.SetKeyword(cameraBuffer, distanceShadowmaskKeyword,
                             shadowmaskExists && useDistanceShadowmask);  //if use distance shadowmask
        CoreUtils.SetKeyword(cameraBuffer, subtractiveLightingKeyword,
                             subtractiveLighting);
    }
Exemple #28
0
    void ConfigureLights()
    {
        for (int i = 0; i < cull.visibleLights.Count; ++i)
        {
            if (i == maxVisibleLights)
            {
                break;
            }

            VisibleLight light = cull.visibleLights[i];
            visibleLightColors[i] = light.finalColor;
            Vector4 attenuation = Vector4.zero;
            attenuation.w = 1f;
            Vector4 shadow = Vector4.zero;
            Vector4 v;
            if (light.lightType == LightType.Directional)
            {
                v        = light.localToWorld.GetColumn(2);
                v.x      = -v.x;
                v.y      = -v.y;
                v.z      = -v.z;
                shadow   = ConfigureShadows(i, light.light);
                shadow.z = 1f;
            }
            else
            {
                v             = light.localToWorld.GetColumn(3);
                attenuation.x = 1.0f / Mathf.Max(light.range * light.range, 1e-3f);

                if (light.lightType == LightType.Spot)
                {
                    Vector4 v1;
                    v1   = light.localToWorld.GetColumn(2);
                    v1.x = -v1.x;
                    v1.y = -v1.y;
                    v1.z = -v1.z;
                    visibleLightSpotDirections[i] = v1;
                    float outerRad = Mathf.Deg2Rad * 0.5f * light.spotAngle;
                    float outerCos = Mathf.Cos(outerRad);
                    float outerTan = Mathf.Tan(outerRad);

                    float innerCos   = Mathf.Cos(Mathf.Atan(23f / 32f) * outerTan);
                    float angleRange = Mathf.Max(innerCos - outerCos, 1e-3f);
                    attenuation.z = 1f / angleRange;
                    attenuation.w = -outerCos * attenuation.z;

                    //Light shadowLight = light.light;
                    //Bounds shadowBounds;
                    //if (shadowLight.shadows != LightShadows.None && cull.GetShadowCasterBounds(i, out shadowBounds))
                    //{
                    //    shadowTileCount += 1;
                    //    shadow.x = shadowLight.shadowStrength;
                    //    shadow.y = shadowLight.shadows == LightShadows.Soft ? 1.0f : 0f;
                    //}
                    shadow = ConfigureShadows(i, light.light);
                }
            }
            visibleLightAttenuations[i] = attenuation;
            shadowData[i] = shadow;
            visibleLightDirectionsOrPositions[i] = v;
        }

        if (cull.visibleLights.Count > maxVisibleLights)
        {
            int[] lightIndices = cull.GetLightIndexMap();
            for (int i = maxVisibleLights; i < cull.visibleLights.Count; ++i)
            {
                lightIndices[i] = -1;
            }
            cull.SetLightIndexMap(lightIndices);
        }

        //for(int i = maxVisibleLights-1; i >= cull.visibleLights.Count; --i)
        //{
        //    visibleLightColors[i] = Color.clear;
        //}
    }
Exemple #29
0
        public void SetupPerObjectLightIndices(ref CullingResults cullResults, ref LightData lightData)
        {
            if (lightData.additionalLightsCount == 0)
            {
                return;
            }

            var visibleLights          = lightData.visibleLights;
            var perObjectLightIndexMap = cullResults.GetLightIndexMap(Allocator.Temp);

            int directionalLightsCount = 0;
            int additionalLightsCount  = 0;

            // Disable all directional lights from the perobject light indices
            // Pipeline handles them globally.
            for (int i = 0; i < visibleLights.Length; ++i)
            {
                if (additionalLightsCount >= maxVisibleAdditionalLights)
                {
                    break;
                }

                VisibleLight light = visibleLights[i];
                if (light.lightType == LightType.Directional)
                {
                    perObjectLightIndexMap[i] = -1;
                    ++directionalLightsCount;
                }
                else
                {
                    perObjectLightIndexMap[i] -= directionalLightsCount;
                    ++additionalLightsCount;
                }
            }

            // Disable all remaining lights we cannot fit into the global light buffer.
            for (int i = directionalLightsCount + additionalLightsCount; i < visibleLights.Length; ++i)
            {
                perObjectLightIndexMap[i] = -1;
            }

            cullResults.SetLightIndexMap(perObjectLightIndexMap);
            perObjectLightIndexMap.Dispose();

            // if not using a compute buffer, engine will set indices in 2 vec4 constants
            // unity_4LightIndices0 and unity_4LightIndices1
            if (useStructuredBufferForLights)
            {
                int lightIndicesCount = cullResults.lightAndReflectionProbeIndexCount;
                if (lightIndicesCount > 0)
                {
                    if (perObjectLightIndices == null)
                    {
                        perObjectLightIndices = new ComputeBuffer(lightIndicesCount, sizeof(int));
                    }
                    else if (perObjectLightIndices.count < lightIndicesCount)
                    {
                        perObjectLightIndices.Release();
                        perObjectLightIndices = new ComputeBuffer(lightIndicesCount, sizeof(int));
                    }

                    cullResults.FillLightAndReflectionProbeIndices(perObjectLightIndices);
                }
            }
        }
        private void SetupLightDatas(ref UnityEngine.Rendering.Universal.LightData
                                     lightData)
        {
            //populate the buffer with le list of lights
            var     lights         = lightData.visibleLights;
            int     index          = 0;
            uint    destLightCount = 0;
            Vector4 lightAttenuation;

            for (int i = 0; i < lights.Length; ++i)
            {
                VisibleLight light = lights[i];
                Vector4      pos   = light.localToWorldMatrix.GetColumn(3);
                if (lightData.mainLightIndex != i)
                {
                    Vector3 col = new Vector3(
                        light.finalColor.r,
                        light.finalColor.g,
                        light.finalColor.b);

                    SetupLightAttenuation(ref light, out lightAttenuation);

                    //calculate light attenuation
                    float lightRangeSqr                 = light.range * light.range;
                    float fadeStartDistanceSqr          = 0.8f * 0.8f * lightRangeSqr;
                    float fadeRangeSqr                  = (fadeStartDistanceSqr - lightRangeSqr);
                    float oneOverFadeRangeSqr           = 1.0f / fadeRangeSqr;
                    float lightRangeSqrOverFadeRangeSqr = -lightRangeSqr / fadeRangeSqr;
                    float oneOverLightRangeSqr          = 1.0f / Mathf.Max(0.0001f,
                                                                           lightRangeSqr);
                    lightAttenuation.x = Application.isMobilePlatform ||
                                         SystemInfo.graphicsDeviceType ==
                                         GraphicsDeviceType.Switch ? oneOverFadeRangeSqr : oneOverLightRangeSqr;
                    lightAttenuation.y = lightRangeSqrOverFadeRangeSqr;

                    m_lightsDatas[index++] = new LightData(
                        pos, 1, col, light.range, lightAttenuation);
                    ++destLightCount;
                    if (index >= mc_maxLightCount)
                    {
                        break;
                    }
                }
                else
                {
                    Shader.SetGlobalVector("_MainLightColor", light.finalColor);
                    Shader.SetGlobalVector("_MainLightPosition", pos.normalized);
                }
            }

            if (destLightCount != ms_currentLightCount)
            {
                //清空index~ms_currentLightCount
                if (ms_currentLightCount > destLightCount)
                {
                    for (int i = index; i < ms_currentLightCount; ++i)
                    {
                        m_lightsDatas[i] = new LightData(
                            Vector3.zero, 0.0f, Vector3.zero, 0.0f, mc_defaultLightAttenuation);
                    }
                }
                ms_currentLightCount = destLightCount;
            }

            m_lightListBuffer.SetData(m_lightsDatas);
        }