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




