Skip to content

向量、位置与旋转

理解如何在 3D 空间中定位和调整对象的方向是至关重要的。BabylonJS 使用右手坐标系。默认情况下:

  • **X 轴:**指向右侧。
  • **Y 轴:**指向上方。
  • **Z 轴:**指向屏幕外,朝向你。

我们将使用 BABYLON.Vector3 来表示位置和旋转(以欧拉角 Euler angles 表示)。

以下代码包含一个辅助函数 showAxis,它绘制 X、Y 和 Z 轴的线条和标签,使可视化坐标系和对象放置位置变得更容易。

import * as BABYLON from '@babylonjs/core';
import '@babylonjs/gui'; // Needed for DynamicTexture used in showAxis
const canvas = document.getElementById('renderCanvas');
const engine = new BABYLON.Engine(canvas, true);
const createScene = async function() {
const scene = new BABYLON.Scene(engine);
scene.clearColor = new BABYLON.Color4(0.2, 0.2, 0.2, 1.0);
// Camera: Positioned to view the origin
// 摄像机:放置在原点以方便查看
const camera = new BABYLON.ArcRotateCamera("camera", -Math.PI / 2 + 0.5, Math.PI / 2.5, 20, BABYLON.Vector3.Zero(), scene);
camera.attachControl(canvas, true);
camera.wheelPrecision = 50; // Adjust zoom speed
// Light
// 光源
const light = new BABYLON.HemisphericLight("light", new BABYLON.Vector3(0, 1, 0), scene);
light.intensity = 0.8;
// --- Axis Visualization Function ---
// --- 轴可视化函数 ---
const showAxis = function(size) {
const makeTextPlane = function(text, color, textSize) {
const dynamicTexture = new BABYLON.DynamicTexture("DynamicTexture", {
width: text.length * 20 + 20, // Adjust width based on text length
height: 70
}, scene, false);
dynamicTexture.hasAlpha = true;
dynamicTexture.drawText(text, null, null, "bold 36px Arial", color, "transparent", true);
const plane = BABYLON.MeshBuilder.CreatePlane("TextPlane", {size: textSize}, scene);
plane.material = new BABYLON.StandardMaterial("TextPlaneMaterial", scene); // StandardMaterial works well for unlit text
plane.material.backFaceCulling = false;
plane.material.specularColor = new BABYLON.Color3(0, 0, 0);
plane.material.diffuseTexture = dynamicTexture;
plane.material.useAlphaFromDiffuseTexture = true;
return plane;
};
const axisX = BABYLON.MeshBuilder.CreateLines("axisX", {
points: [
new BABYLON.Vector3(-size, 0, 0), new BABYLON.Vector3(size, 0, 0)
]
}, scene);
axisX.color = new BABYLON.Color3(1, 0, 0);
const xChar = makeTextPlane("X", "red", size / 10);
xChar.position = new BABYLON.Vector3(size * 0.9, 0.05 * size, 0);
// Add numbers along X
// 在 X 轴上添加数字标记
for (let i = -size; i <= size; i++) {
if (i === 0) continue;
const xNum = makeTextPlane(i.toString(), "red", size / 20);
xNum.position = new BABYLON.Vector3(i, 0.05 * size, 0);
}
const axisY = BABYLON.MeshBuilder.CreateLines("axisY", {
points: [
new BABYLON.Vector3(0, -size, 0), new BABYLON.Vector3(0, size, 0)
]
}, scene);
axisY.color = new BABYLON.Color3(0, 1, 0);
const yChar = makeTextPlane("Y", "green", size / 10);
yChar.position = new BABYLON.Vector3(0, size * 0.9, 0.05 * size);
// Add numbers along Y
// 在 Y 轴上添加数字标记
for (let i = -size; i <= size; i++) {
if (i === 0) continue;
const yNum = makeTextPlane(i.toString(), "green", size / 20);
yNum.position = new BABYLON.Vector3(0, i, 0.05 * size);
}
const axisZ = BABYLON.MeshBuilder.CreateLines("axisZ", {
points: [
new BABYLON.Vector3(0, 0, -size), new BABYLON.Vector3(0, 0, size)
]
}, scene);
axisZ.color = new BABYLON.Color3(0, 0, 1);
const zChar = makeTextPlane("Z", "blue", size / 10);
zChar.position = new BABYLON.Vector3(0, 0.05 * size, size * 0.9);
// Add numbers along Z
// 在 Z 轴上添加数字标记
for (let i = -size; i <= size; i++) {
if (i === 0) continue;
const zNum = makeTextPlane(i.toString(), "blue", size / 20);
zNum.position = new BABYLON.Vector3(0, 0.05 * size, i);
// Rotate Z labels to face camera better initially
// 旋转 Z 轴标签使其初始更好地朝向摄像机
zNum.rotation.y = Math.PI / 2;
}
};
// --- End Axis Visualization Function ---
// --- 轴可视化函数结束 ---
// Call the function to draw axes of size 10
// 调用函数绘制大小为 10 的轴
showAxis(10);
return scene;
};
createScene().then(scene => {
engine.runRenderLoop(function() {
if (scene) {
scene.render();
}
});
});
window.addEventListener('resize', function() {
engine.resize();
});

