向量、位置与旋转
BabylonJS - 网格体的定位与旋转
Section titled “BabylonJS - 网格体的定位与旋转”理解如何在 3D 空间中定位和调整对象的方向是至关重要的。BabylonJS 使用右手坐标系。默认情况下:
- **X 轴:**指向右侧。
- **Y 轴:**指向上方。
- **Z 轴:**指向屏幕外,朝向你。
我们将使用 BABYLON.Vector3 来表示位置和旋转(以欧拉角 Euler angles 表示)。
以下代码包含一个辅助函数 showAxis,它绘制 X、Y 和 Z 轴的线条和标签,使可视化坐标系和对象放置位置变得更容易。
轴可视化示例
Section titled “轴可视化示例”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)。
设置网格体位置
Section titled “设置网格体位置”每个网格体都有一个 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:分配一个新的 Vector3const 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 移动 sphere2const 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(相对于世界坐标系)。
设置网格体旋转
Section titled “设置网格体旋转”旋转通常使用欧拉角(绕 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 场景中的放置和方向。