fix: 优化Gizmo

This commit is contained in:
2026-09-18 03:06:34 +08:00
parent 8645ea3007
commit 9b72f10b43
23 changed files with 787 additions and 254 deletions

View File

@@ -2,6 +2,7 @@ import { beforeEach, expect, it, vi } from "vitest";
import { Object3D, PerspectiveCamera, Scene, Vector2 } from "three";
import { usePlaybackStore } from "../runtime/playbackStore";
import { useDirectorStore } from "../store/directorStore";
import { getCameraLookRotationFromTarget } from "../schema/cameraGeometry";
import { exportVideo } from "./videoExport";
vi.mock("mp4-muxer", () => ({
@@ -125,7 +126,7 @@ function placeCameraShotAt(position: [number, number, number], target: [number,
const shot = store.project.cameras[0];
store.updateCamera(shot.id, {
transform: { position, rotation: [0, 0, 0], scale: [1, 1, 1] },
target,
lookRotation: getCameraLookRotationFromTarget(position, target),
fov,
});
store.setActiveCamera(shot.id);
@@ -172,7 +173,7 @@ it("lets the camera track win over the static shot pose in camera-view exports",
// 给它加一条运镜轨道:0s 处机位在 [5, 1, 0],和机位自身的 [0, 3, 9] 明显不同
useDirectorStore.getState().addCameraKeyframe(shotId, 0, {
position: [5, 1, 0],
target: [0, 1, 0],
lookRotation: getCameraLookRotationFromTarget([5, 1, 0], [0, 1, 0]),
fov: 40,
});
const bridge = createFakeBridge();

View File

@@ -6,7 +6,7 @@ import { usePlaybackStore } from "../runtime/playbackStore";
import { getVideoExportContext } from "./videoExportBridge";
import { hideViewportCaptureHelpers, withViewportCaptureHelpersHidden } from "../canvas/viewportCapture";
import { applyActiveCameraViewAtTime, applyTimelineTransformsAtTime } from "../runtime/playbackTransforms";
import { getCameraViewSnapshotFromShot, type CameraViewSnapshot } from "../schema/cameraGeometry";
import { getCameraRenderQuaternion, getCameraViewSnapshotFromShot, getWorldCameraViewSnapshot, type CameraViewSnapshot } from "../schema/cameraGeometry";
import { getViewportAspectRatioValue } from "../schema/viewportAspectRatio";
import { normalizeTimelineFps } from "../schema/timeline";
@@ -66,7 +66,7 @@ function waitNextFrame(): Promise<void> {
*/
function applyCameraSnapshotToFrameCamera(camera: PerspectiveCamera, view: CameraViewSnapshot) {
camera.position.set(view.position[0], view.position[1], view.position[2]);
camera.lookAt(view.target[0], view.target[1], view.target[2]);
camera.quaternion.copy(getCameraRenderQuaternion(view.lookRotation));
if (Math.abs(camera.fov - view.fov) > 0.001) {
camera.fov = view.fov;
}
@@ -189,7 +189,11 @@ async function exportVideoFrameByFrame(view: VideoExportView): Promise<void> {
view === "camera"
? exportState.project.cameras.find((item) => item.id === exportState.project.activeCameraId) ?? null
: null;
const activeCameraView = activeCameraShot ? getCameraViewSnapshotFromShot(activeCameraShot) : null;
// 机位线框 / 三轴 gizmo 都在场景组里,导出相机也要落在世界坐标(场景变换之内),
// 否则场景一缩放 / 旋转,导出画面里机位所在的世界位置和编辑器里看到的对不上。
const activeCameraView = activeCameraShot
? getWorldCameraViewSnapshot(getCameraViewSnapshotFromShot(activeCameraShot), exportState.project.scene)
: null;
const frameDurationUs = Math.round(1_000_000 / fps);
try {