public bool Setup(ref RenderingData renderingData)
        {
            if (!renderingData.shadowData.supportsMainLightShadows)
            {
                return(false);
            }

            Clear();
            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);
            }

            m_ShadowCasterCascadesCount = renderingData.shadowData.mainLightShadowCascadesCount;

            int shadowResolution = ShadowUtils.GetMaxTileResolutionInAtlas(renderingData.shadowData.mainLightShadowmapWidth,
                                                                           renderingData.shadowData.mainLightShadowmapHeight, m_ShadowCasterCascadesCount);

            m_ShadowmapWidth  = renderingData.shadowData.mainLightShadowmapWidth;
            m_ShadowmapHeight = (m_ShadowCasterCascadesCount == 2) ?
                                renderingData.shadowData.mainLightShadowmapHeight >> 1 :
                                renderingData.shadowData.mainLightShadowmapHeight;

            for (int cascadeIndex = 0; cascadeIndex < m_ShadowCasterCascadesCount; ++cascadeIndex)
            {
                bool success = ShadowUtils.ExtractDirectionalLightMatrix(ref renderingData.cullResults, ref renderingData.shadowData,
                                                                         shadowLightIndex, cascadeIndex, m_ShadowmapWidth, m_ShadowmapHeight, shadowResolution, light.shadowNearPlane,
                                                                         out m_CascadeSplitDistances[cascadeIndex], out m_CascadeSlices[cascadeIndex], out m_CascadeSlices[cascadeIndex].viewMatrix, out m_CascadeSlices[cascadeIndex].projectionMatrix);

                if (!success)
                {
                    return(false);
                }
            }

            return(true);
        }
Exemple #2
0
        public bool Setup(ref RenderingData renderingData)
        {
            if (!renderingData.shadowData.supportsAdditionalLightShadows)
            {
                return(false);
            }

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

            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 all directional lights as they are not baked into the additional
                // shadowmap atlas.
                if (shadowLight.lightType == LightType.Directional)
                {
                    continue;
                }

                int  shadowCastingLightIndex = m_AdditionalShadowCastingLightIndices.Count;
                bool isValidShadowSlice      = false;
                if (IsValidShadowCastingLight(ref renderingData.lightData, i))
                {
                    if (renderingData.cullResults.GetShadowCasterBounds(i, out var bounds))
                    {
                        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;
                }
            }

            // 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);
        }
        public bool Setup(ref RenderingData renderingData)
        {
            if (!renderingData.shadowData.supportsAdditionalLightShadows)
            {
                return(false);
            }

            Clear();

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

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

            for (int i = 0; i < visibleLights.Length && m_AdditionalShadowCastingLightIndices.Count < additionalLightsCount; ++i)
            {
                if (i == renderingData.lightData.mainLightIndex)
                {
                    continue;
                }

                VisibleLight shadowLight = visibleLights[i];
                Light        light       = shadowLight.light;

                if (shadowLight.lightType == LightType.Spot && light != null && light.shadows != LightShadows.None)
                {
                    if (renderingData.cullResults.GetShadowCasterBounds(i, out bounds))
                    {
                        m_AdditionalShadowCastingLightIndices.Add(i);
                    }
                }
            }

            int shadowCastingLightsCount = m_AdditionalShadowCastingLightIndices.Count;

            if (shadowCastingLightsCount == 0)
            {
                return(false);
            }

            // TODO: Add support to point light shadows. We make a simplification here that only works
            // for spot lights and with max spot shadows per pass.
            int atlasWidth      = renderingData.shadowData.additionalLightsShadowmapWidth;
            int atlasHeight     = renderingData.shadowData.additionalLightsShadowmapHeight;
            int sliceResolution = ShadowUtils.GetMaxTileResolutionInAtlas(atlasWidth, atlasHeight, shadowCastingLightsCount);

            bool anyShadows         = false;
            int  shadowSlicesPerRow = (atlasWidth / sliceResolution);

            for (int i = 0; i < shadowCastingLightsCount; ++i)
            {
                int          shadowLightIndex = m_AdditionalShadowCastingLightIndices[i];
                VisibleLight shadowLight      = visibleLights[shadowLightIndex];

                // Currently Only Spot Lights are supported in additional lights
                Debug.Assert(shadowLight.lightType == LightType.Spot);
                Matrix4x4 shadowTransform;
                bool      success = ShadowUtils.ExtractSpotLightMatrix(ref renderingData.cullResults, ref renderingData.shadowData,
                                                                       shadowLightIndex, out shadowTransform, out m_AdditionalLightSlices[i].viewMatrix, out m_AdditionalLightSlices[i].projectionMatrix);

                if (success)
                {
                    // TODO: We need to pass bias and scale list to shader to be able to support multiple
                    // shadow casting additional lights.
                    m_AdditionalLightSlices[i].offsetX         = (i % shadowSlicesPerRow) * sliceResolution;
                    m_AdditionalLightSlices[i].offsetY         = (i / shadowSlicesPerRow) * sliceResolution;
                    m_AdditionalLightSlices[i].resolution      = sliceResolution;
                    m_AdditionalLightSlices[i].shadowTransform = shadowTransform;

                    m_AdditionalLightsShadowStrength[i] = shadowLight.light.shadowStrength;
                    anyShadows = true;
                }
                else
                {
                    m_AdditionalShadowCastingLightIndices.RemoveAt(i--);
                }
            }

            return(anyShadows);
        }