Ejemplo n.º 1
0
        public override bool Update(RenderLight light)
        {
            var range = Math.Max(0.001f, Radius);

            InvSquareRadius = 1.0f / (range * range);
            return(true);
        }
        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);
            }
        }
        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;
            }
        }
        // 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.
        }
Ejemplo n.º 5
0
        public override bool Update(RenderLight light)
        {
            var range = Math.Max(0.001f, Range);

            InvSquareRange = 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);
        }
        // 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.
        }
        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);
        }
Ejemplo n.º 8
0
 private LightShadowType GetShadowType(RenderLight light)
 {
     ShadowMapTexturesPerLight.TryGetValue(light, out LightShadowMapTexture shadow);
     return(shadow?.ShadowType ?? 0);
 }
Ejemplo n.º 9
0
        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();
        }
Ejemplo n.º 10
0
 public override bool Update(RenderLight light)
 {
     return(true);
 }
Ejemplo n.º 11
0
 public bool Update(RenderLight light)
 {
     Rotation = Quaternion.RotationMatrix(light.WorldMatrix);
     return(Skybox != null);
 }
Ejemplo n.º 12
0
 public LightDynamicEntry(RenderLight light, LightShadowMapTexture shadowMapTexture)
 {
     Light            = light;
     ShadowMapTexture = shadowMapTexture;
 }
Ejemplo n.º 13
0
 /// <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);
 }
Ejemplo n.º 14
0
        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);
        }
Ejemplo n.º 15
0
 public abstract bool Update(RenderLight light);