Esempio n. 1
0
            public TEventType GetOrCreate(IInputDevice device)
            {
                TEventType item = pool.Add();

                item.Device = device;
                return(item);
            }
        /// <summary>
        /// Creates a default view with the shadow caster stage added to it
        /// </summary>
        public virtual ShadowMapRenderView CreateRenderView()
        {
            var shadowRenderView = shadowRenderViews.Add();

            shadowRenderView.RenderStages.Add(ShadowCasterRenderStage);
            return(shadowRenderView);
        }
        public virtual LightShadowMapTexture CreateShadowMapTexture(RenderView renderView, RenderLight renderLight, IDirectLight light, int shadowMapSize)
        {
            var shadowMap = shadowMaps.Add();

            shadowMap.Initialize(renderView, renderLight, light, light.Shadow, shadowMapSize, this);
            return(shadowMap);
        }
Esempio n. 4
0
        // TODO: This function is not being called anywhere. Find a way to integrate it and do the light attribute extraction here instead of within "ApplyDrawParameters()".
        public void Collect(RenderContext context, RenderView sourceView, LightShadowMapTexture lightShadowMap) // TODO: Remove the shadow map parameter.
        {
            // Computes the cascade splits
            var lightComponent = lightShadowMap.RenderLight;
            var spotLight      = (LightSpot)lightComponent.Type;

            // Get new shader data from pool
            var shaderData = shaderDataPool.Add();

            lightShadowMap.ShaderData = shaderData;

            shaderData.ProjectiveTextureMipMapLevel = (float)(lightParameters.ProjectionTexture.MipLevels - 1) * spotLight.MipMapScale; // "- 1" because the lowest mip level is 0, not 1.
            shaderData.WorldToTextureUV             = ComputeWorldToTextureUVMatrix(lightComponent);                                    // View-projection matrix without offset to cascade.
        }
        internal void AllocateCollectionsPerGroupOfCullingMask()
        {
            // Clear the pool (but keep instances already created)
            lightCollectionPool.Clear();

            // At worst, we have 32 collections (one per bit active in the culling mask)
            for (int i = 0; i < groupMasks.Length; i++)
            {
                var mask = groupMasks[i++];
                if (mask == 0)
                {
                    continue;
                }

                // Check if there is a previous collection for the current mask
                int collectionIndex = -1;
                for (int j = 0; j < lightCollectionPool.Count; j++)
                {
                    if ((uint)lightCollectionPool[j].CullingMask == mask)
                    {
                        collectionIndex = j;
                        break;
                    }
                }

                // If no collection found for this mask, create a new one
                if (collectionIndex < 0)
                {
                    collectionIndex = lightCollectionPool.Count;

                    // Use a pool to avoid recreating collections
                    var collection = lightCollectionPool.Add();

                    // The selected collection is associated with the specified mask
                    collection.CullingMask = (EntityGroupMask)mask;
                }

                // Store the index of the collection for the current bit
                groupMasks[i] = (uint)collectionIndex;
            }
        }
        public override void Collect(RenderContext context, ShadowMapRenderer shadowMapRenderer, LightShadowMapTexture lightShadowMap)
        {
            var shadow = (LightDirectionalShadowMap)lightShadowMap.Shadow;
            // TODO: Min and Max distance can be auto-computed from readback from Z buffer
            var shadowRenderView = shadowMapRenderer.CurrentView;

            var viewToWorld = shadowRenderView.View;

            viewToWorld.Invert();

            // Update the frustum infos
            UpdateFrustum(shadowRenderView);

            // Computes the cascade splits
            var minMaxDistance = ComputeCascadeSplits(context, shadowMapRenderer, ref lightShadowMap);
            var direction      = lightShadowMap.LightComponent.Direction;

            // Fake value
            // It will be setup by next loop
            Vector3 side        = Vector3.UnitX;
            Vector3 upDirection = Vector3.UnitX;

            // Select best Up vector
            // TODO: User preference?
            foreach (var vectorUp in VectorUps)
            {
                if (Math.Abs(Vector3.Dot(direction, vectorUp)) < (1.0 - 0.0001))
                {
                    side        = Vector3.Normalize(Vector3.Cross(vectorUp, direction));
                    upDirection = Vector3.Normalize(Vector3.Cross(direction, side));
                    break;
                }
            }

            int cascadeCount = lightShadowMap.CascadeCount;

            // Get new shader data from pool
            LightDirectionalShadowMapShaderData shaderData;

            if (cascadeCount == 1)
            {
                shaderData = shaderDataPoolCascade1.Add();
            }
            else if (cascadeCount == 2)
            {
                shaderData = shaderDataPoolCascade2.Add();
            }
            else
            {
                shaderData = shaderDataPoolCascade4.Add();
            }
            lightShadowMap.ShaderData = shaderData;
            shaderData.Texture        = lightShadowMap.Atlas.Texture;
            shaderData.DepthBias      = shadow.BiasParameters.DepthBias;
            shaderData.OffsetScale    = shadow.BiasParameters.NormalOffsetScale;

            float splitMaxRatio = (minMaxDistance.X - shadowRenderView.NearClipPlane) / (shadowRenderView.FarClipPlane - shadowRenderView.NearClipPlane);

            for (int cascadeLevel = 0; cascadeLevel < cascadeCount; ++cascadeLevel)
            {
                // Calculate frustum corners for this cascade
                var splitMinRatio = splitMaxRatio;
                splitMaxRatio = cascadeSplitRatios[cascadeLevel];
                for (int j = 0; j < 4; j++)
                {
                    // Calculate frustum in WS and VS
                    var frustumRange = frustumCornersWS[j + 4] - frustumCornersWS[j];
                    cascadeFrustumCornersWS[j]     = frustumCornersWS[j] + frustumRange * splitMinRatio;
                    cascadeFrustumCornersWS[j + 4] = frustumCornersWS[j] + frustumRange * splitMaxRatio;

                    frustumRange = frustumCornersVS[j + 4] - frustumCornersVS[j];
                    cascadeFrustumCornersVS[j]     = frustumCornersVS[j] + frustumRange * splitMinRatio;
                    cascadeFrustumCornersVS[j + 4] = frustumCornersVS[j] + frustumRange * splitMaxRatio;
                }

                Vector3 cascadeMinBoundLS;
                Vector3 cascadeMaxBoundLS;
                Vector3 target;

                if (!shadow.DepthRange.IsAutomatic && (shadow.StabilizationMode == LightShadowMapStabilizationMode.ViewSnapping || shadow.StabilizationMode == LightShadowMapStabilizationMode.ProjectionSnapping))
                {
                    // Make sure we are using the same direction when stabilizing
                    var boundingVS = BoundingSphere.FromPoints(cascadeFrustumCornersVS);

                    // Compute bounding box center & radius
                    target = Vector3.TransformCoordinate(boundingVS.Center, viewToWorld);
                    var radius = boundingVS.Radius;

                    //if (shadow.AutoComputeMinMax)
                    //{
                    //    var snapRadius = (float)Math.Ceiling(radius / snapRadiusValue) * snapRadiusValue;
                    //    Debug.WriteLine("Radius: {0} SnapRadius: {1} (snap: {2})", radius, snapRadius, snapRadiusValue);
                    //    radius = snapRadius;
                    //}

                    cascadeMaxBoundLS = new Vector3(radius, radius, radius);
                    cascadeMinBoundLS = -cascadeMaxBoundLS;

                    if (shadow.StabilizationMode == LightShadowMapStabilizationMode.ViewSnapping)
                    {
                        // Snap camera to texel units (so that shadow doesn't jitter when light doesn't change direction but camera is moving)
                        // Technique from ShaderX7 - Practical Cascaded Shadows Maps -  p310-311
                        var   shadowMapHalfSize = lightShadowMap.Size * 0.5f;
                        float x = (float)Math.Ceiling(Vector3.Dot(target, upDirection) * shadowMapHalfSize / radius) * radius / shadowMapHalfSize;
                        float y = (float)Math.Ceiling(Vector3.Dot(target, side) * shadowMapHalfSize / radius) * radius / shadowMapHalfSize;
                        float z = Vector3.Dot(target, direction);

                        //target = up * x + side * y + direction * R32G32B32_Float.Dot(target, direction);
                        target = upDirection * x + side * y + direction * z;
                    }
                }
                else
                {
                    var cascadeBoundWS = BoundingBox.FromPoints(cascadeFrustumCornersWS);
                    target = cascadeBoundWS.Center;

                    // Computes the bouding box of the frustum cascade in light space
                    var lightViewMatrix = Matrix.LookAtLH(cascadeBoundWS.Center, cascadeBoundWS.Center + direction, upDirection);
                    cascadeMinBoundLS = new Vector3(float.MaxValue);
                    cascadeMaxBoundLS = new Vector3(-float.MaxValue);
                    for (int i = 0; i < cascadeFrustumCornersWS.Length; i++)
                    {
                        Vector3 cornerViewSpace;
                        Vector3.TransformCoordinate(ref cascadeFrustumCornersWS[i], ref lightViewMatrix, out cornerViewSpace);

                        cascadeMinBoundLS = Vector3.Min(cascadeMinBoundLS, cornerViewSpace);
                        cascadeMaxBoundLS = Vector3.Max(cascadeMaxBoundLS, cornerViewSpace);
                    }

                    // TODO: Adjust orthoSize by taking into account filtering size
                }

                // Update the shadow camera
                var    viewMatrix       = Matrix.LookAtLH(target + direction * cascadeMinBoundLS.Z, target, upDirection);                                                                               // View;;
                var    projectionMatrix = Matrix.OrthoOffCenterLH(cascadeMinBoundLS.X, cascadeMaxBoundLS.X, cascadeMinBoundLS.Y, cascadeMaxBoundLS.Y, 0.0f, cascadeMaxBoundLS.Z - cascadeMinBoundLS.Z); // Projection
                Matrix viewProjectionMatrix;
                Matrix.Multiply(ref viewMatrix, ref projectionMatrix, out viewProjectionMatrix);

                // Stabilize the Shadow matrix on the projection
                if (shadow.StabilizationMode == LightShadowMapStabilizationMode.ProjectionSnapping)
                {
                    var shadowPixelPosition = viewProjectionMatrix.TranslationVector * lightShadowMap.Size * 0.5f;
                    shadowPixelPosition.Z = 0;
                    var shadowPixelPositionRounded = new Vector3((float)Math.Round(shadowPixelPosition.X), (float)Math.Round(shadowPixelPosition.Y), 0.0f);

                    var shadowPixelOffset = new Vector4(shadowPixelPositionRounded - shadowPixelPosition, 0.0f);
                    shadowPixelOffset     *= 2.0f / lightShadowMap.Size;
                    projectionMatrix.Row4 += shadowPixelOffset;
                    Matrix.Multiply(ref viewMatrix, ref projectionMatrix, out viewProjectionMatrix);
                }

                shaderData.ViewMatrix[cascadeLevel]       = viewMatrix;
                shaderData.ProjectionMatrix[cascadeLevel] = projectionMatrix;

                // Cascade splits in light space using depth: Store depth on first CascaderCasterMatrix in last column of each row
                shaderData.CascadeSplits[cascadeLevel] = MathUtil.Lerp(shadowRenderView.NearClipPlane, shadowRenderView.FarClipPlane, cascadeSplitRatios[cascadeLevel]);

                var shadowMapRectangle = lightShadowMap.GetRectangle(cascadeLevel);

                var cascadeTextureCoords = new Vector4((float)shadowMapRectangle.Left / lightShadowMap.Atlas.Width,
                                                       (float)shadowMapRectangle.Top / lightShadowMap.Atlas.Height,
                                                       (float)shadowMapRectangle.Right / lightShadowMap.Atlas.Width,
                                                       (float)shadowMapRectangle.Bottom / lightShadowMap.Atlas.Height);

                //// Add border (avoid using edges due to bilinear filtering and blur)
                //var borderSizeU = VsmBlurSize / lightShadowMap.Atlas.Width;
                //var borderSizeV = VsmBlurSize / lightShadowMap.Atlas.Height;
                //cascadeTextureCoords.X += borderSizeU;
                //cascadeTextureCoords.Y += borderSizeV;
                //cascadeTextureCoords.Z -= borderSizeU;
                //cascadeTextureCoords.W -= borderSizeV;

                float leftX   = (float)lightShadowMap.Size / lightShadowMap.Atlas.Width * 0.5f;
                float leftY   = (float)lightShadowMap.Size / lightShadowMap.Atlas.Height * 0.5f;
                float centerX = 0.5f * (cascadeTextureCoords.X + cascadeTextureCoords.Z);
                float centerY = 0.5f * (cascadeTextureCoords.Y + cascadeTextureCoords.W);

                // Compute receiver view proj matrix
                Matrix adjustmentMatrix = Matrix.Scaling(leftX, -leftY, 1.0f) * Matrix.Translation(centerX, centerY, 0.0f);
                // Calculate View Proj matrix from World space to Cascade space
                Matrix.Multiply(ref viewProjectionMatrix, ref adjustmentMatrix, out shaderData.WorldToShadowCascadeUV[cascadeLevel]);
            }
        }