这个示例绘制了以 (0,0,0) 为原点的红色 (X)、绿色 (Y) 和蓝色 (Z) 线条,带有标签和数字标记。运行此代码可以获得一个清晰的坐标系视觉参考。

注意:makeTextPlane 函数使用 DynamicTexture 在一个平面网格体上绘制文本。对于更复杂的 UI 或文本渲染,可以考虑使用 BabylonJS GUI 扩展 (@babylonjs/gui)。

每个网格体都有一个 position 属性,它是一个 BABYLON.Vector3。你可以直接设置其 x、y 和 z 分量,或者分配一个新的 Vector3。

// Inside createScene, after showAxis(10);
// 在 createScene 内部,showAxis(10); 之后
// --- Positioning Meshes ---
// --- 网格体定位 ---
// Create a sphere material
// 创建一个球体材质
const sphereMat = new BABYLON.PBRMaterial("sphereMat", scene);
sphereMat.albedoColor = new BABYLON.Color3(1, 1, 0); // Yellow 黄色
sphereMat.metallic = 0.2;
sphereMat.roughness = 0.5;
// Sphere 1: Set components individually
// 球体 1:单独设置分量
const sphere1 = BABYLON.MeshBuilder.CreateSphere("sphere1", {diameter: 2}, scene);
sphere1.material = sphereMat;
sphere1.position.x = 5;
sphere1.position.y = 2;
sphere1.position.z = 3;
// Final position: (5, 2, 3)
// 最终位置:(5, 2, 3)
// Sphere 2: Assign a new Vector3
// 球体 2:分配一个新的 Vector3
const sphere2 = BABYLON.MeshBuilder.CreateSphere("sphere2", {diameter: 1.5}, scene);
sphere2.material = sphereMat;
sphere2.position = new BABYLON.Vector3(-4, 1, -2);
// Final position: (-4, 1, -2)
// 最终位置:(-4, 1, -2)
// Sphere 3: Default position is (0, 0, 0)
// 球体 3:默认位置是 (0, 0, 0)
const sphere3 = BABYLON.MeshBuilder.CreateSphere("sphere3", {diameter: 1}, scene);
sphere3.material = sphereMat;
// Final position: (0, 0, 0)
// 最终位置:(0, 0, 0)
// --- Moving Meshes (Relative Positioning) ---
// --- 移动网格体(相对定位)---
// Move sphere1 2 units along the positive X-axis from its current position
// 将 sphere1 沿着正 X 轴从当前位置移动 2 个单位
sphere1.position.x += 2; // Now at (7, 2, 3) 现在位于 (7, 2, 3)
// Move sphere2 using addInPlace
// 使用 addInPlace 移动 sphere2
const moveVector = new BABYLON.Vector3(1, 2, 0);
sphere2.position.addInPlace(moveVector); // Now at (-3, 3, -2) 现在位于 (-3, 3, -2)
// Translate sphere3 along its local Z-axis (initially world Z)
// 沿 sphere3 的本地 Z 轴(最初的世界 Z 轴)平移
// Note: This requires considering rotation if the mesh is rotated.
// 注意:如果网格体已旋转,则需要考虑旋转。
// For simple translation along world axes, addInPlace or direct modification is simpler.
// 对于沿世界轴的简单平移,使用 addInPlace 或直接修改更简单。
sphere3.translate(BABYLON.Axis.Z, 5, BABYLON.Space.WORLD); // Now at (0, 0, 5) 现在位于 (0, 0, 5)

用于定位的关键方法:

  • mesh.position:获取或设置网格体在世界空间中的位置 Vector3。
  • mesh.position.x / .y / .z:访问单独的分量。
  • mesh.position.addInPlace(vector):将 vector 的分量添加到网格体当前位置(修改 mesh.position)。适用于频繁更新。
  • mesh.translate(axis, distance, space):沿指定的 axis (BABYLON.Axis.X/Y/Z) 移动网格体 distance 距离。space 可以是 BABYLON.Space.LOCAL (相对于网格体旋转) 或 BABYLON.Space.WORLD (相对于世界坐标系)。

