public static (Matrix4x4 view, Matrix4x4 projection, bool inverseZ) PSMProjection(int lightIndex,
                                                                                          MyRenderingData renderingData, ShadowSettings settings)
        {
            var camera           = GameObject.Find("Main Camera").GetComponent <Camera>();
            var cameraView       = camera.worldToCameraMatrix;
            var cameraProjection = GL.GetGPUProjectionMatrix(camera.projectionMatrix, false);

            var light    = renderingData.cullResults.visibleLights[lightIndex].light;
            var p        = light.transform.forward.ToVector4(1);
            var l        = light.transform.forward.ToVector4(0);
            var pView    = cameraView * p;
            var lView    = cameraView * l;
            var lClip    = cameraProjection * lView.ToVector3().normalized.ToVector4(0);
            var pClip    = cameraProjection * lView.ToVector3().normalized.ToVector4(1);
            var lightNDC = lClip / pClip.w;

            var lightView = Matrix4x4.LookAt(lightNDC.ToVector3(), Vector3.forward * .5f, Vector3.up).inverse;

            var ndcBounds     = new Bounds(Vector3.forward * 0.5f, new Vector3(2, 2, 1));
            var ndcBoundVerts = new Vector3[8];

            for (int x = -1, i = 0; x <= 1; x += 2)
            {
                for (int y = -1; y <= 1; y += 2)
                {
                    for (int z = -1; z <= 1; z += 2)
                    {
                        ndcBoundVerts[i++] = ndcBounds.center + Vector3.Scale(ndcBounds.extents, new Vector3(x, y, z));
                    }
                }
            }

            var frustum = BestfitFrustum(false, lightView, ndcBoundVerts);

            var inverseZ = Vector3.Dot(lView, -lightNDC.ToVector3()) < 0;

            if (settings.debug)
            {
                ShadowUtils.DrawFrustum(frustum, false, lightView.inverse);
                ShadowUtils.DrawBound(ndcBounds, Color.magenta);
                Debug.DrawLine(Vector3.forward * 0.5f, lightNDC.ToVector3());
            }

            return(lightView, Matrix4x4.Frustum(frustum), inverseZ);
        }