Esempio n. 7
0
        public override LightShaderGroupData CreateGroupData()
        {
            var data = dataPool.Add();

            return(data);
        }
Esempio n. 8
0
        public override void Collect(RenderContext context, ShadowMapRenderer shadowMapRenderer, LightShadowMapTexture lightShadowMap)
        {
            // TODO: Min and Max distance can be auto-computed from readback from Z buffer
            var shadow       = (LightStandardShadowMap)lightShadowMap.Shadow;
            var shadowCamera = shadowMapRenderer.ShadowCamera;

            // Computes the cascade splits
            var lightComponent = lightShadowMap.LightComponent;
            var spotLight      = (LightSpot)lightComponent.Type;
            var position       = lightComponent.Position;
            var direction      = lightComponent.Direction;
            var target         = position + spotLight.Range * direction;
            var orthoSize      = spotLight.LightRadiusAtTarget;

            // Fake value
            // It will be setup by next loop
            Vector3 side        = Vector3.UnitX;
            Vector3 upDirection = Vector3.UnitX;

            // Select best Up vector
            // TODO: User preference?
            foreach (var vectorUp in VectorUps)
            {
                if (Vector3.Dot(direction, vectorUp) < (1.0 - 0.0001))
                {
                    side        = Vector3.Normalize(Vector3.Cross(vectorUp, direction));
                    upDirection = Vector3.Normalize(Vector3.Cross(direction, side));
                    break;
                }
            }

            // Get new shader data from pool
            var shaderData = shaderDataPool.Add();

            lightShadowMap.ShaderData = shaderData;
            shaderData.Texture        = lightShadowMap.Atlas.Texture;
            shaderData.DepthBias      = shadow.BiasParameters.DepthBias;
            shaderData.OffsetScale    = shadow.BiasParameters.NormalOffsetScale;

            // Update the shadow camera
            shadowCamera.ViewMatrix = Matrix.LookAtLH(position, target, upDirection);                                                    // View;;
            // TODO: Calculation of near and far is hardcoded/approximated. We should find a better way to calculate it.
            shadowCamera.ProjectionMatrix = Matrix.PerspectiveFovLH(spotLight.AngleOuterInRadians, 1.0f, 0.01f, spotLight.Range * 2.0f); // Perspective Projection for spotlights
            shadowCamera.Update();

            var shadowMapRectangle = lightShadowMap.GetRectangle(0);

            var cascadeTextureCoords = new Vector4((float)shadowMapRectangle.Left / lightShadowMap.Atlas.Width,
                                                   (float)shadowMapRectangle.Top / lightShadowMap.Atlas.Height,
                                                   (float)shadowMapRectangle.Right / lightShadowMap.Atlas.Width,
                                                   (float)shadowMapRectangle.Bottom / lightShadowMap.Atlas.Height);

            //// Add border (avoid using edges due to bilinear filtering and blur)
            //var borderSizeU = VsmBlurSize / lightShadowMap.Atlas.Width;
            //var borderSizeV = VsmBlurSize / lightShadowMap.Atlas.Height;
            //cascadeTextureCoords.X += borderSizeU;
            //cascadeTextureCoords.Y += borderSizeV;
            //cascadeTextureCoords.Z -= borderSizeU;
            //cascadeTextureCoords.W -= borderSizeV;

            float leftX   = (float)lightShadowMap.Size / lightShadowMap.Atlas.Width * 0.5f;
            float leftY   = (float)lightShadowMap.Size / lightShadowMap.Atlas.Height * 0.5f;
            float centerX = 0.5f * (cascadeTextureCoords.X + cascadeTextureCoords.Z);
            float centerY = 0.5f * (cascadeTextureCoords.Y + cascadeTextureCoords.W);

            // Compute receiver view proj matrix
            Matrix adjustmentMatrix = Matrix.Scaling(leftX, -leftY, 1.0f) * Matrix.Translation(centerX, centerY, 0.0f);

            // Calculate View Proj matrix from World space to Cascade space
            Matrix.Multiply(ref shadowCamera.ViewProjectionMatrix, ref adjustmentMatrix, out shaderData.WorldToShadowCascadeUV);

            shaderData.ViewMatrix       = shadowCamera.ViewMatrix;
            shaderData.ProjectionMatrix = shadowCamera.ProjectionMatrix;
        }
