ThreeJS 坐标系转化

本文详细介绍了ThreeJS中屏幕坐标系、世界坐标系和标准化设备坐标系的概念,并阐述了它们之间的转换关系,包括屏幕坐标转世界坐标和世界坐标转屏幕坐标的步骤及公式,提供了具体的转换函数示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

这一章主要讲的是屏幕坐标和世界坐标之间的数据转换,在THreeJS中还是非常实用的...

目录

一、什么是屏幕坐标系?

二、什么是世界坐标系?

三、什么是标准化设备坐标系?

四、三个坐标系之间的关系

五、屏幕坐标转世界坐标

六、世界坐标转屏幕坐标 


一、什么是屏幕坐标系?

        ThreeJS使用了canvas画布绘制图形,所以屏幕坐标系就是canvas中的坐标系。

屏幕坐标系的起始点在左上角(0,0) 


二、什么是世界坐标系?

世界坐标系(三维坐标系)分为两种,左手坐标系和右手坐标系。

  • 左手坐标系:BabylonJS
  • 右手坐标系:ThreeJS(下图就是右手坐标系

 


三、什么是标准化设备坐标系?

        一旦你的顶点坐标已经在顶点着色器中处理过,它们就应该是标准化设备坐标(Normalized Device Coordinates,NDC)了,标准化设备坐标是一个x、y和z值在-1.0到1.0的一小段空间。任何落在范围外的坐标都会被丢弃/裁剪,不会显示在你的屏幕上。下面你会看到我们定义的在标准化设备坐标中的三角形(忽略z轴):

 与通常的屏幕坐标不同,y轴正方向为向上,(0, 0)坐标是这个图像的中心,而不是左上角。最终你希望所有(变换过的)坐标都在这个坐标空间中,否则它们就不可见了。


四、三个坐标系之间的关系

        将二维坐标映射到三维空间时,首先需要将二维坐标转换为 NDC 坐标。这可以通过将二维坐标的值除以视口的尺寸来实现。

        一旦获得了 NDC 坐标,可以使用其他投影技术(如透视投影或正交投影)将其转换为三维坐标。

物体坐标系之间的转换关系大致为:

        局部坐标 -> 世界坐标 -> 观察空间坐标 -> 裁剪空间坐标 -> 屏幕空间坐标

        我们将 观察空间坐标系 和 裁剪空间坐标系 之间的转换统一处理,最终得到 标准设备坐标系     

因此坐标转换过程就变成了:

        局部坐标 -> 世界坐标 -> 标准设备坐标 -> 屏幕空间坐标

原本世界坐标转换到观察空间坐标需要乘上视图矩阵 CameraMatrixWorldInverse(ViewMatrix)

  随后,观察空间坐标转换到裁剪空间坐标需要乘上相机投影矩阵:ProjectMatrix

  在 ThreeJS 中有一个方法 Vector3.project(camera) 综合了这两步:
 

project( camera ) {
    return this.applyMatrix4( camera.matrixWorldInverse ).applyMatrix4( camera.projectionMatrix );
}

   


五、屏幕坐标转世界坐标

        流程: 屏幕空间坐标 -> 标准设备坐标 -> 世界坐标 

        在ThreeJS 中,画布一般是全屏的,因此画布的宽高 w,h 就是:window.innerWidth 和

window.innerHeight,所以 Three 的空间坐标系中点 (cx, cy)在屏幕坐标系中就是:(w / 2,h / 2)。

        假设 canvas 中有一点 (x,y),这个点在空间坐标系中为 (x1,y1),那么这个转换公式是

x1=(x/w)∗2−1

y1=−(y/h)∗2+1

公式推导过程如下

        1.在屏幕坐标系中,通常将原点放在左上角,x轴向右延伸,y轴向下延伸。这种坐标系的定义方式决定了对于一个点(x, y),其表示方式是x为横向偏移量,y为纵向偏移量。

        2.当应用原点(cx, cy)后,我们需要计算相对于新的原点的表示方式。对于x'来说,它表示相对于新的原点cx的横向偏移量。因此,我们从原始的x值中减去cx,得到相对于新原点的表示,即x' = x - cx。

        3.同样地,对于y'来说,它表示相对于新的原点cy的纵向偏移量。由于屏幕坐标系中y轴方向与常规的数学坐标系相反,我们需要通过计算cy减去原始的y值,得到相对于新原点的表示,即y' = cy - y。

export function twoConversionThreePlus(event, mesh, raycaster, camera) {
    //raycaster 是new THREE.Raycaster();
    //mesh 是网格,可以是scene
    //camera 是当前的活动相机
    const mouse = new THREE.Vector2(
        (event.clientX / window.innerWidth) * 2 - 1,
        -(event.clientY / window.innerHeight) * 2 + 1
    );

    raycaster.setFromCamera(mouse, camera);
    const intersects = raycaster.intersectObject(mesh, true);
    if (intersects.length <= 0) return void 0;
    const selected = intersects[0].object;

    console.log(intersects[0].point, "===", selected);

    return intersects[0].point;
}

//当前函数将返回mesh上点的三维坐标(由鼠标指针指向的点)


六、世界坐标转屏幕坐标 

        流程:世界坐标 -> 标准设备坐标 -> 屏幕空间坐标

        通过 Vector3对象的方法 project(camera),返回的结果是世界坐标 worldVector在 camera相机对象矩阵变化下对应的标准设备坐标, 标准设备坐标 xyz 的范围是[-1,1]

        同样的,假设画布宽为 w ,高为 h,屏幕坐标系中的一点为 (x, y),标准设备坐标系中对应的点为 (x1, y1)

        从标准设备坐标系转换到屏幕坐标系与我们前面计算出的公式相反:

x = x1 * w/2 + w/2

y = y1 * h/2 + h/2

export function threeConversionTwo(model, camera) {
    // 这里判断model是因为model可能是被选中的mesh的position,也可能是点击到了环境贴图
    if (model !== undefined) {
        // 世界坐标转标准设备坐标
        const worldVectorsss = new THREE.Vector3(model.x, model.y, model.z)
        const stdVectorsss = worldVectorsss.project(camera);
        console.log(stdVectorsss)

        const a = window.innerWidth / 2;
        const b = window.innerHeight / 2;
        //标准设备坐标转屏幕坐标x,y
        const x = Math.round(stdVectorsss.x * a + a);
        const y = Math.round(-stdVectorsss.y * b + b);

        console.log(x, y)
    }
    return
}

### Three.js 三维坐标系解析 #### 局部坐标系与世界坐标系Three.js 中,存在两种主要的坐标系:局部坐标系和世界坐标系。每个对象都有自己的局部坐标系,在这个体系下定义的位置、旋转以及缩放属性都是相对于该对象自身的中心点而言的[^1]。 对于场景中的任何几何体来说,当对其进行变换(平移、旋转或缩放)时,实际上是在修改它在自己局部坐标系下的参数。然而,为了能够正确显示在整个虚拟世界的环境中,还需要将其转换到全局统一的世界坐标系中去。这种从局部向全球转变的过程涉及到一系列复杂的矩阵运算,包括但不限于模型视图矩阵(ModelView Matrix) 和投影矩阵(Projection Matrix)。 #### 坐标轴方向及其作用 Three.js 使用的是右手定则笛卡尔直角坐标系统,这意味着 X 轴指向右侧,Y 轴指向上方,Z 轴则是朝外的方向——即正对着观察者的一面被认为是正面(+Z),背离观察者的那一面被认作背面(-Z)。 这样的设定不仅有助于开发者更直观地理解和控制物体的空间位置关系,同时也方便了后续涉及光照计算、摄像机视角调整等方面的工作。例如,通过改变某个物体沿 Z 轴上的位移可以轻松实现景深效果;利用绕 Y 轴转动的方式可以让角色面向不同的方位等操作都变得简单明了。 ```javascript // 创建一个位于 (0, 0, 0) 的立方体并设置其大小为宽高深各 1 单位 const geometry = new THREE.BoxGeometry(); const material = new THREE.MeshBasicMaterial({ color: 0x00ff00 }); const cube = new THREE.Mesh(geometry, material); scene.add(cube); // 将立方体沿着 z 方向往前移动一些距离使其可见 cube.position.z = -5; ``` #### 三维至二维坐标的映射 值得注意的是,虽然 Three.js 主要处理的是三维数据结构,但在最终呈现给用户的画面上却是以二维形式展现出来的。因此,有一个重要的环节就是如何把三维空间内的坐标准确无误地投射到 Canvas 平面上完成可视化展示。这通常借助于透视投影(Perspective Projection) 或 正交投影(Orthographic Projection) 来达成目的[^2]。 具体来讲,无论是哪种类型的相机(Camera),都会先将所有顶点按照一定的规则转化为标准化设备坐标(Normalized Device Coordinates,NDCs),然后再进一步经过裁剪(Clipping)等一系列步骤之后才真正形成屏幕上所看到的画面内容。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值