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