void SetupMainLightShadowReceiverConstants(CommandBuffer cmd, ref ShadowData shadowData, VisibleLight shadowLight) { Light light = shadowLight.light; m_shadow_layer_surport = shadowData.surportsLayerDistance; Array.Copy(shadowData.shadowLayerDistance, m_shadow_layer_distance, m_shadow_layer_distance.Length); if (!m_shadow_layer_surport) { for (int i = 0, length = m_shadow_layer_distance.Length; i < length; ++i) { m_shadow_layer_distance[i] = 0; } } else { m_shadow_layer_distance = shadowData.shadowLayerDistance; for (int i = 0, length = m_shadow_layer_distance.Length; i < length; ++i) { if (m_shadow_layer_distance[i] < 0) { m_shadow_layer_distance[i] = shadowData.maxShadowDistance; } } } light.layerShadowCullDistances = m_shadow_layer_distance; int cascadeCount = m_ShadowCasterCascadesCount; for (int i = 0; i < cascadeCount; ++i) { m_MainLightShadowMatrices[i] = m_CascadeSlices[i].shadowTransform; } // We setup and additional a no-op WorldToShadow matrix in the last index // because the ComputeCascadeIndex function in Shadows.hlsl can return an index // out of bounds. (position not inside any cascade) and we want to avoid branching Matrix4x4 noOpShadowMatrix = Matrix4x4.zero; noOpShadowMatrix.m22 = (SystemInfo.usesReversedZBuffer) ? 1.0f : 0.0f; for (int i = cascadeCount; i <= k_MaxCascades; ++i) { m_MainLightShadowMatrices[i] = noOpShadowMatrix; } float invShadowAtlasWidth = 1.0f / m_ShadowmapWidth; float invShadowAtlasHeight = 1.0f / m_ShadowmapHeight; float invHalfShadowAtlasWidth = 0.5f * invShadowAtlasWidth; float invHalfShadowAtlasHeight = 0.5f * invShadowAtlasHeight; cmd.SetGlobalTexture(m_MainLightShadowmap.id, m_MainLightShadowmapTexture); cmd.SetGlobalMatrixArray(MainLightShadowConstantBuffer._WorldToShadow, m_MainLightShadowMatrices); cmd.SetGlobalVector(MainLightShadowConstantBuffer._ShadowData, new Vector4(light.shadowStrength, 0.0f, 0.0f, 0.0f)); cmd.SetGlobalVector(MainLightShadowConstantBuffer._CascadeShadowSplitSpheres0, m_CascadeSplitDistances[0]); cmd.SetGlobalVector(MainLightShadowConstantBuffer._CascadeShadowSplitSpheres1, m_CascadeSplitDistances[1]); cmd.SetGlobalVector(MainLightShadowConstantBuffer._CascadeShadowSplitSpheres2, m_CascadeSplitDistances[2]); cmd.SetGlobalVector(MainLightShadowConstantBuffer._CascadeShadowSplitSpheres3, m_CascadeSplitDistances[3]); cmd.SetGlobalVector(MainLightShadowConstantBuffer._CascadeShadowSplitSphereRadii, new Vector4(m_CascadeSplitDistances[0].w * m_CascadeSplitDistances[0].w, m_CascadeSplitDistances[1].w * m_CascadeSplitDistances[1].w, m_CascadeSplitDistances[2].w * m_CascadeSplitDistances[2].w, m_CascadeSplitDistances[3].w * m_CascadeSplitDistances[3].w)); cmd.SetGlobalVector(MainLightShadowConstantBuffer._ShadowOffset0, new Vector4(-invHalfShadowAtlasWidth, -invHalfShadowAtlasHeight, 0.0f, 0.0f)); cmd.SetGlobalVector(MainLightShadowConstantBuffer._ShadowOffset1, new Vector4(invHalfShadowAtlasWidth, -invHalfShadowAtlasHeight, 0.0f, 0.0f)); cmd.SetGlobalVector(MainLightShadowConstantBuffer._ShadowOffset2, new Vector4(-invHalfShadowAtlasWidth, invHalfShadowAtlasHeight, 0.0f, 0.0f)); cmd.SetGlobalVector(MainLightShadowConstantBuffer._ShadowOffset3, new Vector4(invHalfShadowAtlasWidth, invHalfShadowAtlasHeight, 0.0f, 0.0f)); cmd.SetGlobalVector(MainLightShadowConstantBuffer._ShadowmapSize, new Vector4(invShadowAtlasWidth, invShadowAtlasHeight, m_ShadowmapWidth, m_ShadowmapHeight)); }
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); cmd.SetGlobalDepthBias(shadowData.bias[shadowLightIndex].z, 0.0f);// shader do slope bias 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); cmd.SetGlobalDepthBias(0, 0); context.ExecuteCommandBuffer(cmd); CommandBufferPool.Release(cmd); }
public static bool ExtractDirectionalLightMatrix(ref CullingResults cullResults, ref ShadowData shadowData, int shadowLightIndex, int cascadeIndex, int shadowResolution, float shadowNearPlane, out Vector4 cascadeSplitDistance, out ShadowSliceData shadowSliceData, out Matrix4x4 viewMatrix, out Matrix4x4 projMatrix) { ShadowSplitData splitData; bool success = cullResults.ComputeDirectionalShadowMatricesAndCullingPrimitives(shadowLightIndex, cascadeIndex, shadowData.mainLightShadowCascadesCount, shadowData.mainLightShadowCascadesSplit, shadowResolution, shadowNearPlane, out viewMatrix, out projMatrix, out splitData); cascadeSplitDistance = splitData.cullingSphere; shadowSliceData.offsetX = (cascadeIndex % 2) * shadowResolution; shadowSliceData.offsetY = (cascadeIndex / 2) * shadowResolution; shadowSliceData.resolution = shadowResolution; shadowSliceData.viewMatrix = viewMatrix; shadowSliceData.projectionMatrix = projMatrix; shadowSliceData.shadowTransform = GetShadowTransform(projMatrix, viewMatrix); // If we have shadow cascades baked into the atlas we bake cascade transform // in each shadow matrix to save shader ALU and L/S if (shadowData.mainLightShadowCascadesCount > 1) { ApplySliceTransform(ref shadowSliceData, shadowData.mainLightShadowmapWidth, shadowData.mainLightShadowmapHeight); } return(success); }
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 LWRP // 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; } else { Debug.LogWarning("Only spot and directional shadow casters are supported in lightweight 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; if (shadowData.supportsSoftShadows) { // 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 (5x5) const float kernelRadius = 2.5f; depthBias *= kernelRadius; normalBias *= kernelRadius; } return(new Vector4(depthBias, normalBias, 0.0f, 0.0f)); }
static void InitializeShadowData(LightweightRenderPipelineAsset settings, NativeArray <VisibleLight> visibleLights, bool mainLightCastShadows, bool additionalLightsCastShadows, out ShadowData shadowData) { m_ShadowBiasData.Clear(); for (int i = 0; i < visibleLights.Length; ++i) { Light light = visibleLights[i].light; LWRPAdditionalLightData data = (light != null) ? light.gameObject.GetComponent <LWRPAdditionalLightData>() : null; if (data && !data.usePipelineSettings) { m_ShadowBiasData.Add(new Vector4(light.shadowBias, light.shadowNormalBias, 0.0f, 0.0f)); } else { m_ShadowBiasData.Add(new Vector4(settings.shadowDepthBias, settings.shadowNormalBias, 0.0f, 0.0f)); } } shadowData.bias = m_ShadowBiasData; // Until we can have keyword stripping forcing single cascade hard shadows on gles2 bool supportsScreenSpaceShadows = SystemInfo.graphicsDeviceType != GraphicsDeviceType.OpenGLES2; shadowData.supportsMainLightShadows = settings.supportsMainLightShadows && mainLightCastShadows; // we resolve shadows in screenspace when cascades are enabled to save ALU as computing cascade index + shadowCoord on fragment is expensive shadowData.requiresScreenSpaceShadowResolve = shadowData.supportsMainLightShadows && supportsScreenSpaceShadows && settings.shadowCascadeOption != ShadowCascadesOption.NoCascades; int shadowCascadesCount; switch (settings.shadowCascadeOption) { case ShadowCascadesOption.FourCascades: shadowCascadesCount = 4; break; case ShadowCascadesOption.TwoCascades: shadowCascadesCount = 2; break; default: shadowCascadesCount = 1; break; } shadowData.mainLightShadowCascadesCount = (shadowData.requiresScreenSpaceShadowResolve) ? shadowCascadesCount : 1; shadowData.mainLightShadowmapWidth = settings.mainLightShadowmapResolution; shadowData.mainLightShadowmapHeight = settings.mainLightShadowmapResolution; switch (shadowData.mainLightShadowCascadesCount) { case 1: shadowData.mainLightShadowCascadesSplit = new Vector3(1.0f, 0.0f, 0.0f); break; case 2: shadowData.mainLightShadowCascadesSplit = new Vector3(settings.cascade2Split, 1.0f, 0.0f); break; default: shadowData.mainLightShadowCascadesSplit = settings.cascade4Split; break; } shadowData.supportsAdditionalLightShadows = settings.supportsAdditionalLightShadows && additionalLightsCastShadows; shadowData.additionalLightsShadowmapWidth = shadowData.additionalLightsShadowmapHeight = settings.additionalLightsShadowmapResolution; shadowData.supportsSoftShadows = settings.supportsSoftShadows && (shadowData.supportsMainLightShadows || shadowData.supportsAdditionalLightShadows); shadowData.shadowmapDepthBufferBits = 16; }
void RenderAdditionalShadowmapAtlas(ref ScriptableRenderContext context, ref CullingResults cullResults, ref LightData lightData, ref ShadowData shadowData) { NativeArray <VisibleLight> visibleLights = lightData.visibleLights; bool additionalLightHasSoftShadows = false; CommandBuffer cmd = CommandBufferPool.Get(m_ProfilerTag); using (new ProfilingSample(cmd, m_ProfilerTag)) { for (int i = 0; i < m_AdditionalShadowCastingLightIndices.Count; ++i) { int shadowLightIndex = m_AdditionalShadowCastingLightIndices[i]; VisibleLight shadowLight = visibleLights[shadowLightIndex]; if (m_AdditionalShadowCastingLightIndices.Count > 1) { ShadowUtils.ApplySliceTransform(ref m_AdditionalLightSlices[i], m_ShadowmapWidth, m_ShadowmapHeight); } var settings = new ShadowDrawingSettings(cullResults, shadowLightIndex); Vector4 shadowBias = ShadowUtils.GetShadowBias(ref shadowLight, shadowLightIndex, ref shadowData, m_AdditionalLightSlices[i].projectionMatrix, m_AdditionalLightSlices[i].resolution); ShadowUtils.SetupShadowCasterConstantBuffer(cmd, ref shadowLight, shadowBias); ShadowUtils.RenderShadowSlice(cmd, ref context, ref m_AdditionalLightSlices[i], ref settings, m_AdditionalLightSlices[i].projectionMatrix, m_AdditionalLightSlices[i].viewMatrix); additionalLightHasSoftShadows |= shadowLight.light.shadows == LightShadows.Soft; } SetupAdditionalLightsShadowReceiverConstants(cmd, ref shadowData); } // 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, true); CoreUtils.SetKeyword(cmd, ShaderKeywordStrings.SoftShadows, softShadows); context.ExecuteCommandBuffer(cmd); CommandBufferPool.Release(cmd); }