private LightComponentCollectionGroup GetLightGroup(RenderViewLightData renderViewData, LightComponent light) { LightComponentCollectionGroup lightGroup; var directLight = light.Type as IDirectLight; var lightGroups = renderViewData.ActiveLightGroups; var type = light.Type.GetType(); if (!lightGroups.TryGetValue(type, out lightGroup)) { lightGroup = new LightComponentCollectionGroup(type); lightGroups.Add(type, lightGroup); } return lightGroup; }
/// <summary> /// Collects the visible lights by intersecting them with the frustum. /// </summary> private void CollectVisibleLights() { foreach (var renderView in RenderSystem.Views) { if (renderView.GetType() != typeof(RenderView)) continue; lightProcessor = renderView.SceneInstance.GetProcessor<LightProcessor>(); // No light processors means no light in the scene, so we can early exit if (lightProcessor == null) continue; RenderViewLightData renderViewLightData; if (!renderViewDatas.TryGetValue(renderView, out renderViewLightData)) { renderViewLightData = new RenderViewLightData(); renderViewDatas.Add(renderView, renderViewLightData); } else { // 1) Clear the cache of current lights (without destroying collections but keeping previously allocated ones) ClearCache(renderViewLightData.ActiveLightGroups); } renderViewLightData.VisibleLights.Clear(); renderViewLightData.VisibleLightsWithShadows.Clear(); // TODO GRAPHICS REFACTOR var sceneCullingMask = renderView.CullingMask; // 2) Cull lights with the frustum var frustum = renderView.Frustum; foreach (var light in lightProcessor.Lights) { // If light is not part of the culling mask group, we can skip it var entityLightMask = (EntityGroupMask)(1 << (int)light.Entity.Group); if ((entityLightMask & sceneCullingMask) == 0 && (light.CullingMask & sceneCullingMask) == 0) { continue; } // If light is not in the frustum, we can skip it var directLight = light.Type as IDirectLight; if (directLight != null && directLight.HasBoundingBox && !frustum.Contains(ref light.BoundingBoxExt)) { continue; } // Find the group for this light var lightGroup = GetLightGroup(renderViewLightData, light); lightGroup.PrepareLight(light); // This is a visible light renderViewLightData.VisibleLights.Add(light); // Add light to a special list if it has shadows if (directLight != null && directLight.Shadow.Enabled && ShadowMapRenderer != null) { // A visible light with shadows renderViewLightData.VisibleLightsWithShadows.Add(light); } } // 3) Allocate collection based on their culling mask AllocateCollectionsPerGroupOfCullingMask(renderViewLightData.ActiveLightGroups); // 4) Collect lights to the correct light collection group foreach (var light in renderViewLightData.VisibleLights) { var lightGroup = GetLightGroup(renderViewLightData, light); lightGroup.AddLight(light); } } }
private static void PrepareLightGroups(RenderDrawContext context, FastList<RenderView> renderViews, RenderView renderView, RenderViewLightData renderViewData, ShadowMapRenderer shadowMapRenderer, EntityGroup group) { foreach (var activeRenderer in renderViewData.ActiveRenderers) { // Find lights var lightRenderer = activeRenderer.LightRenderer; var lightCollection = activeRenderer.LightGroup.FindLightCollectionByGroup(group); var processLightsParameters = new LightGroupRendererBase.ProcessLightsParameters { Context = context, ViewIndex = renderViewData.ViewIndex, View = renderView, Views = renderViews, LightCollection = lightCollection, LightType = activeRenderer.LightGroup.LightType, LightStart = 0, LightEnd = lightCollection.Count, ShadowMapRenderer = shadowMapRenderer, ShadowMapTexturesPerLight = renderViewData.LightComponentsWithShadows, }; lightRenderer.ProcessLights(processLightsParameters); } }
private void PrepareLightGroups(RenderDrawContext context, FastList <RenderView> renderViews, RenderView renderView, RenderViewLightData renderViewData, IShadowMapRenderer shadowMapRenderer, RenderGroup group) { var viewIndex = renderViews.IndexOf(renderView); foreach (var activeRenderer in renderViewData.ActiveRenderers) { // Find lights var lightCollection = activeRenderer.LightGroup.FindLightCollectionByGroup(group); // Light collections aren't cleared (see ClearCache). Can be null after switching to empty scenes. if (lightCollection is null) { continue; } // Indices of lights in lightCollection that need processing lightIndicesToProcess.Clear(); for (int i = 0; i < lightCollection.Count; i++) { lightIndicesToProcess.Add(i); } // Loop over all the renderers in order int rendererIndex = 0; foreach (var renderer in activeRenderer.Renderers) { var processLightsParameters = new LightGroupRendererBase.ProcessLightsParameters { Context = context, ViewIndex = viewIndex, View = renderView, Views = renderViews, Renderers = activeRenderer.Renderers, RendererIndex = rendererIndex++, LightCollection = lightCollection, LightIndices = lightIndicesToProcess, LightType = activeRenderer.LightGroup.LightType, ShadowMapRenderer = shadowMapRenderer, ShadowMapTexturesPerLight = renderViewData.RenderLightsWithShadows, }; renderer.ProcessLights(processLightsParameters); } } }
/// <summary> /// Collects the visible lights by intersecting them with the frustum. /// </summary> private void CollectVisibleLights() { foreach (var renderView in RenderSystem.Views) { if (renderView.GetType() != typeof(RenderView)) { continue; } var lightRenderView = renderView.LightingView ?? renderView; // Check if already processed if (!processedRenderViews.Add(lightRenderView)) { continue; } RenderViewLightData renderViewLightData; if (!renderViewDatas.TryGetValue(lightRenderView, out renderViewLightData)) { renderViewLightData = new RenderViewLightData(); renderViewDatas.Add(lightRenderView, renderViewLightData); } else { // 1) Clear the cache of current lights (without destroying collections but keeping previously allocated ones) ClearCache(renderViewLightData.ActiveLightGroups); } renderViewLightData.VisibleLights.Clear(); renderViewLightData.VisibleLightsWithShadows.Clear(); var lights = Context.VisibilityGroup.Tags.Get(CurrentLights); // No light processors means no light in the scene, so we can early exit if (lights == null) { continue; } // TODO GRAPHICS REFACTOR var sceneCullingMask = lightRenderView.CullingMask; // 2) Cull lights with the frustum var frustum = lightRenderView.Frustum; foreach (var light in lights) { // TODO: New mechanism for light selection (probably in ForwardLighting configuration) // Light should probably have their own LightGroup (separate from RenderGroup) // If light is not part of the culling mask group, we can skip it //var entityLightMask = (RenderGroupMask)(1 << (int)light.Entity.Group); //if ((entityLightMask & sceneCullingMask) == 0 && (light.CullingMask & sceneCullingMask) == 0) //{ // continue; //} // If light is not in the frustum, we can skip it var directLight = light.Type as IDirectLight; if (directLight != null && directLight.HasBoundingBox && !frustum.Contains(ref light.BoundingBoxExt)) { continue; } // Find the group for this light var lightGroup = GetLightGroup(renderViewLightData, light); lightGroup.PrepareLight(light); // This is a visible light renderViewLightData.VisibleLights.Add(light); // Add light to a special list if it has shadows if (directLight != null && directLight.Shadow.Enabled && ShadowMapRenderer != null) { // A visible light with shadows renderViewLightData.VisibleLightsWithShadows.Add(light); } } // 3) Allocate collection based on their culling mask AllocateCollectionsPerGroupOfCullingMask(renderViewLightData.ActiveLightGroups); // 4) Collect lights to the correct light collection group foreach (var light in renderViewLightData.VisibleLights) { var lightGroup = GetLightGroup(renderViewLightData, light); lightGroup.AddLight(light); } } processedRenderViews.Clear(); }
private LightParametersPermutationEntry PrepareRenderMeshForRendering(RenderViewLightData renderViewData, RenderMesh renderMesh, RenderEffect renderEffect, int effectSlot) { var shaderKeyIdBuilder = new ObjectIdSimpleBuilder(); var parametersKeyIdBuilder = new ObjectIdSimpleBuilder(); var renderModel = renderMesh.RenderModel; var modelComponent = renderModel.ModelComponent; var group = modelComponent.Entity.Group; var boundingBox = modelComponent.BoundingBox; 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? var isShadowReceiver = renderMesh.IsShadowReceiver && modelComponent.IsShadowReceiver; 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; 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 (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, isShadowReceiver ? currentShadowRenderer : null)); currentShaderKey = new LightForwardShaderEntryKey(lightRendererId, shadowType, allocCountForNewLightType); currentShadowRenderer = newShadowRenderer; } } parametersKeyIdBuilder.Write(light.Id); directLightsPerMesh.Add(new LightEntry(directLightShaderGroupEntryKeys.Count, light, shadowTexture)); } if (directLightsPerMesh.Count > 0) { 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(renderEffect); 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); } newShaderEntryParameters.ApplyEffectPermutations(renderEffect); return(newShaderEntryParameters); }
/// <summary> /// Collects the visible lights by intersecting them with the frustum. /// </summary> private void CollectVisibleLights() { foreach (var renderView in RenderSystem.Views) { if (renderView.GetType() != typeof(RenderView)) { continue; } if (renderView.Camera == null) { continue; } lightProcessor = renderView.SceneInstance.GetProcessor <LightProcessor>(); // No light processors means no light in the scene, so we can early exit if (lightProcessor == null) { continue; } RenderViewLightData renderViewLightData; if (!renderViewDatas.TryGetValue(renderView, out renderViewLightData)) { renderViewLightData = new RenderViewLightData(); renderViewDatas.Add(renderView, renderViewLightData); } else { // 1) Clear the cache of current lights (without destroying collections but keeping previously allocated ones) ClearCache(renderViewLightData.ActiveLightGroups); ClearCache(renderViewLightData.ActiveLightGroupsWithShadows); } renderViewLightData.VisibleLights.Clear(); renderViewLightData.VisibleLightsWithShadows.Clear(); // TODO GRAPHICS REFACTOR var sceneCullingMask = renderView.SceneCameraRenderer.CullingMask; // 2) Cull lights with the frustum var frustum = renderView.Camera.Frustum; foreach (var light in lightProcessor.Lights) { // If light is not part of the culling mask group, we can skip it var entityLightMask = (EntityGroupMask)(1 << (int)light.Entity.Group); if ((entityLightMask & sceneCullingMask) == 0 && (light.CullingMask & sceneCullingMask) == 0) { continue; } // If light is not in the frustum, we can skip it var directLight = light.Type as IDirectLight; if (directLight != null && directLight.HasBoundingBox && !frustum.Contains(ref light.BoundingBoxExt)) { continue; } // Find the group for this light var lightGroup = GetLightGroup(renderViewLightData, light); lightGroup.PrepareLight(light); // This is a visible light renderViewLightData.VisibleLights.Add(light); // Add light to a special list if it has shadows if (directLight != null && directLight.Shadow.Enabled && ShadowMapRenderer != null) { // A visible light with shadows renderViewLightData.VisibleLightsWithShadows.Add(light); } } // 3) Allocate collection based on their culling mask AllocateCollectionsPerGroupOfCullingMask(renderViewLightData.ActiveLightGroups); AllocateCollectionsPerGroupOfCullingMask(renderViewLightData.ActiveLightGroupsWithShadows); // 4) Collect lights to the correct light collection group foreach (var light in renderViewLightData.VisibleLights) { var lightGroup = GetLightGroup(renderViewLightData, light); lightGroup.AddLight(light); } } }
private static void PrepareLightGroups(RenderDrawContext context, FastList <RenderView> renderViews, RenderView renderView, RenderViewLightData renderViewData, ShadowMapRenderer shadowMapRenderer, EntityGroup group) { foreach (var activeRenderer in renderViewData.ActiveRenderers) { // Find lights var lightRenderer = activeRenderer.LightRenderer; var lightCollection = activeRenderer.LightGroup.FindLightCollectionByGroup(group); var processLightsParameters = new LightGroupRendererBase.ProcessLightsParameters { Context = context, ViewIndex = renderViewData.ViewIndex, View = renderView, Views = renderViews, LightCollection = lightCollection, LightType = activeRenderer.LightGroup.LightType, LightStart = 0, LightEnd = lightCollection.Count, ShadowMapRenderer = shadowMapRenderer, ShadowMapTexturesPerLight = renderViewData.LightComponentsWithShadows, }; lightRenderer.ProcessLights(processLightsParameters); } }