public ShaderGroupDataKey(Texture texture, ILightShadowMapRenderer renderer, int cascadeCount, int lightMaxCount) { Texture = texture; Renderer = renderer; CascadeCount = cascadeCount; LightMaxCount = lightMaxCount; }
public void Initialize(RenderView renderView, LightComponent lightComponent, IDirectLight light, LightShadowMap shadowMap, int size, ILightShadowMapRenderer renderer) { if (renderView == null) { throw new ArgumentNullException(nameof(renderView)); } if (lightComponent == null) { throw new ArgumentNullException(nameof(lightComponent)); } if (light == null) { throw new ArgumentNullException(nameof(light)); } if (shadowMap == null) { throw new ArgumentNullException(nameof(shadowMap)); } if (renderer == null) { throw new ArgumentNullException(nameof(renderer)); } RenderView = renderView; LightComponent = lightComponent; Light = light; Shadow = shadowMap; Size = size; FilterType = Shadow.Filter == null || !Shadow.Filter.RequiresCustomBuffer() ? null : Shadow.Filter.GetType(); Renderer = renderer; Atlas = null; // Reset the atlas, It will be setup after CascadeCount = 1; ShadowType = renderer.GetShadowType(Shadow); }
public void Initialize(LightComponent lightComponent, IDirectLight light, LightShadowMap shadowMap, int size, ILightShadowMapRenderer renderer) { if (lightComponent == null) { throw new ArgumentNullException("lightComponent"); } if (light == null) { throw new ArgumentNullException("light"); } if (shadowMap == null) { throw new ArgumentNullException("shadowMap"); } if (renderer == null) { throw new ArgumentNullException("renderer"); } LightComponent = lightComponent; Light = light; Shadow = shadowMap; Size = size; FilterType = Shadow.Filter == null || !Shadow.Filter.RequiresCustomBuffer() ? null : Shadow.Filter.GetType(); Renderer = renderer; Atlas = null; // Reset the atlas, It will be setup after ShadowType = renderer.GetShadowType(Shadow); }
public LightGroupKey(ILightShadowMapRenderer shadowRenderer, LightShadowType shadowType, ITextureProjectionRenderer textureProjectionRenderer) { ShadowRenderer = shadowRenderer; ShadowType = shadowType; TextureProjectionRenderer = textureProjectionRenderer; }
private ILightShadowMapShaderGroupData CreateShadowMapShaderGroupData(ILightShadowMapRenderer shadowRenderer, LightShadowType shadowType) { ILightShadowMapShaderGroupData shadowGroupData = null; if (shadowRenderer != null) { shadowGroupData = shadowRenderer.CreateShaderGroupData(shadowType); } return(shadowGroupData); }
public LightGroupKey(ILightShadowMapRenderer shadowRenderer, LightShadowType shadowType) { ShadowRenderer = shadowRenderer; ShadowType = shadowType; }
public override void ProcessLights(ProcessLightsParameters parameters) { if (parameters.LightCollection.Count == 0) { return; } // Check if we have a fallback renderer next in the chain, in case we don't need shadows bool hasNextRenderer = parameters.RendererIndex < (parameters.Renderers.Length - 1); ILightShadowMapRenderer currentShadowRenderer = null; LightShadowType currentShadowType = 0; // Start by filtering/sorting what can be processed shadowComparer.ShadowMapTexturesPerLight = parameters.ShadowMapTexturesPerLight; shadowComparer.Lights = parameters.LightCollection; parameters.LightIndices.Sort(0, parameters.LightIndices.Count, shadowComparer); // Loop over the number of lights + 1 where the last iteration will always flush the last batch of lights for (int j = 0; j < parameters.LightIndices.Count + 1;) { // TODO: Eventually move this loop to a separate function that returns a structure. // These variables will contain the relevant parameters of the next usable light: LightShadowType nextShadowType = 0; ILightShadowMapRenderer nextShadowRenderer = null; LightShadowMapTexture nextShadowTexture = null; RenderLight nextLight = null; // Find the next light whose attributes aren't null: if (j < parameters.LightIndices.Count) { nextLight = parameters.LightCollection[parameters.LightIndices[j]]; if (parameters.ShadowMapRenderer != null && parameters.ShadowMapTexturesPerLight.TryGetValue(nextLight, out nextShadowTexture) && nextShadowTexture.Atlas != null) // atlas could not be allocated? treat it as a non-shadowed texture { nextShadowType = nextShadowTexture.ShadowType; nextShadowRenderer = nextShadowTexture.Renderer; } } // Flush current group // If we detect that the previous light's attributes don't match the next one's, create a new group (or add to an existing one that has the same attributes): if (j == parameters.LightIndices.Count || currentShadowType != nextShadowType || currentShadowRenderer != nextShadowRenderer) // TODO: Refactor this into a little structure instead. { if (processedLights.Count > 0) { var lightGroupKey = new LightGroupKey(currentShadowRenderer, currentShadowType); LightShaderGroupDynamic lightShaderGroup = FindOrCreateLightShaderGroup(lightGroupKey, parameters); // Add view and lights to the current group: var allowedLightCount = lightShaderGroup.AddView(parameters.ViewIndex, parameters.View, processedLights.Count); for (int i = 0; i < allowedLightCount; ++i) { LightDynamicEntry light = processedLights[i]; lightShaderGroup.AddLight(light.Light, light.ShadowMapTexture); } // TODO: assign extra lights to non-shadow rendering if possible //for (int i = lightCount; i < processedLights.Count; ++i) // XXX.AddLight(processedLights[i], null); // Add the current light shader group to the collection if it hasn't already been added: var lightShaderGroupEntry = new LightShaderGroupEntry <LightGroupKey>(lightGroupKey, lightShaderGroup); if (!lightShaderGroups.Contains(lightShaderGroupEntry)) { lightShaderGroups.Add(lightShaderGroupEntry); } processedLights.Clear(); } // Start next group currentShadowType = nextShadowType; currentShadowRenderer = nextShadowRenderer; } if (j < parameters.LightIndices.Count) { // Do we need to process non shadowing lights or defer it to something else? if (nextShadowTexture == null && hasNextRenderer) { // Break out so the remaining lights can be handled by the next renderer break; } parameters.LightIndices.RemoveAt(j); processedLights.Add(new LightDynamicEntry(nextLight, nextShadowTexture)); } else { j++; } } processedLights.Clear(); }
private ILightShadowMapShaderGroupData CreateShadowMapShaderGroupData(ILightShadowMapRenderer shadowRenderer, LightShadowType shadowType) { ILightShadowMapShaderGroupData shadowGroupData = shadowRenderer?.CreateShaderGroupData(shadowType); return(shadowGroupData); }
public LightForwardShaderFullEntryKey(LightForwardShaderEntryKey key, LightGroupRendererBase lightGroupRenderer, ILightShadowMapRenderer shadowRenderer) { Key = key; LightGroupRenderer = lightGroupRenderer; ShadowRenderer = shadowRenderer; }
private LightParametersPermutationEntry PreparePermutationEntryForRendering(RenderViewLightData renderViewData, bool isShadowReceiver, ref BoundingBox boundingBox, EntityGroup group, int effectSlot) { var shaderKeyIdBuilder = new ObjectIdSimpleBuilder(); var parametersKeyIdBuilder = new ObjectIdSimpleBuilder(); directLightsPerMesh.Clear(); directLightShaderGroupEntryKeys.Clear(); environmentLightsPerMesh.Clear(); environmentLightShaderGroupEntryKeys.Clear(); // Create different parameter collections depending on shadows // TODO GRAPHICS REFACTOR can we use the same parameter collection for shadowed/non-shadowed? parametersKeyIdBuilder.Write(isShadowReceiver ? 1U : 0U); shaderKeyIdBuilder.Write(isShadowReceiver ? 1U : 0U); shaderKeyIdBuilder.Write((uint)effectSlot); // This loop is looking for visible lights per render model and calculate a ShaderId and ParametersId // TODO: Part of this loop could be processed outisde of the PrepareRenderMeshForRendering // For example: Environment lights or directional lights are always active, so we could pregenerate part of the // id and groups outside this loop. Also considering that each light renderer has a maximum of lights // we could pre foreach (var activeRenderer in renderViewData.ActiveRenderers) { var lightRenderer = activeRenderer.LightRenderer; var lightCollection = activeRenderer.LightGroup.FindLightCollectionByGroup(group); var lightCount = lightCollection == null ? 0 : lightCollection.Count; int lightMaxCount = Math.Min(lightCount, lightRenderer.LightMaxCount); var lightRendererId = lightRenderer.LightRendererId; var allocCountForNewLightType = lightRenderer.AllocateLightMaxCount ? (byte)lightRenderer.LightMaxCount : (byte)1; var currentShaderKey = new LightForwardShaderEntryKey(); // Path for environment lights if (lightRenderer.IsEnvironmentLight) { // The loop is simpler for environment lights (single group per light, no shadow maps, no bounding box...etc) for (int i = 0; i < lightMaxCount; i++) { var light = lightCollection[i]; currentShaderKey = new LightForwardShaderEntryKey(lightRendererId, 0, allocCountForNewLightType); unsafe { shaderKeyIdBuilder.Write(*(uint *)¤tShaderKey); } parametersKeyIdBuilder.Write(light.Id); environmentLightsPerMesh.Add(new LightEntry(environmentLightShaderGroupEntryKeys.Count, light, null)); environmentLightShaderGroupEntryKeys.Add(new LightForwardShaderFullEntryKey(currentShaderKey, lightRenderer, null)); } } else { ILightShadowMapRenderer currentShadowRenderer = null; bool hasDirectLight = false; for (int i = 0; i < lightMaxCount; i++) { var light = lightCollection[i]; var directLight = (IDirectLight)light.Type; // If the light does not intersects the model, we can skip it if (directLight.HasBoundingBox && !light.BoundingBox.Intersects(ref boundingBox)) { continue; } LightShadowMapTexture shadowTexture = null; LightShadowType shadowType = 0; ILightShadowMapRenderer newShadowRenderer = null; if (ShadowMapRenderer != null && renderViewData.LightComponentsWithShadows.TryGetValue(light, out shadowTexture)) { shadowType = shadowTexture.ShadowType; newShadowRenderer = (ILightShadowMapRenderer)shadowTexture.Renderer; } if (!hasDirectLight) { hasDirectLight = true; currentShaderKey = new LightForwardShaderEntryKey(lightRendererId, shadowType, allocCountForNewLightType); currentShadowRenderer = newShadowRenderer; } else { if (currentShaderKey.LightRendererId == lightRendererId && currentShaderKey.ShadowType == shadowType) { if (!lightRenderer.AllocateLightMaxCount) { currentShaderKey.LightCount++; } } else { unsafe { shaderKeyIdBuilder.Write(*(uint *)¤tShaderKey); } directLightShaderGroupEntryKeys.Add(new LightForwardShaderFullEntryKey(currentShaderKey, lightRenderer, isShadowReceiver ? currentShadowRenderer : null)); currentShaderKey = new LightForwardShaderEntryKey(lightRendererId, shadowType, allocCountForNewLightType); currentShadowRenderer = newShadowRenderer; } } parametersKeyIdBuilder.Write(light.Id); directLightsPerMesh.Add(new LightEntry(directLightShaderGroupEntryKeys.Count, light, shadowTexture)); } if (hasDirectLight) { unsafe { shaderKeyIdBuilder.Write(*(uint *)¤tShaderKey); } directLightShaderGroupEntryKeys.Add(new LightForwardShaderFullEntryKey(currentShaderKey, lightRenderer, isShadowReceiver ? currentShadowRenderer : null)); } } } // Find or create an existing shaders/parameters permutation // Build the keys for Shaders and Parameters permutations ObjectId shaderKeyId; ObjectId parametersKeyId; shaderKeyIdBuilder.ComputeHash(out shaderKeyId); parametersKeyIdBuilder.ComputeHash(out parametersKeyId); // Calculate the shader parameters just once // If we don't have already this permutation, use it LightShaderPermutationEntry newLightShaderPermutationEntry; if (!shaderEntries.TryGetValue(shaderKeyId, out newLightShaderPermutationEntry)) { newLightShaderPermutationEntry = CreateShaderPermutationEntry(); shaderEntries.Add(shaderKeyId, newLightShaderPermutationEntry); } // Calculate the shader parameters just once per light combination and for this rendering pass LightParametersPermutationEntry newShaderEntryParameters; if (!lightParameterEntries.TryGetValue(parametersKeyId, out newShaderEntryParameters)) { newShaderEntryParameters = CreateParametersPermutationEntry(newLightShaderPermutationEntry); lightParameterEntries.Add(parametersKeyId, newShaderEntryParameters); } return(newShaderEntryParameters); }
public void Initialize(LightComponent lightComponent, IDirectLight light, LightShadowMap shadowMap, int size, ILightShadowMapRenderer renderer) { if (lightComponent == null) throw new ArgumentNullException("lightComponent"); if (light == null) throw new ArgumentNullException("light"); if (shadowMap == null) throw new ArgumentNullException("shadowMap"); if (renderer == null) throw new ArgumentNullException("renderer"); LightComponent = lightComponent; Light = light; Shadow = shadowMap; Size = size; FilterType = Shadow.Filter == null || !Shadow.Filter.RequiresCustomBuffer() ? null : Shadow.Filter.GetType(); Renderer = renderer; Atlas = null; // Reset the atlas, It will be setup after ShadowType = renderer.GetShadowType(Shadow); }
private bool PrepareRenderModelForRendering(RenderContext context, RenderModel model) { var shaderKeyIdBuilder = new ObjectIdSimpleBuilder(); var parametersKeyIdBuilder = new ObjectIdSimpleBuilder(); //var idBuilder = new ObjectIdBuilder(); var modelComponent = model.ModelComponent; var group = modelComponent.Entity.Group; var modelBoundingBox = modelComponent.BoundingBox; directLightsPerModel.Clear(); directLightShaderGroupEntryKeys.Clear(); directLightShaderGroupEntryKeysNoShadows.Clear(); environmentLightsPerModel.Clear(); environmentLightShaderGroupEntryKeys.Clear(); // This loop is looking for visible lights per render model and calculate a ShaderId and ParametersId // TODO: Part of this loop could be processed outisde of the PrepareRenderModelForRendering // For example: Environment lights or directional lights are always active, so we could pregenerate part of the // id and groups outside this loop. Also considering that each light renderer has a maximum of lights // we could pre foreach (var activeRenderer in activeRenderers) { var lightRenderer = activeRenderer.LightRenderer; var lightCollection = activeRenderer.LightGroup.FindLightCollectionByGroup(group); var lightCount = lightCollection == null ? 0 : lightCollection.Count; int lightMaxCount = Math.Min(lightCount, lightRenderer.LightMaxCount); var lightRendererId = lightRenderer.LightRendererId; var allocCountForNewLightType = lightRenderer.AllocateLightMaxCount ? (byte)lightRenderer.LightMaxCount : (byte)1; var currentShaderKey = new LightForwardShaderEntryKey(); // Path for environment lights if (lightRenderer.IsEnvironmentLight) { // The loop is simpler for environment lights (single group per light, no shadow maps, no bounding box...etc) for (int i = 0; i < lightMaxCount; i++) { var light = lightCollection[i]; currentShaderKey = new LightForwardShaderEntryKey(lightRendererId, 0, allocCountForNewLightType); unsafe { shaderKeyIdBuilder.Write(*(uint *)¤tShaderKey); } parametersKeyIdBuilder.Write(light.Id); environmentLightsPerModel.Add(new LightEntry(environmentLightShaderGroupEntryKeys.Count, 0, light, null)); environmentLightShaderGroupEntryKeys.Add(new LightForwardShaderFullEntryKey(currentShaderKey, lightRenderer, null)); } } else { ILightShadowMapRenderer currentShadowRenderer = null; for (int i = 0; i < lightMaxCount; i++) { var light = lightCollection[i]; var directLight = (IDirectLight)light.Type; // If the light does not intersects the model, we can skip it if (directLight.HasBoundingBox && !light.BoundingBox.Intersects(ref modelBoundingBox)) { continue; } LightShadowMapTexture shadowTexture = null; LightShadowType shadowType = 0; ILightShadowMapRenderer newShadowRenderer = null; if (shadowMapRenderer != null && shadowMapRenderer.LightComponentsWithShadows.TryGetValue(light, out shadowTexture)) { shadowType = shadowTexture.ShadowType; newShadowRenderer = shadowTexture.Renderer; } if (i == 0) { currentShaderKey = new LightForwardShaderEntryKey(lightRendererId, shadowType, allocCountForNewLightType); currentShadowRenderer = newShadowRenderer; } else { if (currentShaderKey.LightRendererId == lightRendererId && currentShaderKey.ShadowType == shadowType) { if (!lightRenderer.AllocateLightMaxCount) { currentShaderKey.LightCount++; } } else { unsafe { shaderKeyIdBuilder.Write(*(uint *)¤tShaderKey); } directLightShaderGroupEntryKeys.Add(new LightForwardShaderFullEntryKey(currentShaderKey, lightRenderer, currentShadowRenderer)); currentShaderKey = new LightForwardShaderEntryKey(lightRendererId, shadowType, allocCountForNewLightType); currentShadowRenderer = newShadowRenderer; } } parametersKeyIdBuilder.Write(light.Id); directLightsPerModel.Add(new LightEntry(directLightShaderGroupEntryKeys.Count, directLightShaderGroupEntryKeysNoShadows.Count, light, shadowTexture)); } if (directLightsPerModel.Count > 0) { directLightShaderGroupEntryKeysNoShadows.Add(new LightForwardShaderFullEntryKey(new LightForwardShaderEntryKey(lightRendererId, 0, (byte)directLightsPerModel.Count), lightRenderer, null)); unsafe { shaderKeyIdBuilder.Write(*(uint *)¤tShaderKey); } directLightShaderGroupEntryKeys.Add(new LightForwardShaderFullEntryKey(currentShaderKey, lightRenderer, currentShadowRenderer)); } } } // Find or create an existing shaders/parameters permutation // Build the keys for Shaders and Parameters permutations ObjectId shaderKeyId; ObjectId parametersKeyId; shaderKeyIdBuilder.ComputeHash(out shaderKeyId); parametersKeyIdBuilder.ComputeHash(out parametersKeyId); // Calculate the shader parameters just once // If we don't have already this permutation, use it LightShaderPermutationEntry newLightShaderPermutationEntry; if (!shaderEntries.TryGetValue(shaderKeyId, out newLightShaderPermutationEntry)) { newLightShaderPermutationEntry = CreateShaderPermutationEntry(); shaderEntries.Add(shaderKeyId, newLightShaderPermutationEntry); } LightParametersPermutationEntry newShaderEntryParameters; // Calculate the shader parameters just once per light combination and for this rendering pass if (!lightParameterEntries.TryGetValue(parametersKeyId, out newShaderEntryParameters)) { newShaderEntryParameters = CreateParametersPermutationEntry(newLightShaderPermutationEntry); lightParameterEntries.Add(parametersKeyId, newShaderEntryParameters); } modelToLights.Add(model, new RenderModelLights(newLightShaderPermutationEntry, newShaderEntryParameters)); return(true); }
public override void ProcessLights(ProcessLightsParameters parameters) { if (parameters.LightCollection.Count == 0) { return; } ILightShadowMapRenderer currentShadowRenderer = null; LightShadowType currentShadowType = 0; // Start by filtering/sorting what can be processed shadowComparer.ShadowMapTexturesPerLight = parameters.ShadowMapTexturesPerLight; parameters.LightCollection.Sort(parameters.LightStart, parameters.LightEnd - parameters.LightStart, shadowComparer); for (int index = parameters.LightStart; index <= parameters.LightEnd; index++) { LightShadowType nextShadowType = 0; ILightShadowMapRenderer nextShadowRenderer = null; LightShadowMapTexture nextShadowTexture = null; LightComponent nextLight = null; if (index < parameters.LightEnd) { nextLight = parameters.LightCollection[index]; if (parameters.ShadowMapRenderer != null && parameters.ShadowMapTexturesPerLight.TryGetValue(nextLight, out nextShadowTexture) && nextShadowTexture.Atlas != null) // atlas could not be allocated? treat it as a non-shadowed texture { nextShadowType = nextShadowTexture.ShadowType; nextShadowRenderer = nextShadowTexture.Renderer; } } // Flush current group if (index == parameters.LightEnd || currentShadowType != nextShadowType || currentShadowRenderer != nextShadowRenderer) { if (processedLights.Count > 0) { var lightGroupKey = new LightGroupKey(currentShadowRenderer, currentShadowType); LightShaderGroupDynamic lightShaderGroup; if (!lightShaderGroupPool.TryGetValue(lightGroupKey, out lightShaderGroup)) { ILightShadowMapShaderGroupData shadowGroupData = null; if (currentShadowRenderer != null) { shadowGroupData = currentShadowRenderer.CreateShaderGroupData(currentShadowType); } lightShaderGroup = CreateLightShaderGroup(parameters.Context, shadowGroupData); lightShaderGroup.SetViews(parameters.Views); lightShaderGroupPool.Add(lightGroupKey, lightShaderGroup); } // Add view and lights var allowedLightCount = lightShaderGroup.AddView(parameters.ViewIndex, parameters.View, processedLights.Count); for (int i = 0; i < allowedLightCount; ++i) { var light = processedLights[i]; lightShaderGroup.AddLight(light.Light, light.ShadowMapTexture); } // TODO: assign extra lights to non-shadow rendering if possible //for (int i = lightCount; i < processedLights.Count; ++i) // XXX.AddLight(processedLights[i], null); var lightShaderGroupEntry = new LightShaderGroupEntry(lightGroupKey, lightShaderGroup); if (!lightShaderGroups.Contains(lightShaderGroupEntry)) { lightShaderGroups.Add(lightShaderGroupEntry); } processedLights.Clear(); } // Start next group currentShadowType = nextShadowType; currentShadowRenderer = nextShadowRenderer; } if (index < parameters.LightEnd) { // Do we need to process non shadowing lights or defer it to something else? if (nextShadowTexture == null && NonShadowRenderer != null) { parameters.LightStart = index; NonShadowRenderer.ProcessLights(parameters); break; } processedLights.Add(new LightDynamicEntry(nextLight, nextShadowTexture)); } } processedLights.Clear(); }
public override void ProcessLights(ProcessLightsParameters parameters) { if (parameters.LightCollection.Count == 0) { return; } // Check if we have a fallback renderer next in the chain, in case we don't need shadows bool hasNextRenderer = parameters.RendererIndex < (parameters.Renderers.Length - 1); ILightShadowMapRenderer currentShadowRenderer = null; LightShadowType currentShadowType = 0; // Start by filtering/sorting what can be processed shadowComparer.ShadowMapTexturesPerLight = parameters.ShadowMapTexturesPerLight; shadowComparer.Lights = parameters.LightCollection; parameters.LightIndices.Sort(0, parameters.LightIndices.Count, shadowComparer); // Loop over the number of lights + 1 where the last iteration will always flush the last batch of lights for (int j = 0; j < parameters.LightIndices.Count + 1;) { LightShadowType nextShadowType = 0; ILightShadowMapRenderer nextShadowRenderer = null; LightShadowMapTexture nextShadowTexture = null; LightComponent nextLight = null; if (j < parameters.LightIndices.Count) { nextLight = parameters.LightCollection[parameters.LightIndices[j]]; if (parameters.ShadowMapRenderer != null && parameters.ShadowMapTexturesPerLight.TryGetValue(nextLight, out nextShadowTexture) && nextShadowTexture.Atlas != null) // atlas could not be allocated? treat it as a non-shadowed texture { nextShadowType = nextShadowTexture.ShadowType; nextShadowRenderer = nextShadowTexture.Renderer; } } // Flush current group if (j == parameters.LightIndices.Count || currentShadowType != nextShadowType || currentShadowRenderer != nextShadowRenderer) { if (processedLights.Count > 0) { var lightGroupKey = new LightGroupKey(currentShadowRenderer, currentShadowType); LightShaderGroupDynamic lightShaderGroup; if (!lightShaderGroupPool.TryGetValue(lightGroupKey, out lightShaderGroup)) { ILightShadowMapShaderGroupData shadowGroupData = null; if (currentShadowRenderer != null) { shadowGroupData = currentShadowRenderer.CreateShaderGroupData(currentShadowType); } lightShaderGroup = CreateLightShaderGroup(parameters.Context, shadowGroupData); lightShaderGroup.SetViews(parameters.Views); lightShaderGroupPool.Add(lightGroupKey, lightShaderGroup); } // Add view and lights var allowedLightCount = lightShaderGroup.AddView(parameters.ViewIndex, parameters.View, processedLights.Count); for (int i = 0; i < allowedLightCount; ++i) { var light = processedLights[i]; lightShaderGroup.AddLight(light.Light, light.ShadowMapTexture); } // TODO: assign extra lights to non-shadow rendering if possible //for (int i = lightCount; i < processedLights.Count; ++i) // XXX.AddLight(processedLights[i], null); var lightShaderGroupEntry = new LightShaderGroupEntry(lightGroupKey, lightShaderGroup); if (!lightShaderGroups.Contains(lightShaderGroupEntry)) { lightShaderGroups.Add(lightShaderGroupEntry); } processedLights.Clear(); } // Start next group currentShadowType = nextShadowType; currentShadowRenderer = nextShadowRenderer; } if (j < parameters.LightIndices.Count) { // Do we need to process non shadowing lights or defer it to something else? if (nextShadowTexture == null && hasNextRenderer) { // Break out so the remaining lights can be handled by the next renderer break; } parameters.LightIndices.RemoveAt(j); processedLights.Add(new LightDynamicEntry(nextLight, nextShadowTexture)); } else { j++; } } processedLights.Clear(); }