Beispiel #1
0
        public Shadowmap(Device device)
        {
            TextureSize         = 1024;                                         //todo: make this a setting...
            CascadeCount        = Math.Min(Settings.Default.ShadowCascades, 8); // 6; //use setting
            PCFSize             = 3;
            PCFOffset           = 0.000125f;                                    //0.002f
            BlurBetweenCascades = 0.05f;

            ShadowVars = new GpuVarsBuffer <ShadowmapVars>(device);


            DepthTexture    = DXUtility.CreateTexture2D(device, TextureSize * CascadeCount, TextureSize, 1, 1, Format.R32_Typeless, 1, 0, ResourceUsage.Default, BindFlags.DepthStencil | BindFlags.ShaderResource, 0, 0);
            DepthTextureSS  = DXUtility.CreateSamplerState(device, TextureAddressMode.Border, new Color4(0.0f), Comparison.Less, Filter.ComparisonMinMagLinearMipPoint, 0, 0.0f, 0.0f, 0.0f);
            DepthTextureSRV = DXUtility.CreateShaderResourceView(device, DepthTexture, Format.R32_Float, ShaderResourceViewDimension.Texture2D, 1, 0, 0, 0);
            DepthTextureDSV = DXUtility.CreateDepthStencilView(device, DepthTexture, Format.D32_Float, DepthStencilViewDimension.Texture2D);

            Cascades = new List <ShadowmapCascade>(CascadeCount);
            for (int i = 0; i < CascadeCount; i++)
            {
                ShadowmapCascade c = new ShadowmapCascade();
                c.Owner         = this;
                c.Index         = i;
                c.ZNear         = 0.0f;
                c.ZFar          = 1.0f;
                c.IntervalNear  = 0.0f;
                c.IntervalFar   = 1.0f;
                c.DepthRenderVP = new ViewportF()
                {
                    Height   = (float)TextureSize,
                    Width    = (float)TextureSize,
                    MaxDepth = 1.0f,
                    MinDepth = 0.0f,
                    X        = (float)(TextureSize * i),
                    Y        = 0,
                };
                Cascades.Add(c);
            }

            DepthRenderRS = DXUtility.CreateRasterizerState(device, FillMode.Solid, CullMode.None, true, false, true, 0, 0.0f, 1.0f);
            DepthRenderDS = DXUtility.CreateDepthStencilState(device, true, DepthWriteMask.All);

            DepthRenderVP          = new ViewportF();
            DepthRenderVP.Height   = (float)TextureSize;
            DepthRenderVP.Width    = (float)TextureSize;
            DepthRenderVP.MaxDepth = 1.0f;
            DepthRenderVP.MinDepth = 0.0f;
            DepthRenderVP.X        = 0;
            DepthRenderVP.Y        = 0;

            graphicsMemoryUsage = (long)(TextureSize * TextureSize * CascadeCount * 4);
        }
