ThreeJs 入门05-坐标系的秘密(世界坐标系、本地坐标系)

8,808 阅读4分钟

「这是我参与2022首次更文挑战的第8天,活动详情查看:2022首次更文挑战

示例代码采用three.js-r73版本: cdnjs.cloudflare.com/ajax/libs/t…

我们现在可以在场景中画出一条线和基本的三角形,下面会使用直线画一个网格出来,为了更好的理解这个网格在空间中的位置,我们需要先了解一下空间坐标系。

坐标系

坐标系如下图,向上为Y正轴,向右为X正轴,向屏幕从里到外方向为Z正轴。 image.png

左手坐标系和右手坐标系

  • 三维坐标系分为两种,左手坐标系和右手坐标系。下面两张图都可以表示左右手坐标系。

image.pngimage.png

  • Threejs使用的是右手坐标系,这源于opengl默认情况下,也是右手坐标系。x轴正方向向右,y轴正方向向上,z轴由屏幕从里向外。

使用three.js-editor编辑器看坐标系

  • three.js源码地址:github.com/mrdoob/thre…
  • 在three.js源码中,有一个编辑器,就是editor目录,在浏览器打开index.html页面,大概长这个样子:

image.png

  • 然后我们点击左上角add,添加一个box

image.png image.png

  • 我们可以看到,红轴为X轴,绿轴为Y轴,蓝轴为Z轴,箭头指向为正方向。

关于围绕某个轴旋转

  • 还记得我们之前做过的物体旋转动画吗
  • 拇指指向旋转轴的正方向,四指弯曲的方向为旋转的正方向。

image.png

世界坐标和本地坐标

  • 一个应用程序可能包含成百上千个单独的对象,我们必须把它们放到一个公共的场景里。
  • 公共的场景就是世界坐标
  • 相对于物体来说,物体本身的坐标就是本地坐标
  • 相机默认在世界坐标系的原点

世界坐标

  • 比如我们在threejs编辑器中,添加两个盒子,这两个盒子在场景中都有自己的position
  • 场景就是世界坐标
  • 两个盒子的位置是世界坐标系下的位置

image.png

本地坐标

  • 还是上面那张图,我们每个盒子都有自己的坐标系,自己的坐标系就是本地坐标系。

场景中添加辅助坐标

  • 我们可以给我们的场景添加辅助坐标
    • 用于简单模拟3个坐标轴的对象.
    • 红色代表 X 轴. 绿色代表 Y 轴. 蓝色代表 Z 轴.
new THREE.AxisHelper() // 最新版重命名 AxesHelper
//  定义辅助坐标
var axisHelper
function initAxisHelper() {
    axisHelper = new THREE.AxisHelper(4) //
    scene.add(axisHelper)
}
  • 相机的位置:
camera.position.x = 0;
camera.position.y = 0;
camera.position.z = 5;
  • 添加之后的效果:

image.png

  • 这个时候我们看不到我们的Z轴,因为我们的摄像机是正对着物体(就像正在观看电脑屏幕的你),如何才能看到Z轴呢,我们需要让摄像机偏移一点,让相机向右移动一些距离
camera.position.x = 5;
camera.position.y = 0;
camera.position.z = 5;
  • 此时我们就可以看到x,y,z轴了

image.png

辅助坐标轴进行旋转

我们可以让我们的辅助坐标旋转,然后相对于物体是静止的

function render() {
    renderer.clear();
    cube.rotation.y += 0.01;
    axisHelper.rotation.y += 0.01;
    renderer.render(scene, camera);
    requestAnimationFrame(render);
}

2022-01-22 16-25-48.2022-01-22 16_26_13.gif

Object3D添加辅助坐标和物体为子类

  • 目前的辅助坐标是场景的,并不是物体的,通过相对静止的方法才使得辅助坐标作用在物体上,当关闭旋转就不是了
  • 我们可以把辅助坐标和物体组合到一个3D对象上-Object3D,
new THREE.Object3D()
  • 这是Three.js中大部分对象的基类,提供了一系列的属性和方法来对三维空间中的物体进行操纵。
  • 请注意,可以通过**.add( object )**方法来将对象进行组合,该方法将对象添加为子对象,但为此最好使用Group(来作为父对象)。

修改原来的代码

  • 辅助坐标不再添加到场景中
var cube;
function initObject() {
    ....
    cube = new THREE.Mesh(geometry, material);
    // scene.add(cube);
}

//  定义辅助坐标
var axisHelper
function initAxisHelper() {
    axisHelper = new THREE.AxisHelper(4) //
    // scene.add(axisHelper)
}

  • 添加initObjectTotal方法,将物体和辅助坐标添加到objectTotal
// 初始化一个三维物体,并添加子物体和辅助坐标
var objectTotal
function initObjectTotal() {
    objectTotal = new THREE.Object3D()
    objectTotal.add(cube)
    objectTotal.add(axisHelper)
    scene.add(objectTotal)
}
  • render函数中旋转objectTotal就可以让子物体一起旋转
function render() {
    renderer.clear();
    objectTotal.rotation.y += 0.01;
    renderer.render(scene, camera);
    requestAnimationFrame(render);
}

function threeStart() {
    initThree();
    initCamera();
    initScene();
    initObject();
    initAxisHelper();
    initObjectTotal()
    initLight();
    render();
}

codepen 代码示例