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);
        }
예제 #2
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);
        }