Beispiel #2
0
        public void SetFinalRenderResources(DeviceContext context)
        {
            ShadowVars.Vars.CamScenePos  = new Vector4(SceneCamPos, 0.0f); //in shadow scene coords
            ShadowVars.Vars.CamSceneView = Matrix.Transpose(SceneCamView);
            ShadowVars.Vars.LightView    = Matrix.Transpose(LightView);
            ShadowVars.Vars.LightDir     = new Vector4(LightDirection, 0.0f);

            Matrix dxmatTextureScale       = Matrix.Scaling(0.5f, -0.5f, 1.0f);
            Matrix dxmatTextureTranslation = Matrix.Translation(0.5f, 0.5f, 0.0f);
            Matrix dxmatTextureST          = Matrix.Multiply(dxmatTextureScale, dxmatTextureTranslation);

            for (int i = 0; i < CascadeCount; ++i)
            {
                ShadowmapCascade cascade        = Cascades[i];
                Matrix           mShadowTexture = Matrix.Multiply(cascade.Ortho, dxmatTextureST);
                ShadowVars.Vars.CascadeScales.Set(i, new Vector4(mShadowTexture.M11, mShadowTexture.M22, mShadowTexture.M33, 1.0f));
                ShadowVars.Vars.CascadeOffsets.Set(i, new Vector4(mShadowTexture.M41, mShadowTexture.M42, mShadowTexture.M43, 0.0f));
                ShadowVars.Vars.CascadeDepths.Set(i, new Vector4(cascade.IntervalFar, 0.0f, 0.0f, 0.0f));
            }

            ShadowVars.Vars.CascadeCount  = CascadeCount;
            ShadowVars.Vars.CascadeVisual = 0;

            ShadowVars.Vars.PCFLoopStart = (PCFSize) / -2;
            ShadowVars.Vars.PCFLoopEnd   = (PCFSize) / 2 + 1;


            // The border padding values keep the pixel shader from reading the borders during PCF filtering.
            float txs = (float)TextureSize;

            ShadowVars.Vars.BorderPaddingMax = (txs - 1.0f) / txs;
            ShadowVars.Vars.BorderPaddingMin = 1.0f / txs;

            ShadowVars.Vars.Bias = PCFOffset;
            ShadowVars.Vars.BlurBetweenCascades = BlurBetweenCascades;
            ShadowVars.Vars.CascadeCountInv     = 1.0f / (float)CascadeCount;
            ShadowVars.Vars.TexelSize           = 1.0f / txs;
            ShadowVars.Vars.TexelSizeX          = ShadowVars.Vars.TexelSize / (float)CascadeCount;
            ShadowVars.Vars.ShadowMaxDistance   = maxShadowDistance;

            ShadowVars.Update(context);


            SetBuffers(context);
        }
Beispiel #3
0
        public void BeginDepthRender(DeviceContext context, int cascadeIndex)
        {
            ShadowmapCascade cascade = Cascades[cascadeIndex];

            context.Rasterizer.SetViewport(cascade.DepthRenderVP);
        }
