public void Render(RenderLoop loop, CullResults cullResults, out ShadowOutput packedShadows) { if (!m_Settings.enabled) { ClearPackedShadows(cullResults.visibleLights, out packedShadows); } // Pack all shadow quads into the texture if (!AutoPackLightsIntoShadowTexture(GetInputShadowLightData(cullResults), cullResults.visibleLights, out packedShadows)) { // No shadowing lights found, so skip all rendering return; } RenderPackedShadows(loop, cullResults, ref packedShadows); }
//--------------------------------------------------------------------------------------------------------------------------------------------------- // Render shadows //--------------------------------------------------------------------------------------------------------------------------------------------------- void RenderPackedShadows(RenderLoop loop, CullResults cullResults, ref ShadowOutput packedShadows) { var setRenderTargetCommandBuffer = new CommandBuffer(); setRenderTargetCommandBuffer.name = "Render packed shadows"; setRenderTargetCommandBuffer.GetTemporaryRT(m_ShadowTexName, m_Settings.shadowAtlasWidth, m_Settings.shadowAtlasHeight, k_DepthBuffer, FilterMode.Bilinear, RenderTextureFormat.Shadowmap, RenderTextureReadWrite.Linear); setRenderTargetCommandBuffer.SetRenderTarget(new RenderTargetIdentifier(m_ShadowTexName)); setRenderTargetCommandBuffer.ClearRenderTarget(true, true, Color.green); loop.ExecuteCommandBuffer(setRenderTargetCommandBuffer); setRenderTargetCommandBuffer.Dispose(); VisibleLight[] visibleLights = cullResults.visibleLights; var shadowSlices = packedShadows.shadowSlices; // Render each light's shadow buffer into a subrect of the shared depth texture for (int lightIndex = 0; lightIndex < packedShadows.shadowLights.Length; lightIndex++) { int shadowSliceCount = packedShadows.shadowLights[lightIndex].shadowSliceCount; if (shadowSliceCount == 0) { continue; } Profiler.BeginSample("Shadows.GetShadowCasterBounds"); Bounds bounds; if (!cullResults.GetShadowCasterBounds(lightIndex, out bounds)) { Profiler.EndSample(); return; } Profiler.EndSample(); Profiler.BeginSample("Shadows.DrawShadows"); Matrix4x4 proj; Matrix4x4 view; var lightType = visibleLights[lightIndex].lightType; var lightDirection = visibleLights[lightIndex].light.transform.forward; var shadowNearClip = visibleLights[lightIndex].light.shadowNearPlane; int shadowSliceIndex = packedShadows.GetShadowSliceIndex(lightIndex, 0); if (lightType == LightType.Spot) { var settings = new DrawShadowsSettings(cullResults, lightIndex); bool needRendering = cullResults.ComputeSpotShadowsMatricesAndCullingPrimitives(lightIndex, out view, out proj, out settings.splitData); SetupShadowSplitMatrices(ref packedShadows.shadowSlices[shadowSliceIndex], proj, view); if (needRendering) { RenderShadowSplit(ref shadowSlices[shadowSliceIndex], lightDirection, proj, view, ref loop, settings); } } else if (lightType == LightType.Directional) { Vector3 splitRatio = m_Settings.directionalLightCascades; for (int s = 0; s < 4; ++s) { packedShadows.directionalShadowSplitSphereSqr[s] = new Vector4(0, 0, 0, float.NegativeInfinity); } for (int s = 0; s < shadowSliceCount; ++s, shadowSliceIndex++) { var settings = new DrawShadowsSettings(cullResults, lightIndex); var shadowResolution = shadowSlices[shadowSliceIndex].shadowResolution; bool needRendering = cullResults.ComputeDirectionalShadowMatricesAndCullingPrimitives(lightIndex, s, shadowSliceCount, splitRatio, shadowResolution, shadowNearClip, out view, out proj, out settings.splitData); packedShadows.directionalShadowSplitSphereSqr[s] = settings.splitData.cullingSphere; packedShadows.directionalShadowSplitSphereSqr[s].w *= packedShadows.directionalShadowSplitSphereSqr[s].w; SetupShadowSplitMatrices(ref shadowSlices[shadowSliceIndex], proj, view); if (needRendering) { RenderShadowSplit(ref shadowSlices[shadowSliceIndex], lightDirection, proj, view, ref loop, settings); } } } else if (lightType == LightType.Point) { for (int s = 0; s < shadowSliceCount; ++s, shadowSliceIndex++) { var settings = new DrawShadowsSettings(cullResults, lightIndex); bool needRendering = cullResults.ComputePointShadowsMatricesAndCullingPrimitives(lightIndex, (CubemapFace)s, 2.0f, out view, out proj, out settings.splitData); SetupShadowSplitMatrices(ref shadowSlices[shadowSliceIndex], proj, view); if (needRendering) { RenderShadowSplit(ref shadowSlices[shadowSliceIndex], lightDirection, proj, view, ref loop, settings); } } } Profiler.EndSample(); } }
public static void ClearPackedShadows(VisibleLight[] lights, out ShadowOutput packedShadows) { packedShadows.directionalShadowSplitSphereSqr = null; packedShadows.shadowSlices = null; packedShadows.shadowLights = new ShadowLight[lights.Length]; }
//--------------------------------------------------------------------------------------------------------------------------------------------------- bool AutoPackLightsIntoShadowTexture(List <InputShadowLightData> shadowLights, VisibleLight[] lights, out ShadowOutput packedShadows) { var activeShadowLights = new Dictionary <int, InputShadowLightData>(); var shadowIndices = new List <int>(); //@TODO: Disallow multiple directional lights for (int i = 0; i < shadowLights.Count; i++) { shadowIndices.Add(shadowLights[i].lightIndex); activeShadowLights[shadowLights[i].lightIndex] = shadowLights[i]; } // World's stupidest sheet packer: // 1. Sort all lights from largest to smallest // 2. In a left->right, top->bottom pattern, fill quads until you reach the edge of the texture // 3. Move position to x=0, y=bottomOfFirstTextureInThisRow // 4. Goto 2. // Yes, this will produce holes as the quads shrink, but it's good enough for now. I'll work on this more later to fill the gaps. // Sort all lights from largest to smallest shadowIndices.Sort( delegate(int l1, int l2) { var nCompare = 0; // Sort shadow-casting lights by shadow resolution nCompare = activeShadowLights[l1].shadowResolution.CompareTo(activeShadowLights[l2].shadowResolution); // Sort by shadow size if (nCompare == 0) // Same, so sort by range to stabilize sort results { nCompare = lights[l1].range.CompareTo(lights[l2].range); // Sort by shadow size } if (nCompare == 0) // Still same, so sort by instance ID to stabilize sort results { nCompare = lights[l1].light.GetInstanceID().CompareTo(lights[l2].light.GetInstanceID()); } return(nCompare); } ); // Start filling lights into texture var requestedPages = new List <AtlasEntry>(); packedShadows.shadowLights = new ShadowLight[lights.Length]; for (int i = 0; i != shadowIndices.Count; i++) { var numShadowSplits = CalculateNumShadowSplits(shadowIndices[i], lights); packedShadows.shadowLights[shadowIndices[i]].shadowSliceCount = numShadowSplits; packedShadows.shadowLights[shadowIndices[i]].shadowSliceIndex = requestedPages.Count; for (int s = 0; s < numShadowSplits; s++) { requestedPages.Add(new AtlasEntry(requestedPages.Count, shadowIndices[i])); } } var nCurrentX = 0; var nCurrentY = -1; var nNextY = 0; packedShadows.shadowSlices = new ShadowSliceData[requestedPages.Count]; packedShadows.directionalShadowSplitSphereSqr = new Vector4[4]; foreach (var entry in requestedPages) { var shadowResolution = activeShadowLights[entry.lightIndex].shadowResolution; // Check if first texture is too wide if (nCurrentY == -1) { if ((shadowResolution > m_Settings.shadowAtlasWidth) || (shadowResolution > m_Settings.shadowAtlasHeight)) { Debug.LogError("ERROR! Shadow packer ran out of space in the " + m_Settings.shadowAtlasWidth + "x" + m_Settings.shadowAtlasHeight + " texture!\n\n"); m_FailedToPackLastTime = true; ClearPackedShadows(lights, out packedShadows); return(false); } } // Goto next scanline if ((nCurrentY == -1) || ((nCurrentX + shadowResolution) > m_Settings.shadowAtlasWidth)) { nCurrentX = 0; nCurrentY = nNextY; nNextY += shadowResolution; } // Check if we've run out of space if ((nCurrentY + shadowResolution) > m_Settings.shadowAtlasHeight) { Debug.LogError("ERROR! Shadow packer ran out of space in the " + m_Settings.shadowAtlasWidth + "x" + m_Settings.shadowAtlasHeight + " texture!\n\n"); m_FailedToPackLastTime = true; ClearPackedShadows(lights, out packedShadows); return(false); } // Save location to light packedShadows.shadowSlices[entry.splitIndex].atlasX = nCurrentX; packedShadows.shadowSlices[entry.splitIndex].atlasY = nCurrentY; packedShadows.shadowSlices[entry.splitIndex].shadowResolution = shadowResolution; // Move ahead nCurrentX += shadowResolution; //Debug.Log( "Sheet packer: " + vl.m_cachedLight.name + " ( " + vl.m_shadowX + ", " + vl.m_shadowY + " ) " + vl.m_shadowResolution + "\n\n" ); } if (m_FailedToPackLastTime) { m_FailedToPackLastTime = false; Debug.Log("SUCCESS! Shadow packer can now fit all lights into the " + m_Settings.shadowAtlasWidth + "x" + m_Settings.shadowAtlasHeight + " texture!\n\n"); } return(requestedPages.Count != 0); }
//--------------------------------------------------------------------------------------------------------------------------------------------------- void UpdateLightConstants(VisibleLight[] visibleLights, ref ShadowOutput shadow) { var numLightsIncludingTooMany = 0; var numLights = 0; var lightColor = new Vector4[k_MaxLights]; var lightPosition_invRadius = new Vector4[k_MaxLights]; var lightDirection = new Vector4[k_MaxLights]; var lightShadowIndex_lightParams = new Vector4[k_MaxLights]; var lightFalloffParams = new Vector4[k_MaxLights]; var spotLightInnerOuterConeCosines = new Vector4[k_MaxLights]; var matWorldToShadow = new Matrix4x4[k_MaxLights * k_MaxShadowmapPerLights]; var dirShadowSplitSpheres = new Vector4[k_MaxDirectionalSplit]; for (int nLight = 0; nLight < visibleLights.Length; nLight++) { numLightsIncludingTooMany++; if (numLightsIncludingTooMany > k_MaxLights) { continue; } var light = visibleLights[nLight]; var lightType = light.lightType; var position = light.light.transform.position; var lightDir = light.light.transform.forward.normalized; var additionalLightData = light.light.GetComponent <AdditionalLightData>(); // Setup shadow data arrays var hasShadows = shadow.GetShadowSliceCountLightIndex(nLight) != 0; if (lightType == LightType.Directional) { lightColor[numLights] = light.finalColor; lightPosition_invRadius[numLights] = new Vector4( position.x - (lightDir.x * k_DirectionalLightPullbackDistance), position.y - (lightDir.y * k_DirectionalLightPullbackDistance), position.z - (lightDir.z * k_DirectionalLightPullbackDistance), -1.0f); lightDirection[numLights] = new Vector4(lightDir.x, lightDir.y, lightDir.z); lightShadowIndex_lightParams[numLights] = new Vector4(0, 0, 1, 1); lightFalloffParams[numLights] = new Vector4(0.0f, 0.0f, float.MaxValue, (float)lightType); spotLightInnerOuterConeCosines[numLights] = new Vector4(0.0f, -1.0f, 1.0f); if (hasShadows) { for (int s = 0; s < k_MaxDirectionalSplit; ++s) { dirShadowSplitSpheres[s] = shadow.directionalShadowSplitSphereSqr[s]; } } } else if (lightType == LightType.Point) { lightColor[numLights] = light.finalColor; lightPosition_invRadius[numLights] = new Vector4(position.x, position.y, position.z, 1.0f / light.range); lightDirection[numLights] = new Vector4(0.0f, 0.0f, 0.0f); lightShadowIndex_lightParams[numLights] = new Vector4(0, 0, 1, 1); lightFalloffParams[numLights] = new Vector4(1.0f, 0.0f, light.range * light.range, (float)lightType); spotLightInnerOuterConeCosines[numLights] = new Vector4(0.0f, -1.0f, 1.0f); } else if (lightType == LightType.Spot) { lightColor[numLights] = light.finalColor; lightPosition_invRadius[numLights] = new Vector4(position.x, position.y, position.z, 1.0f / light.range); lightDirection[numLights] = new Vector4(lightDir.x, lightDir.y, lightDir.z); lightShadowIndex_lightParams[numLights] = new Vector4(0, 0, 1, 1); lightFalloffParams[numLights] = new Vector4(1.0f, 0.0f, light.range * light.range, (float)lightType); var flInnerConePercent = AdditionalLightData.GetInnerSpotPercent01(additionalLightData); var spotAngle = light.light.spotAngle; var flPhiDot = Mathf.Clamp(Mathf.Cos(spotAngle * 0.5f * Mathf.Deg2Rad), 0.0f, 1.0f); // outer cone var flThetaDot = Mathf.Clamp(Mathf.Cos(spotAngle * 0.5f * flInnerConePercent * Mathf.Deg2Rad), 0.0f, 1.0f); // inner cone spotLightInnerOuterConeCosines[numLights] = new Vector4(flThetaDot, flPhiDot, 1.0f / Mathf.Max(0.01f, flThetaDot - flPhiDot)); } if (hasShadows) { // Enable shadows lightShadowIndex_lightParams[numLights].x = 1; for (int s = 0; s < shadow.GetShadowSliceCountLightIndex(nLight); ++s) { var shadowSliceIndex = shadow.GetShadowSliceIndex(nLight, s); matWorldToShadow[numLights * k_MaxShadowmapPerLights + s] = shadow.shadowSlices[shadowSliceIndex].shadowTransform.transpose; } } numLights++; } // Warn if too many lights found if (numLightsIncludingTooMany > k_MaxLights) { if (numLightsIncludingTooMany > m_WarnedTooManyLights) { Debug.LogError("ERROR! Found " + numLightsIncludingTooMany + " runtime lights! Valve renderer supports up to " + k_MaxLights + " active runtime lights at a time!\nDisabling " + (numLightsIncludingTooMany - k_MaxLights) + " runtime light" + ((numLightsIncludingTooMany - k_MaxLights) > 1 ? "s" : "") + "!\n"); } m_WarnedTooManyLights = numLightsIncludingTooMany; } else { if (m_WarnedTooManyLights > 0) { m_WarnedTooManyLights = 0; Debug.Log("SUCCESS! Found " + numLightsIncludingTooMany + " runtime lights which is within the supported number of lights, " + k_MaxLights + ".\n\n"); } } // Send constants to shaders Shader.SetGlobalInt("g_nNumLights", numLights); // New method for Unity 5.4 to set arrays of constants Shader.SetGlobalVectorArray("g_vLightPosition_flInvRadius", lightPosition_invRadius); Shader.SetGlobalVectorArray("g_vLightColor", lightColor); Shader.SetGlobalVectorArray("g_vLightDirection", lightDirection); Shader.SetGlobalVectorArray("g_vLightShadowIndex_vLightParams", lightShadowIndex_lightParams); Shader.SetGlobalVectorArray("g_vLightFalloffParams", lightFalloffParams); Shader.SetGlobalVectorArray("g_vSpotLightInnerOuterConeCosines", spotLightInnerOuterConeCosines); Shader.SetGlobalMatrixArray("g_matWorldToShadow", matWorldToShadow); Shader.SetGlobalVectorArray("g_vDirShadowSplitSpheres", dirShadowSplitSpheres); // Time #if (UNITY_EDITOR) { Shader.SetGlobalFloat("g_flTime", Time.realtimeSinceStartup); //Debug.Log( "Time " + Time.realtimeSinceStartup ); } #else { Shader.SetGlobalFloat("g_flTime", Time.timeSinceLevelLoad); //Debug.Log( "Time " + Time.timeSinceLevelLoad ); } #endif // PCF 3x3 Shadows var texelEpsilonX = 1.0f / m_ShadowSettings.shadowAtlasWidth; var texelEpsilonY = 1.0f / m_ShadowSettings.shadowAtlasHeight; var shadow3x3PCFTerms0 = new Vector4(20.0f / 267.0f, 33.0f / 267.0f, 55.0f / 267.0f, 0.0f); var shadow3x3PCFTerms1 = new Vector4(texelEpsilonX, texelEpsilonY, -texelEpsilonX, -texelEpsilonY); var shadow3x3PCFTerms2 = new Vector4(texelEpsilonX, texelEpsilonY, 0.0f, 0.0f); var shadow3x3PCFTerms3 = new Vector4(-texelEpsilonX, -texelEpsilonY, 0.0f, 0.0f); Shader.SetGlobalVector("g_vShadow3x3PCFTerms0", shadow3x3PCFTerms0); Shader.SetGlobalVector("g_vShadow3x3PCFTerms1", shadow3x3PCFTerms1); Shader.SetGlobalVector("g_vShadow3x3PCFTerms2", shadow3x3PCFTerms2); Shader.SetGlobalVector("g_vShadow3x3PCFTerms3", shadow3x3PCFTerms3); }