internal void AddLight(RenderLight renderLight) { var cullingMask = RenderGroupMask.All; //lightComponent.CullingMask; // Iterate only on allocated collections foreach (var lightCollectionGroup in lightCollectionPool) { // Check if a light culling mask belong to the current group if ((lightCollectionGroup.CullingMask & cullingMask) == 0) { continue; } lightCollectionGroup.Add(renderLight); } // Keep a list of all lights for this group allLights.Add(renderLight); // If the light has shadows, populate a separate list var directLight = renderLight.Type as IDirectLight; if (directLight != null && directLight.Shadow.Enabled) { allLightsWithShadows.Add(renderLight); } }
// Computes the view-projection matrix without any offset or scaling cascade. private static Matrix ComputeProjectorPlaneMatrix(RenderLight light) { var spotLight = (LightSpot)light.Type; // Calculate the width and height of the near plane in world space: var nearClipDistance = Math.Min(spotLight.ProjectionPlaneDistance, spotLight.Range); float angleOuterInRadians = MathUtil.DegreesToRadians(Math.Max(spotLight.AngleInner, spotLight.AngleOuter)); float height = (float)Math.Tan(angleOuterInRadians / 2.0f) * nearClipDistance; // TODO: Is this correct? float width = height * spotLight.AspectRatio; // TODO: Is this correct? Matrix viewMatrix = light.WorldMatrix; // Translate the matrix to position it at the near plane. Vector3 translation = viewMatrix.Forward * nearClipDistance; viewMatrix.M41 += translation.X; viewMatrix.M42 += translation.Y; viewMatrix.M43 += translation.Z; // Scale the X axis by the plane width: viewMatrix.M11 *= -width; viewMatrix.M12 *= -width; // Invert the matrix so we don't have to do it in the shader. viewMatrix.M13 *= -width; // Scale the Y axis by the plane width: viewMatrix.M21 *= -height; viewMatrix.M22 *= -height; // Invert the matrix so we don't have to do it in the shader. viewMatrix.M23 *= -height; return(viewMatrix); // Model matrix of the projector plane. }
public override bool Update(RenderLight light) { var range = Math.Max(0.001f, Radius); InvSquareRadius = 1.0f / (range * range); return(true); }
internal void PrepareLight(RenderLight renderLight) { var cullingMask = RenderGroupMask.All; //lightComponent.CullingMask; // Don't procses a mask have we have already processed. We don't expect a huge combination of culling mask here if (!allMasks.Add(cullingMask)) { return; } // Fit individual bits and prepare collection group based on mask // We iterate on each possible bits and we `And` all active masks for this particular bit var groupMask = (uint)cullingMask; for (int groupIndex = 0; groupMask != 0; groupMask = groupMask >> 1, groupIndex++) { if ((groupMask & 1) == 0) { continue; } var previousMask = groupMasks[groupIndex * 2]; previousMask = previousMask == 0 ? (uint)cullingMask : previousMask & (uint)cullingMask; groupMasks[groupIndex * 2] = previousMask; } }
public override bool Update(RenderLight light) { var range = Math.Max(0.001f, Radius); InvSquareRadius = LightClusteredPointSpotGroupRenderer.UseLinearLighting ? range : 1.0f / (range * range); return(true); }
public override bool Update(RenderLight light) { var range = Math.Max(0.001f, Range); InvSquareRange = LightClusteredPointSpotGroupRenderer.UseLinearLighting ? range : 1.0f / (range * range); var innerAngle = Math.Min(AngleInner, AngleOuter); var outerAngle = Math.Max(AngleInner, AngleOuter); AngleOuterInRadians = MathUtil.DegreesToRadians(outerAngle); var cosInner = (float)Math.Cos(MathUtil.DegreesToRadians(innerAngle / 2)); var cosOuter = (float)Math.Cos(AngleOuterInRadians * 0.5f); LightAngleScale = 1.0f / Math.Max(0.001f, cosInner - cosOuter); LightAngleOffset = -cosOuter * LightAngleScale; LightRadiusAtTarget = (float)Math.Abs(Range * Math.Sin(AngleOuterInRadians * 0.5f)); return(true); }
private bool CanRenderLight(RenderLight renderLight, ref ProcessLightsParameters parameters, bool hasNextRenderer) { Texture projectionTexture = null; if (renderLight.Type is LightSpot spotLight) // TODO: PERFORMANCE: I would say that casting this for every light is slow, no? { projectionTexture = spotLight.ProjectiveTexture; } // Check if there might be a renderer that supports shadows instead (in that case skip the light) LightShadowMapTexture shadowMapTexture; if ((hasNextRenderer && parameters.ShadowMapTexturesPerLight.TryGetValue(renderLight, out shadowMapTexture)) // If the light has shadows: // TODO: Check for "hasNextRenderer && projectionTexture != null" instead? || projectionTexture != null) // If the light projects a texture (we check for this because otherwise this renderer would "steal" the light from the spot light renderer which handles texture projection): { // Ignore lights with textures or shadows. return false; } return true; }
// Computes the view-projection matrix without any offset or scaling cascade. private static Matrix ComputeWorldToTextureUVMatrix(RenderLight light) { var spotLight = (LightSpot)light.Type; Matrix.Invert(ref light.WorldMatrix, out var viewMatrix); // TODO: PERFORMANCE: This does redundant work. The view projection matrix is already calculated within "LightSpotShadowMapRenderer". // TODO: Calculation of near and far is hardcoded/approximated. We should find a better way to calculate it. var nearClip = 0.01f; // TODO: This should be configurable. var farClip = spotLight.Range; // Removed the multiplication by two because I didn't see the point in it. var projectionMatrix = Matrix.PerspectiveFovRH(spotLight.AngleOuterInRadians, spotLight.AspectRatio, nearClip, farClip); // Perspective Projection for spotlights Matrix viewProjectionMatrix; Matrix.Multiply(ref viewMatrix, ref projectionMatrix, out viewProjectionMatrix); // TODO: Add an offset so we don't have to do it in the shader? return(viewProjectionMatrix); // View-projection matrix without offset to cascade. }
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); var currentGroupParameters = SpotLightGroupParameters.Null; // 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: var nextGroupParameters = SpotLightGroupParameters.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 (nextLight.Type is LightSpot spotLight) { if (spotLight.ProjectiveTexture != null) // TODO: Remove this branch?! { nextGroupParameters.SpotParameters.ProjectionTexture = spotLight.ProjectiveTexture; nextGroupParameters.SpotParameters.FlipMode = spotLight.FlipMode; nextGroupParameters.SpotParameters.UVScale = spotLight.UVScale; nextGroupParameters.SpotParameters.UVOffset = spotLight.UVOffset; } } 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 { nextGroupParameters.ShadowType = nextShadowTexture.ShadowType; nextGroupParameters.ShadowRenderer = 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 || !currentGroupParameters.Equals(ref nextGroupParameters)) { if (processedLights.Count > 0) { ITextureProjectionRenderer currentTextureProjectionRenderer = FindOrCreateTextureProjectionRenderer(currentGroupParameters); var lightGroupKey = new LightGroupKey(currentGroupParameters.ShadowRenderer, currentGroupParameters.ShadowType, currentTextureProjectionRenderer); 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 currentGroupParameters = nextGroupParameters; } 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(); }
/// <summary> /// Try to add light to this group (returns false if not possible). /// </summary> /// <param name="light"></param> /// <param name="shadowMapTexture"></param> /// <returns></returns> public bool AddLight(RenderLight light, LightShadowMapTexture shadowMapTexture) { lights.Add(new LightDynamicEntry(light, shadowMapTexture)); return(true); }
public abstract bool Update(RenderLight light);
public override bool Update(RenderLight light) { return(true); }
private LightShadowType GetShadowType(RenderLight light) { ShadowMapTexturesPerLight.TryGetValue(light, out LightShadowMapTexture shadow); return(shadow?.ShadowType ?? 0); }
public bool Update(RenderLight light) { return(true); }
public LightDynamicEntry(RenderLight light, LightShadowMapTexture shadowMapTexture) { Light = light; ShadowMapTexture = shadowMapTexture; }
private RenderLightCollectionGroup GetLightGroup(RenderViewLightData renderViewData, RenderLight light) { RenderLightCollectionGroup lightGroup; var directLight = light.Type as IDirectLight; var lightGroups = renderViewData.ActiveLightGroups; var type = light.Type.GetType(); if (!lightGroups.TryGetValue(type, out lightGroup)) { lightGroup = new RenderLightCollectionGroup(type); lightGroups.Add(type, lightGroup); } return(lightGroup); }
public bool Update(RenderLight light) { Rotation = Quaternion.RotationMatrix(light.WorldMatrix); return(Skybox != null); }