Beispiel #4
0
        public void BeginUpdate(DeviceContext context, Camera cam, Vector3 lightDir, List <RenderableGeometryInst> items)
        {
            //items should be potential shadow casters.

            RTS.Set(context);

            var ppos     = cam.Position;
            var view     = cam.ViewMatrix;
            var proj     = cam.ProjMatrix;
            var viewproj = cam.ViewProjMatrix;


            //need to compute a local scene space for the shadows. use a snapped version of the camera coords...
            Vector3 pp       = ppos;
            float   snapsize = 20.0f; //20m snap... //ideally should snap to texel size

            SceneOrigin.X = pp.X - (pp.X % snapsize);
            SceneOrigin.Y = pp.Y - (pp.Y % snapsize);
            SceneOrigin.Z = pp.Z - (pp.Z % snapsize);
            SceneCamPos   = (pp - SceneOrigin);


            //the items passed in here are visible items. need to compute the scene bounds from these.
            Vector4 vFLTMAX = new Vector4(float.MaxValue);
            Vector4 vFLTMIN = new Vector4(float.MinValue);
            Vector3 vHMAX   = new Vector3(float.MaxValue);
            Vector3 vHMIN   = new Vector3(float.MinValue);

            WorldMin = vHMAX;
            WorldMax = vHMIN;
            for (int i = 0; i < items.Count; i++)
            {
                var inst = items[i].Inst;

                Vector3 imin = inst.BBMin - 100.0f; //extra bias to make sure scene isn't too small in model view...
                Vector3 imax = inst.BBMax + 100.0f;

                WorldMin = Vector3.Min(WorldMin, imin);
                WorldMax = Vector3.Max(WorldMax, imax);
            }
            SceneMin    = (WorldMin - SceneOrigin);
            SceneMax    = (WorldMax - SceneOrigin);
            SceneCenter = (SceneMax + SceneMin) * 0.5f;
            SceneExtent = (SceneMax - SceneMin) * 0.5f;

            Matrix sceneCamTrans = Matrix.Translation(-SceneCamPos);

            SceneCamView = Matrix.Multiply(sceneCamTrans, view);
            Matrix camViewInv = Matrix.Invert(SceneCamView);

            Vector3 lightUp = new Vector3(0.0f, 1.0f, 0.0f);                   //BUG: should select this depending on light dir!?

            LightView      = Matrix.LookAtLH(lightDir, Vector3.Zero, lightUp); //BUG?: pos/lightdir wrong way around??
            LightDirection = lightDir;

            Vector4[] vSceneAABBPointsLightSpace = new Vector4[8];
            // This function simply converts the center and extents of an AABB into 8 points
            CreateAABBPoints(ref vSceneAABBPointsLightSpace, SceneCenter, SceneExtent);
            // Transform the scene AABB to Light space.
            for (int index = 0; index < 8; ++index)
            {
                vSceneAABBPointsLightSpace[index] = LightView.Multiply(vSceneAABBPointsLightSpace[index]);
            }


            float   fFrustumIntervalBegin, fFrustumIntervalEnd;
            Vector4 vLightCameraOrthographicMin;  // light space frustrum aabb
            Vector4 vLightCameraOrthographicMax;
            //float[] fCascadeIntervals = { 7.5f, 20.0f, 60.0f, 150.0f, 500.0f, 1000.0f, 1500.0f, 2500.0f };
            //float[] fCascadeIntervals = { 7.0f, 20.0f, 65.0f, 160.0f, 650.0f, 2000.0f, 5000.0f, 10000.0f };

            Vector4 vWorldUnitsPerTexel = Vector4.Zero;
            float   fInvTexelCount      = 1.0f / (float)TextureSize;

            // We loop over the cascades to calculate the orthographic projection for each cascade.
            for (int iCascadeIndex = 0; iCascadeIndex < CascadeCount; ++iCascadeIndex)
            {
                ShadowmapCascade cascade = Cascades[iCascadeIndex];

                fFrustumIntervalBegin = 0.0f;
                // Scale the intervals between 0 and 1. They are now percentages that we can scale with.
                fFrustumIntervalEnd = fCascadeIntervals[iCascadeIndex];
                //fFrustumIntervalBegin = fFrustumIntervalBegin * fCameraNearFarRange;
                //fFrustumIntervalEnd = fFrustumIntervalEnd * fCameraNearFarRange;
                Vector4[] vFrustumPoints = new Vector4[8];


                // This function takes the began and end intervals along with the projection matrix and returns the 8
                // points that repreresent the cascade Interval
                CreateFrustumPointsFromCascadeInterval(fFrustumIntervalBegin, fFrustumIntervalEnd, proj, ref vFrustumPoints);

                vLightCameraOrthographicMin = vFLTMAX;
                vLightCameraOrthographicMax = vFLTMIN;

                Vector4 vTempTranslatedCornerPoint;
                // This next section of code calculates the min and max values for the orthographic projection.
                for (int icpIndex = 0; icpIndex < 8; ++icpIndex)
                {
                    // Transform the frustum from camera view space to world space.
                    vFrustumPoints[icpIndex] = camViewInv.Multiply(vFrustumPoints[icpIndex]);
                    // Transform the point from world space to Light Camera Space.
                    vTempTranslatedCornerPoint = LightView.Multiply(vFrustumPoints[icpIndex]);
                    // Find the closest point.
                    vLightCameraOrthographicMin = Vector4.Min(vTempTranslatedCornerPoint, vLightCameraOrthographicMin);
                    vLightCameraOrthographicMax = Vector4.Max(vTempTranslatedCornerPoint, vLightCameraOrthographicMax);
                }

                // This code removes the shimmering effect along the edges of shadows due to
                // the light changing to fit the camera.
                // Fit the ortho projection to the cascades far plane and a near plane of zero.
                // Pad the projection to be the size of the diagonal of the Frustum partition.
                //
                // To do this, we pad the ortho transform so that it is always big enough to cover
                // the entire camera view frustum.
                Vector4 vDiagonal = (vFrustumPoints[0] - vFrustumPoints[6]);

                // The bound is the length of the diagonal of the frustum interval.
                float fCascadeBound = vDiagonal.XYZ().Length();
                vDiagonal = new Vector4(fCascadeBound);

                // The offset calculated will pad the ortho projection so that it is always the same size
                // and big enough to cover the entire cascade interval.
                Vector4 vBorderOffset = (vDiagonal - (vLightCameraOrthographicMax - vLightCameraOrthographicMin)) * 0.5f;
                // Set the Z and W components to zero.
                //vBoarderOffset *= g_vMultiplySetzwToZero;
                vBorderOffset.Z = 0.0f;
                vBorderOffset.W = 0.0f;

                // Add the offsets to the projection.
                vLightCameraOrthographicMax += vBorderOffset;
                vLightCameraOrthographicMin -= vBorderOffset;

                // The world units per texel are used to snap the shadow the orthographic projection
                // to texel sized increments.  This keeps the edges of the shadows from shimmering.
                float fWorldUnitsPerTexel = fCascadeBound / (float)TextureSize;
                vWorldUnitsPerTexel = new Vector4(fWorldUnitsPerTexel, fWorldUnitsPerTexel, 1.0f, 1.0f); //1.0 instead of 0.0 to remove divide by 0

                // We snap the camera to 1 pixel increments so that moving the camera does not cause the shadows to jitter.
                // This is a matter of integer dividing by the world space size of a texel
                vLightCameraOrthographicMin = vLightCameraOrthographicMin / vWorldUnitsPerTexel;
                vLightCameraOrthographicMin = vLightCameraOrthographicMin.Floor();
                vLightCameraOrthographicMin = vLightCameraOrthographicMin * vWorldUnitsPerTexel;

                vLightCameraOrthographicMax = vLightCameraOrthographicMax / vWorldUnitsPerTexel;
                vLightCameraOrthographicMax = vLightCameraOrthographicMax.Floor();
                vLightCameraOrthographicMax = vLightCameraOrthographicMax * vWorldUnitsPerTexel;


                //These are the unconfigured near and far plane values.  They are purposly awful to show
                // how important calculating accurate near and far planes is.
                float fNearPlane;
                float fFarPlane;


                // By intersecting the light frustum with the scene AABB we can get a tighter bound on the near and far plane.
                ComputeNearAndFar(out fNearPlane, out fFarPlane, vLightCameraOrthographicMin, vLightCameraOrthographicMax, vSceneAABBPointsLightSpace);


                // Create the orthographic projection for this cascade.
                cascade.Ortho = Matrix.OrthoOffCenterLH(vLightCameraOrthographicMin.X, vLightCameraOrthographicMax.X, vLightCameraOrthographicMin.Y, vLightCameraOrthographicMax.Y, fNearPlane, fFarPlane);

                cascade.ZNear        = fNearPlane;
                cascade.ZFar         = fFarPlane;
                cascade.IntervalNear = fFrustumIntervalBegin;
                cascade.IntervalFar  = fFrustumIntervalEnd;

                cascade.Matrix                   = Matrix.Multiply(LightView, cascade.Ortho);
                cascade.MatrixInv                = Matrix.Invert(cascade.Matrix);
                cascade.WorldUnitsPerTexel       = fWorldUnitsPerTexel;
                cascade.WorldUnitsToCascadeUnits = 2.0f / fCascadeBound;
            }


            context.ClearDepthStencilView(DepthTextureDSV, DepthStencilClearFlags.Depth, 1.0f, 0);
            // Set a null render target so as not to render color.
            context.OutputMerger.SetRenderTargets(DepthTextureDSV, (RenderTargetView)null);

            context.OutputMerger.SetDepthStencilState(DepthRenderDS);

            context.Rasterizer.State = DepthRenderRS;
        }