Esempio n. 9
0
        public override void Collect(RenderContext context, RenderView sourceView, LightShadowMapTexture lightShadowMap)
        {
            // TODO: Min and Max distance can be auto-computed from readback from Z buffer  // Yeah sure... good luck with that.
            var shadow = (LightStandardShadowMap)lightShadowMap.Shadow;

            // Computes the cascade splits
            var renderLight = lightShadowMap.RenderLight;
            var spotLight   = (LightSpot)renderLight.Type;

            // Get new shader data from pool
            var shaderData = shaderDataPool.Add();

            lightShadowMap.ShaderData = shaderData;
            shaderData.Texture        = lightShadowMap.Atlas.Texture;

            shaderData.DepthBias   = shadow.BiasParameters.DepthBias;
            shaderData.OffsetScale = shadow.BiasParameters.NormalOffsetScale;

            // 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 * 2.0f;                  // TODO: For some reason this multiplication by two is required. This should be investigated and fixed properly.

            shaderData.DepthRange = new Vector2(nearClip, farClip); //////////////////////////////////////////

            // Update the shadow camera
            Matrix.Invert(ref renderLight.WorldMatrix, out var viewMatrix);
            Matrix.PerspectiveFovRH(spotLight.AngleOuterInRadians, spotLight.AspectRatio, nearClip, farClip, out var projectionMatrix); // Perspective Projection for spotlights
            Matrix.Multiply(ref viewMatrix, ref projectionMatrix, out var viewProjectionMatrix);

            var shadowMapRectangle = lightShadowMap.GetRectangle(0);

            var cascadeTextureCoords = new Vector4(
                (float)shadowMapRectangle.Left / lightShadowMap.Atlas.Width,
                (float)shadowMapRectangle.Top / lightShadowMap.Atlas.Height,
                (float)shadowMapRectangle.Right / lightShadowMap.Atlas.Width,
                (float)shadowMapRectangle.Bottom / lightShadowMap.Atlas.Height);

            //// Add border (avoid using edges due to bilinear filtering and blur)
            //var borderSizeU = VsmBlurSize / lightShadowMap.Atlas.Width;
            //var borderSizeV = VsmBlurSize / lightShadowMap.Atlas.Height;
            //cascadeTextureCoords.X += borderSizeU;
            //cascadeTextureCoords.Y += borderSizeV;
            //cascadeTextureCoords.Z -= borderSizeU;
            //cascadeTextureCoords.W -= borderSizeV;

            float leftX   = (float)lightShadowMap.Size / lightShadowMap.Atlas.Width * 0.5f;
            float leftY   = (float)lightShadowMap.Size / lightShadowMap.Atlas.Height * 0.5f;
            float centerX = 0.5f * (cascadeTextureCoords.X + cascadeTextureCoords.Z);
            float centerY = 0.5f * (cascadeTextureCoords.Y + cascadeTextureCoords.W);

            // Compute receiver view proj matrix
            Matrix.Scaling(leftX, -leftY, 1.0f, out var scaleMatrix);
            Matrix.Translation(centerX, centerY, 0.0f, out var translationMatrix);
            Matrix.Multiply(ref scaleMatrix, ref translationMatrix, out var adjustmentMatrix);
            // Calculate View Proj matrix from World space to Cascade space
            Matrix.Multiply(ref viewProjectionMatrix, ref adjustmentMatrix, out shaderData.WorldToShadowCascadeUV);

            //Matrix rotationMatrix = Matrix.RotationZ(rotationZ);
            //Matrix.Multiply(ref viewProjectionMatrix, ref rotationMatrix, out shaderData.worldToShadowProjectiveTextureUV);

            shaderData.ViewMatrix       = viewMatrix;
            shaderData.ProjectionMatrix = projectionMatrix;

            // Allocate shadow render view
            var shadowRenderView = CreateRenderView();

            shadowRenderView.RenderView       = sourceView;
            shadowRenderView.ShadowMapTexture = lightShadowMap;
            shadowRenderView.Rectangle        = shadowMapRectangle;
            // Compute view parameters
            shadowRenderView.View       = shaderData.ViewMatrix;
            shadowRenderView.Projection = shaderData.ProjectionMatrix;
            Matrix.Multiply(ref shadowRenderView.View, ref shadowRenderView.Projection, out shadowRenderView.ViewProjection);
            shadowRenderView.ViewSize      = new Vector2(shadowMapRectangle.Width, shadowMapRectangle.Height);
            shadowRenderView.NearClipPlane = nearClip;
            shadowRenderView.FarClipPlane  = farClip;

            // Add the render view for the current frame
            context.RenderSystem.Views.Add(shadowRenderView);

            // Collect objects in shadow views
            context.VisibilityGroup.TryCollect(shadowRenderView);
        }