Ejemplo n.º 2
0
        public static Matrix4x4 TSMTransform(Camera camera, Matrix4x4 lightViewProjection,
                                             ShadowSettings shadowSettings)
        {
            var frustumVerts = ShadowUtils
                               .GetCameraFrustumVerticies(camera, camera.nearClipPlane, shadowSettings.maxShadowDistance)
                               .Select(p => ToPostPerspective(p, lightViewProjection));
            var convex     = GetConvexHull(frustumVerts.Select(p => p.ToVector2()).ToArray());
            var nearCenter =
                ToPostPerspective(camera.transform.position + camera.transform.forward * camera.nearClipPlane,
                                  lightViewProjection).ToVector2();
            var farCenter =
                ToPostPerspective(
                    camera.transform.position + camera.transform.forward * shadowSettings.maxShadowDistance,
                    lightViewProjection).ToVector2();
            var focusPoint =
                ToPostPerspective(camera.transform.position + camera.transform.forward * shadowSettings.focusDistance,
                                  lightViewProjection).ToVector2();
            var centralLine       = farCenter - nearCenter;
            var centralLineVector = centralLine.normalized;
            var topPoint          = nearCenter +
                                    centralLineVector * convex.Min(p => Vector2.Dot(p - nearCenter, centralLineVector));
            var bottomVert  = convex.MaxOf(p => Vector2.Dot(p - nearCenter, centralLineVector), p => p);
            var bottomPoint = topPoint + centralLineVector * Vector2.Dot(bottomVert - topPoint, centralLineVector);
            var tangent     = (bottomVert - bottomPoint).normalized *
                              Mathf.Sign(MathUtility.Cross2(bottomVert - topPoint, centralLineVector));
            var height   = (bottomPoint - topPoint).magnitude;
            var focusLen = Vector2.Dot(focusPoint - topPoint, centralLineVector);

            var λ = height;
            var ξ = -0.6f;
            var δ = focusLen;
            var η = (λ * δ + λ * δ * ξ) / (λ - 2 * δ - λ * ξ);

            var origin          = -η * centralLineVector + topPoint;
            var cosMaxHalfAngle = convex.Min(p => Vector2.Dot((p - origin).normalized, centralLineVector));
            var tan             = Mathf.Sqrt(1 - cosMaxHalfAngle * cosMaxHalfAngle) / cosMaxHalfAngle;
            var minSinHalfAngle = convex.Min(p => MathUtility.Cross2((p - origin).normalized, centralLineVector));
            var maxSinHalfAngle = convex.Max(p => MathUtility.Cross2((p - origin).normalized, centralLineVector));
            var minTan          = minSinHalfAngle / Mathf.Sqrt(1 - minSinHalfAngle * minSinHalfAngle);
            var maxTan          = maxSinHalfAngle / Mathf.Sqrt(1 - maxSinHalfAngle * maxSinHalfAngle);

            var t0 = bottomPoint + (bottomPoint - origin).magnitude * minTan * tangent;
            var t1 = bottomPoint + (bottomPoint - origin).magnitude * maxTan * tangent;
            var t2 = topPoint + (topPoint - origin).magnitude * minTan * tangent;
            var t3 = topPoint + (topPoint - origin).magnitude * maxTan * tangent;

            // Transform trapezoid into unit cube
            // Following https://www.comp.nus.edu.sg/~tants/tsm/TSM_recipe.html
            var transform = Matrix4x4.identity;

            // #1
            Vector4 u = (t2 + t3) / 2;

            transform = Matrix4x4.Translate(-u) * transform;

            // #2
            u         = (t2 - t3) / (t2 - t3).magnitude;
            transform = new Matrix4x4(
                new Vector4(u.x, u.y, 0, 0),
                new Vector4(u.y, -u.x, 0, 0),
                new Vector4(0, 0, 1, 0),
                new Vector4(0, 0, 0, 1)
                ) * transform;

            // #3
            u         = transform * origin.ToVector4(0, 1);
            transform = Matrix4x4.Translate(-u) * transform;

            // #4
            u         = (transform.MultiplyPoint((t2 + t3) / 2));
            transform = new Matrix4x4(
                new Vector4(1, -u.x / u.y, 0, 0),
                new Vector4(0, 1, 0, 0),
                new Vector4(0, 0, 1, 0),
                new Vector4(0, 0, 0, 1)
                ).transpose *transform;

            // #5
            u         = transform.MultiplyPoint(t2);
            transform = Matrix4x4.Scale(new Vector3(1 / u.x, 1 / u.y, 1)) * transform;

            // #6
            transform = new Matrix4x4(
                new Vector4(1, 0, 0, 0),
                new Vector4(0, 1, 0, 1),
                new Vector4(0, 0, 1, 0),
                new Vector4(0, 1, 0, 0)
                ) * transform;

            // #7
            u = transform * t0.ToVector4(0, 1);
            var v = transform * t2.ToVector4(0, 1);

            transform = Matrix4x4.Translate(new Vector3(0, -(u.y / u.w + v.y / v.w) / 2, 0)) * transform;

            // #8
            u         = transform * t0.ToVector4(0, 1);
            transform = Matrix4x4.Scale(new Vector3(1, -u.w / u.y, 1)) * transform;


            var trapezoidal = new Vector2[] { t0, t1, t3, t2 };
            var t           = trapezoidal.Select(p => transform.MultiplyPoint(p).ToVector2()).ToArray();

            //DrawPolygonOnLightPlane(t, lightViewProjection, Color.blue);
            if (shadowSettings.debug)
            {
                ShadowUtils.DrawPolygonOnLightPlane(trapezoidal, lightViewProjection, Color.blue);
                ShadowUtils.DrawPolygonOnLightPlane(new Vector2[] { nearCenter, farCenter }, lightViewProjection, Color.red);
                ShadowUtils.DrawPolygonOnLightPlane(convex, lightViewProjection, Color.red);
            }

            return(transform);
        }
