diff --git a/src/editor/canvas/LensMonitor.tsx b/src/editor/canvas/LensMonitor.tsx index 6d3e173..7a7fe95 100644 --- a/src/editor/canvas/LensMonitor.tsx +++ b/src/editor/canvas/LensMonitor.tsx @@ -1,6 +1,6 @@ import { Canvas, useFrame, useThree } from "@react-three/fiber"; import { PerspectiveCamera as DreiPerspectiveCamera } from "@react-three/drei"; -import { useEffect, useMemo, useRef, useState } from "react"; +import { useMemo, useRef, useState } from "react"; import { PerspectiveCamera as ThreePerspectiveCamera, Raycaster, Scene, Vector2 } from "three"; import { useDirectorStore } from "../store/directorStore"; import { usePlaybackStore } from "../runtime/playbackStore"; @@ -29,7 +29,9 @@ import { evaluateCameraViewAtTime, getActiveCameraIdAtTime } from "../schema/tim * getActiveCameraIdAtTime 解析;位姿、镜头(焦段/光圈/对焦/景深强度)、画幅全部取自这台机位, * 保证监视器所见与导出的机位视角严格一致。 * - * 刻意做成独立的第二个 Canvas:景深后处理只作用在监视器上,不影响主视口 / 截图的渲染管线。 + * 刻意做成独立的第二个 Canvas,并且无论主视口处于「导演视角」还是「机位视角」都自行渲染: + * 景深后处理只作用在监视器上,不影响主视口 / 截图的渲染管线;画面内容 / 虚化 / 取景 + * 也不会因为主视口切换视角而改变。 */ function LensMonitorCamera({ @@ -124,70 +126,8 @@ const PITCH_LIMIT = 89; /** 区分「单击对焦」与「拖拽瞄准」的位移阈值(像素) */ const CLICK_THRESHOLD_PX = 4; -/** - * 机位视角下监视器的「镜像」:主视口此刻渲染的就是机位画面,监视器没必要再开一个 Canvas - * 把整个场景重渲一遍(第二份场景 + 第二套景深后处理,既卡,又容易和主视口取景 / 虚化漂移)。 - * 这里直接把主视口画布按「传感器画幅」居中裁一块拷过来,逐像素与主视口一致。 - * - * fov 是垂直的:视口比传感器画幅更宽时按宽度裁,垂直取景保持不变 —— - * 结果与监视器自己用传感器画幅渲染完全一致。 - */ -function MonitorMirror({ sensorAspect }: { sensorAspect: number }) { - const canvasRef = useRef(null); - - useEffect(() => { - let frame = 0; - const draw = () => { - frame = requestAnimationFrame(draw); - const target = canvasRef.current; - const source = document.querySelector(".director-canvas canvas"); - if (!target || !source) return; - - // 输出尺寸跟随容器(CSS 像素) - const cssWidth = target.clientWidth; - const cssHeight = target.clientHeight; - if (!cssWidth || !cssHeight) return; - if (target.width !== cssWidth || target.height !== cssHeight) { - target.width = cssWidth; - target.height = cssHeight; - } - - const sourceWidth = source.width; - const sourceHeight = source.height; - if (!sourceWidth || !sourceHeight) return; - - let cropWidth = sourceWidth; - let cropHeight = Math.round(sourceWidth / sensorAspect); - if (cropHeight > sourceHeight) { - cropHeight = sourceHeight; - cropWidth = Math.round(sourceHeight * sensorAspect); - } - - const context = target.getContext("2d"); - if (!context) return; - context.drawImage( - source, - Math.round((sourceWidth - cropWidth) / 2), - Math.round((sourceHeight - cropHeight) / 2), - cropWidth, - cropHeight, - 0, - 0, - target.width, - target.height - ); - }; - - frame = requestAnimationFrame(draw); - return () => cancelAnimationFrame(frame); - }, [sensorAspect]); - - return ; -} - export function LensMonitor({ rightOffset = 12 }: { rightOffset?: number }) { const sceneSettings = useDirectorStore((state) => state.project.scene); - const viewMode = useDirectorStore((state) => state.viewMode); const assets = useDirectorStore((state) => state.project.assets); const panoramaAssetId = useDirectorStore((state) => state.project.panoramaAssetId); const panoramaAsset = assets.find((item) => item.id === panoramaAssetId); @@ -305,12 +245,10 @@ export function LensMonitor({ rightOffset = 12 }: { rightOffset?: number }) { onPointerCancel={endDrag} > {camera && lens ? ( - // 机位视角下主视口本身就是机位画面 → 监视器直接镜像它,不再开第二个 Canvas 重渲整个场景 - //(省掉一份场景渲染 + 一套景深后处理,而且与主视口逐像素一致)。 - // 其它视角(例如导演视角下想同时看机位画面)才需要监视器自己渲染。 - viewMode === "camera" ? ( - - ) : ( + // 无论主视口是导演视角还是机位视角,监视器都自行渲染机位画面。 + // 曾试过「机位视角下镜像主视口」省一份渲染,但主视口会带上网格 / 地面坐标轴 / 轨迹 / + // 角色标签 / 选中 gizmo,且虚化按主视口宽度计算 —— 切到机位视角后监视器就和导演视角下 + // 的干净机位画面对不上。统一自渲染才能保证两个视角下所见完全一致。 - ) ) : (
无激活机位
)}