旋转通常使用欧拉角(绕 X、Y 和 Z 轴的旋转)表示。BabylonJS 将其存储在 mesh.rotation 属性中(一个 Vector3),其中分量表示以**弧度(radians)**为单位的角度。

或者,四元数 (mesh.rotationQuaternion) 用于更复杂或数值更稳定的旋转,特别是在插值或避免万向锁(gimbal lock)时。对于基本旋转,欧拉角通常更简单。

记住:**角度以弧度为单位!**要将角度转换为弧度,请使用以下公式:弧度 = 角度 * (Math.PI / 180)。

// Inside createScene...
// 在 createScene 内部...
// --- Rotating Meshes ---
// --- 旋转网格体 ---
// Create a box material
// 创建一个盒子材质
const boxMat = new BABYLON.PBRMaterial("boxMat", scene);
boxMat.albedoColor = new BABYLON.Color3(0.6, 0.6, 1.0); // Light Blue 浅蓝色
boxMat.metallic = 0.1;
boxMat.roughness = 0.7;
// Box 1: Rotate 45 degrees around the Y-axis
// 盒子 1:绕 Y 轴旋转 45 度
const box1 = BABYLON.MeshBuilder.CreateBox("box1", {width: 4, height: 1, depth: 2}, scene);
box1.material = boxMat;
box1.position = new BABYLON.Vector3(-5, 0.5, 5);
box1.rotation.y = Math.PI / 4; // 45 degrees in radians 45 度(弧度)
// Box 2: Set multiple rotations
// 盒子 2:设置多个旋转
const box2 = BABYLON.MeshBuilder.CreateBox("box2", {size: 2}, scene);
box2.material = boxMat;
box2.position = new BABYLON.Vector3(5, 1, -5);
box2.rotation = new BABYLON.Vector3(
BABYLON.Tools.ToRadians(30), // 30 degrees around X 绕 X 旋转 30 度
BABYLON.Tools.ToRadians(0), // 0 degrees around Y 绕 Y 旋转 0 度
BABYLON.Tools.ToRadians(-45) // -45 degrees around Z 绕 Z 旋转 -45 度
);
// Box 3: Add rotation incrementally (e.g., in render loop)
// 盒子 3:增量添加旋转(例如,在渲染循环中)
const box3 = BABYLON.MeshBuilder.CreateBox("box3", {size: 1.5}, scene);
box3.material = boxMat;
box3.position = new BABYLON.Vector3(0, 0.75, 0);
scene.onBeforeRenderObservable.add(() => {
// Rotate box3 around its local Y-axis each frame
// 每帧绕 box3 的本地 Y 轴旋转
box3.rotation.y += 0.01;
// Rotate box1 around world X-axis slowly
// 缓慢绕 box1 的世界 X 轴旋转
box1.rotate(BABYLON.Axis.X, 0.005, BABYLON.Space.WORLD);
});
// Using Quaternions (Alternative)
// 使用四元数(替代方法)
const box4 = BABYLON.MeshBuilder.CreateBox("box4", {size: 1}, scene);
box4.material = boxMat;
box4.position = new BABYLON.Vector3(3, 0.5, 3);
// Rotate 90 degrees around an arbitrary axis (e.g., normalized vector (1, 1, 0))
// 绕任意轴(例如,归一化向量 (1, 1, 0))旋转 90 度
const rotationAxis = new BABYLON.Vector3(1, 1, 0).normalize();
const angle = Math.PI / 2; // 90 degrees 90 度
box4.rotationQuaternion = BABYLON.Quaternion.RotationAxis(rotationAxis, angle);

用于旋转的关键方法:

  • mesh.rotation:获取或设置网格体的欧拉角旋转 Vector3 (以弧度为单位)。
  • mesh.rotation.x / .y / .z:访问单独的旋转分量 (弧度)。
  • BABYLON.Tools.ToRadians(degrees):将角度转换为弧度的辅助函数。
  • mesh.addRotation(x, y, z):将给定的弧度值添加到当前 mesh.rotation 分量中。
  • mesh.rotate(axis, angle, space):绕指定的 axis (BABYLON.Axis.X/Y/Z 或自定义 Vector3) 旋转网格体 angle 角度 (弧度)。space 可以是 BABYLON.Space.LOCAL 或 BABYLON.Space.WORLD。
  • mesh.rotationQuaternion:使用 BABYLON.Quaternion 获取或设置网格体的旋转。有助于避免万向锁并进行平滑插值。BABYLON.Quaternion 上有各种静态方法可以创建四元数(例如,RotationAxis,RotationYawPitchRoll)。

在使用轴可视化的同时,尝试更改示例中的位置和旋转值,以查看对象在 3D 场景中的放置和方向。