Ejemplo n.º 3
0
        public static (Matrix4x4 view, Matrix4x4 projection) GetShadowViewProjection(ShadowSettings settings,
                                                                                     MyRenderingData renderingData, int lightIndex)
        {
            var camera = renderingData.camera;

            if (settings.debug)
            {
                camera = GameObject.Find("Main Camera").GetComponent <Camera>();
            }
            var cameraToWorld = camera.transform.localToWorldMatrix;
            var p0            = cameraToWorld.MultiplyPoint(new Vector3(0, 0, 0));
            var h             = Mathf.Tan(camera.fieldOfView * Mathf.Deg2Rad / 2);
            var w             = h * camera.aspect;

            var p1 = cameraToWorld.MultiplyPoint(new Vector3(-w, -h, 1) * settings.maxShadowDistance);
            var p2 = cameraToWorld.MultiplyPoint(new Vector3(w, -h, 1) * settings.maxShadowDistance);
            var p3 = cameraToWorld.MultiplyPoint(new Vector3(w, h, 1) * settings.maxShadowDistance);
            var p4 = cameraToWorld.MultiplyPoint(new Vector3(-w, h, 1) * settings.maxShadowDistance);

            //左右手坐标转换
            var view = Matrix4x4.Scale(new Vector3(1, 1, -1)) * settings.transform.worldToLocalMatrix;

            renderingData.cullResults.GetShadowCasterBounds(lightIndex, out var bounds);

            //阴影包围盒
            var casterBoundVerts = new Vector3[8];

            for (int x = -1, i = 0; x <= 1; x += 2)
            {
                for (int y = -1; y <= 1; y += 2)
                {
                    for (int z = -1; z <= 1; z += 2)
                    {
                        casterBoundVerts[i++] = bounds.center + Vector3.Scale(bounds.extents, new Vector3(x, y, z));
                    }
                }
            }

            var debug = camera.name == "Main Camera" && settings.debug;

            if (settings.light.type == LightType.Point)
            {
                //朝向 + shadow框
                var rotation = Matrix4x4.Rotate(Quaternion.FromToRotation(Vector3.forward,
                                                                          settings.transform.worldToLocalMatrix.MultiplyPoint(bounds.center).normalized));
                var rotatedView   = rotation.inverse * settings.light.transform.worldToLocalMatrix;
                var frustumCaster = BestfitFrustum(false, rotatedView, casterBoundVerts);

                if (debug)
                {
                    ShadowUtils.DrawBound(bounds, Color.cyan);
                    ShadowUtils.DrawFrustum(frustumCaster, false, rotatedView.inverse);
                }

                return(Matrix4x4.Scale(new Vector3(1, 1, -1)) * rotatedView, Matrix4x4.Frustum(frustumCaster));
            }
            else if (settings.light.type == LightType.Spot)
            {
                Matrix4x4 lightView     = settings.light.transform.worldToLocalMatrix;
                var       frustumCamera = BestfitFrustum(false, lightView, p0, p1, p2, p3, p4);
                var       frustumCaster = BestfitFrustum(false, lightView, casterBoundVerts);
                var       near          = settings.nearDistance;
                var       far           = Mathf.Min(frustumCaster.zFar, frustumCamera.zFar, settings.light.range);

                return(view, Matrix4x4.Perspective(settings.light.spotAngle, 1, near, far));
            }
            else if (settings.light.type == LightType.Directional)
            {
                //shadow框和摄像机框 比较 选择最大/小的
                var frustumCamera =
                    BestfitFrustum(true, settings.light.transform.worldToLocalMatrix, p0, p1, p2, p3, p4);
                var frustumCaster = BestfitFrustum(true, settings.light.transform.worldToLocalMatrix, casterBoundVerts);

                var left   = Mathf.Max(frustumCaster.left, frustumCamera.left);
                var right  = Mathf.Min(frustumCaster.right, frustumCamera.right);
                var bottom = Mathf.Max(frustumCaster.bottom, frustumCamera.bottom);
                var top    = Mathf.Min(frustumCaster.top, frustumCamera.top);
                var zNear  = frustumCaster.zNear;
                var zFar   = Mathf.Min(frustumCaster.zFar, frustumCamera.zFar);

                if (debug)
                {
                    var bound = new Bounds();
                    bound.min = new Vector3(left, bottom, zNear);
                    bound.max = new Vector3(right, top, zFar);
                    ShadowUtils.DrawFrustum(new FrustumPlanes()
                    {
                        left   = Mathf.Max(frustumCaster.left, frustumCamera.left),
                        right  = Mathf.Min(frustumCaster.right, frustumCamera.right),
                        bottom = Mathf.Max(frustumCaster.bottom, frustumCamera.bottom),
                        top    = Mathf.Min(frustumCaster.top, frustumCamera.top),
                        zNear  = frustumCaster.zNear,
                        zFar   = Mathf.Min(frustumCaster.zFar, frustumCamera.zFar),
                    }, true, settings.transform.localToWorldMatrix);
                    //DrawBound(bounds, Color.cyan);
                    Debug.DrawRay(
                        settings.transform.localToWorldMatrix.MultiplyPoint(new Vector3(bound.center.x, bound.center.y,
                                                                                        zNear)), settings.transform.forward * 5, Color.green);
                }

                return(view, Matrix4x4.Ortho(left, right, bottom, top, zNear, zFar));
            }

            return(Matrix4x4.identity, Matrix4x4.identity);
        }