You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

THREE.js场景中鼠标按下时实现平滑相机旋转的问题

Hey there! Let's nail that smooth 3-axis camera rotation (yaw, pitch, roll) you're aiming for—just like that showroom demo. Your start with quaternions is spot-on, since they’re the best way to avoid gimbal lock when working with three-axis rotations. Here’s how to refine and complete your implementation:

Core Approach

We’ll track mouse drag offsets to calculate rotation increments, use quaternions to handle the 3-axis rotation without gimbal lock, and apply spherical linear interpolation (slerp) to smooth the camera’s transition to the target rotation.

Step 1: Initialize Required Variables

First, set up global/class-level variables to track state:

// Track mouse interaction state
let isMouseDown = false;
let initialMousePos = new THREE.Vector2();
let initialCameraQuat = new THREE.Quaternion();

// Target rotation and smoothness control
let targetQuat = new THREE.Quaternion();
const smoothFactor = 0.1; // Lower = smoother, range 0-1
const rotationSensitivity = 0.5; // Adjust to make rotation faster/slower

Step 2:完善 Mouse Down Handler

Update your OnMouseDownHandler to record the initial state when the user starts dragging:

function OnMouseDownHandler() {
  isMouseDown = true;
  // Normalize mouse position to [-1, 1] (matches Three.js coordinate space)
  initialMousePos.set(
    (VS.Mouse.x / window.innerWidth) * 2 - 1,
    -(VS.Mouse.y / window.innerHeight) * 2 + 1
  );
  // Save the camera's current rotation to use as a starting point
  initialCameraQuat.copy(VS.Camera.quaternion);
}

Step 3: Add Mouse Move Logic

Calculate rotation offsets as the user drags, then update the target quaternion:

function OnMouseMoveHandler() {
  if (!isMouseDown) return;

  // Get current normalized mouse position
  const currentMousePos = new THREE.Vector2(
    (VS.Mouse.x / window.innerWidth) * 2 - 1,
    -(VS.Mouse.y / window.innerHeight) * 2 + 1
  );

  // Calculate how far the mouse has moved from the starting point
  const mouseDelta = new THREE.Vector2(
    currentMousePos.x - initialMousePos.x,
    currentMousePos.y - initialMousePos.y
  );

  // Calculate rotation amounts:
  // Yaw = horizontal drag (绕Y轴), Pitch = vertical drag (绕X轴)
  // Roll = optional (use Ctrl+drag for example)
  const yaw = mouseDelta.x * rotationSensitivity;
  const pitch = mouseDelta.y * rotationSensitivity;
  const roll = VS.Inputs.CtrlPressed ? mouseDelta.x * rotationSensitivity * 0.5 : 0;

  // Create a quaternion for the rotation increment (use YXZ order to avoid gimbal lock)
  const rotationIncrement = new THREE.Quaternion()
    .setFromEuler(new THREE.Euler(pitch, yaw, roll, 'YXZ'));

  // Update target rotation: initial camera rotation + drag increment
  targetQuat.copy(initialCameraQuat).multiply(rotationIncrement);
}

Step 4: Add Mouse Up Handler

Stop tracking drag when the user releases the mouse:

function OnMouseUpHandler() {
  isMouseDown = false;
}

Step 5: Smoothly Update Camera in Animation Loop

Add this to your render/animate function to interpolate the camera’s rotation to the target:

function animate() {
  requestAnimationFrame(animate);

  // Smoothly transition the camera's rotation to the target
  if (!VS.Camera.quaternion.equals(targetQuat)) {
    VS.Camera.quaternion.slerp(targetQuat, smoothFactor);
    // Optional: Reset up vector if roll causes it to drift
    VS.Camera.up.set(0, 1, 0);
    // If you're looking at a specific point, re-apply lookAt here
    // VS.Camera.lookAt(0, 0, 0);
  }

  // Your existing render logic
  renderer.render(scene, VS.Camera);
}

Key Notes

  • Quaternions > Euler Angles: Quaternions eliminate gimbal lock, which is critical for smooth 3-axis rotation.
  • Relative Drag: By tracking the initial mouse position and camera rotation, we ensure drags are relative to the starting state (intuitive for users).
  • Customizable Roll: The example uses Ctrl+drag for roll, but you can adjust this to use a different mouse button, keyboard key, or even a separate input.

If you wanted an orbit-style rotation (camera circling a fixed target point instead of free rotation), just modify the mouse move logic to rotate the camera’s position around the target instead of modifying the quaternion directly—let me know if you want that variant!

内容的提问来源于stack exchange,提问作者Manmohan Bishnoi

火山引擎 最新活动