Beispiel #1
0
        public static void SimpleShadowmapProjection(
            List <SSObject> objects,
            SSLight light,
            FrustumCuller frustum, // can be null (disabled)
            SSCamera camera,
            out float width, out float height, out float nearZ, out float farZ,
            out Vector3 viewEye, out Vector3 viewTarget, out Vector3 viewUp)
        {
            if (light.Type != SSLight.LightType.Directional)
            {
                throw new NotSupportedException();
            }

            // light-aligned unit vectors
            Vector3 lightZ = light.Direction.Normalized();
            Vector3 lightX, lightY;

            OpenTKHelper.TwoPerpAxes(lightZ, out lightX, out lightY);

            // Step 1: light-direction aligned AABB of the visible objects
            Vector3 projBBMin = new Vector3(float.PositiveInfinity);
            Vector3 projBBMax = new Vector3(float.NegativeInfinity);

            foreach (var obj in objects)
            {
                if (obj.renderState.toBeDeleted ||
                    !obj.renderState.visible ||
                    !obj.renderState.castsShadow)
                {
                    continue;
                }
                else if (frustum == null || (obj.boundingSphere != null &&
                                             frustum.isSphereInsideFrustum(obj.Pos, obj.ScaledRadius)))
                {
                    // determine AABB in light coordinates of the objects so far
                    Vector3 lightAlignedPos = OpenTKHelper.ProjectCoord(obj.Pos, lightX, lightY, lightZ);
                    Vector3 rad             = new Vector3(obj.ScaledRadius);
                    Vector3 localMin        = lightAlignedPos - rad;
                    Vector3 localMax        = lightAlignedPos + rad;
                    projBBMin = Vector3.ComponentMin(projBBMin, localMin);
                    projBBMax = Vector3.ComponentMax(projBBMax, localMax);
                }
            }

            if (frustum != null)
            {
                // then we need to do a second pass, including shadow-casters that
                // are between the camera-frusum and the light

                // compute the camera's position in lightspace, because we need to
                // include everything "closer" that the midline of the camera frustum
                Vector3 lightAlignedCameraPos = OpenTKHelper.ProjectCoord(camera.Pos, lightX, lightY, lightZ);
                float   minZTest = lightAlignedCameraPos.Z;

                // TODO what happens if all objects are exluded?

                // Step 2: Extend Z of AABB to cover objects "between" current AABB and the light
                foreach (var obj in objects)
                {
                    if (obj.renderState.toBeDeleted ||
                        !obj.renderState.visible ||
                        !obj.renderState.castsShadow)
                    {
                        continue;
                    }

                    Vector3 lightAlignedPos = OpenTKHelper.ProjectCoord(obj.Pos, lightX, lightY, lightZ);
                    Vector3 rad             = new Vector3(obj.ScaledRadius);
                    Vector3 localMin        = lightAlignedPos - rad;
                    Vector3 localMax        = lightAlignedPos + rad;

                    if (OpenTKHelper.RectsOverlap(projBBMin.Xy, projBBMax.Xy, localMin.Xy, localMax.Xy) &&
                        localMin.Z < minZTest)
                    {
                        projBBMin = Vector3.ComponentMin(projBBMin, localMin);
                        projBBMax = Vector3.ComponentMax(projBBMax, localMax);
                    }
                }
            }
            // Finish the projection matrix

            // Use center of AABB in regular coordinates to get the view matrix
            Vector3 centerAligned = (projBBMin + projBBMax) / 2f;

            viewTarget = centerAligned.X * lightX
                         + centerAligned.Y * lightY
                         + centerAligned.Z * lightZ;
            float farEnough = (centerAligned.Z - projBBMin.Z) + 1f;

            viewEye = viewTarget - farEnough * lightZ;
            viewUp  = lightY;

            width  = projBBMax.X - projBBMin.X;
            height = projBBMax.Y - projBBMin.Y;
            nearZ  = 1f;
            farZ   = 1f + (projBBMax.Z - projBBMin.Z);
        }