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

Threejs右手系欧拉角适配Unity3D左手系问题求助

Fixing Euler Angle Mismatch Between Three.js and Unity3D

Hey there, I’ve dealt with this exact coordinate system mismatch between Three.js and Unity plenty of times—it’s such a common gotcha when moving rotation data between the two! Let’s break down why this happens and walk through reliable fixes.

Why the Mismatch Happens

First, let’s clarify the core differences that throw off your rotations:

  • Three.js: Uses a right-handed coordinate system where -Z is the forward direction, and its default Euler rotation order is XYZ (applies X rotation first, then Y, then Z).
  • Unity3D: Uses a left-handed coordinate system where +Z is forward, and its default Euler rotation order is ZXY (applies Z rotation first, then X, then Y—often called Yaw-Pitch-Roll in Unity workflows).

These two differences combined mean raw Euler angles from Three.js will look flipped or skewed when dropped directly into Unity.


Solution 1: Direct Euler Angle Conversion (Quick, But Watch for Gimbal Lock)

If you need to work directly with Euler angles, you’ll need to adjust axis signs and reorder the angles to match Unity’s system. Here’s the mapping:

  1. Flip the Z-axis value (to reverse the forward direction from -Z to +Z).
  2. Invert the Y-axis value (to account for left vs right-handed rotation direction).
  3. Ensure the rotation order matches Unity’s default ZXY (or adjust your Unity code to use Three’s XYZ order if needed).

Code Examples

Three.js (Pre-Process Before Sending to Unity)

// Your Three.js Euler angle (default order: 'XYZ')
const threeEuler = new THREE.Euler(threeX, threeY, threeZ);

// Convert to Unity-compatible Euler values
const unityEuler = {
  // X stays the same
  x: threeEuler.x,
  // Invert Y for handedness
  y: -threeEuler.y,
  // Flip Z for forward direction
  z: -threeEuler.z
};

// Send unityEuler.x, y, z to Unity

Unity (Apply the Converted Euler Angles)

// Receive values from Three.js
float convertedX = ...;
float convertedY = ...;
float convertedZ = ...;

// Apply using Unity's default rotation order (ZXY)
transform.eulerAngles = new Vector3(convertedX, convertedY, convertedZ);

// If you need to match Three's XYZ order explicitly:
transform.rotation = Quaternion.Euler(convertedX, convertedY, convertedZ);

Note: Euler angles are prone to gimbal lock, so this method works best for simple rotations. For complex or continuous motion, use quaternions instead.


Solution 2: Quaternion Conversion (More Reliable, No Gimbal Lock)

Quaternions handle 3D rotations without gimbal lock and make coordinate system conversion cleaner. Here’s the step-by-step:

  1. Convert your Three.js Euler angle to a quaternion (Three.js has built-in methods for this).
  2. Adjust the quaternion to match Unity’s left-handed system.
  3. Apply the converted quaternion directly to a Unity Transform (or convert it back to Euler angles if you need them).

Code Examples

Three.js (Convert Euler to Unity-Compatible Quaternion)

// Your source Euler angle in Three.js
const threeEuler = new THREE.Euler(threeX, threeY, threeZ);
// Convert to quaternion
const threeQuat = new THREE.Quaternion().setFromEuler(threeEuler);

// Adjust for Unity's left-handed +Z forward system
const unityQuatData = {
  x: threeQuat.x,
  y: -threeQuat.y,
  z: -threeQuat.z,
  w: threeQuat.w
};

// Send these four values to Unity

Unity (Apply the Converted Quaternion)

// Receive quaternion values from Three.js
float qX = ...;
float qY = ...;
float qZ = ...;
float qW = ...;

// Create Unity Quaternion
Quaternion unityRotation = new Quaternion(qX, qY, qZ, qW);

// Apply directly to transform (no gimbal lock issues!)
transform.rotation = unityRotation;

// Optional: Convert back to Euler angles if needed
Vector3 unityEuler = unityRotation.eulerAngles;
transform.eulerAngles = unityEuler;

Pro Tips to Avoid Headaches

  • Test with Simple Rotations: Start with 90-degree rotations around each axis to validate your conversion—e.g., rotate an object 90° around Y in Three.js, then check if it matches in Unity after conversion.
  • Explicitly Set Rotation Orders: If your rotations still look off, double-check the Euler order in both engines. Three.js lets you define order via new THREE.Euler(x,y,z,'YXZ'), and Unity’s Quaternion.Euler() can take a rotation order parameter if needed.
  • Stick to Quaternions for Continuous Motion: If you’re sending real-time rotation data (like from a Three.js animation to Unity), quaternions will give you smoother, glitch-free results.

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

火山引擎 最新活动