• three.js世界坐标系和设备坐标系


    简述

    物体的坐标转换过程大致为:局部坐标 -> 世界坐标 -> 观察空间坐标 -> 裁剪空间坐标 -> 屏幕空间坐标

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

    因此坐标转换过程就变成了:局部坐标 -> 世界坐标 -> 标准设备坐标 -> 屏幕空间坐标

    屏幕坐标转世界坐标

    ThreeJS 是使用了 canvas 画布绘制图形的,因此屏幕坐标系就是 canvas 中的坐标系,也就是左上角是坐标原点:

    在 ThreeJS 中,一个物体可看作一个 Mesh,Mesh 的坐标是用一个 Vector3 来表示的,Vector3 中包含了 x、y、z 坐标。

    空间坐标系是三维的,其原点默认在屏幕中心,且 x y z 的范围是 [-1,1],因此其 x、y 轴在屏幕坐标系中的表示就是
      
    在这里插入图片描述
    通过Vector3对象的方法project,方法的参数是相机对象,语句worldVector.project(camera);返回的结果是世界坐标worldVector在camera相机对象矩阵变化下对应的标准设备坐标, 标准设备坐标xyz的范围是[-1,1]。

    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

    公式推导过程如下:

    在这里插入图片描述

    世界坐标转屏幕坐标

    屏幕坐标转空间坐标需要经过两个步骤:屏幕坐标 -> 标准设备坐标 -> 世界坐标。
     通过 Vector3对象的方法 project(camera),返回的结果是世界坐标 worldVector在 camera相机对象矩阵变化下对应的标准设备坐标, 标准设备坐标 xyz 的范围是[-1,1]。

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

    从标准设备坐标系转换到屏幕坐标系与我们前面计算出的公式相反:
    在这里插入图片描述

    首先计算出屏幕坐标系中心:

    const centerX = window.innerWidth / 2;
    const centerY = window.innerHeight / 2;
    
    • 1
    • 2

    计算出的 centerX 和 centerY 同时也表示了坐标轴的一半大小。

    然后,将设备坐标系使用 project 方法转换到标准设备坐标系,再转换到屏幕坐标系中:

    const standardVec = worldVector.project(camera);
    
    const screenX = Math.round(centerX * standardVec.x + centerX);
    const screenY = Math.round(-centerY * standardVec.y + centerY);
    
    • 1
    • 2
    • 3
    • 4

    在这里插入图片描述

    第三方 CSS2DRenderer

     /**
        * 创建div元素(作为立方体标签)
        */
        var div = document.createElement('div');
        div.innerHTML = '立方体';
        div.style.padding = '5px 10px';
        div.style.color = '#fff';
        div.style.fontSize = '16px';
        div.style.position = 'absolute';
        div.style.backgroundColor = 'rgba(25,25,25,0.5)';
        div.style.borderRadius = '5px'
        // document.body.appendChild(div);
    
        // 获得HTML元素创建的UI界面
        var tag = document.getElementById('tag');
    
        //div元素包装为CSS2模型对象CSS2DObject,并插入场景中
        var label = new CSS2DObject(div);
        label.position.copy(boxMesh.position);
        // label.position.y += 30
        scene.add(label); //CSS2模型标签插入到场景中
    
        // 创建一个CSS2渲染器CSS2DRenderer
        var labelRenderer = new CSS2DRenderer();
        labelRenderer.setSize(window.innerWidth, window.innerHeight);
        labelRenderer.domElement.style.position = 'absolute';
        // 避免renderer.domElement影响HTMl标签定位,设置top为0px
        labelRenderer.domElement.style.top = '0px';
        labelRenderer.domElement.style.left = '0px';
        //设置.pointerEvents=none,以免模型标签HTML元素遮挡鼠标选择场景模型
        labelRenderer.domElement.style.pointerEvents = 'none';
        document.body.appendChild(labelRenderer.domElement);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    第三方 CSS3DRenderer

     /**
        * 创建div元素(作为立方体标签)
        */
        var div = document.createElement('div');
        div.innerHTML = '立方体';
        div.style.padding = '5px 10px';
        div.style.color = '#fff';
        div.style.fontSize = '16px';
        div.style.position = 'absolute';
        div.style.backgroundColor = 'rgba(25,25,25,0.5)';
        div.style.borderRadius = '5px'
        // document.body.appendChild(div);
    
        // 获得HTML元素创建的UI界面
        // var tag = document.getElementById('tag');
    
        //div元素包装为CSS3模型对象CSS3DObject,并插入场景中
        var label = new CSS3DObject(div);
        div.style.pointerEvents = 'none';//避免HTML标签遮挡三维场景的鼠标事件
        label.position.copy(boxMesh.position);
        //缩放CSS3DObject模型对象
        label.scale.set(0.5,0.5,0.5)
        label.position.y += 20
        scene.add(label); //CSS3模型标签插入到场景中
    
        // 创建一个CSS3渲染器CSS3DRenderer
        var labelRenderer = new CSS3DRenderer();
        labelRenderer.setSize(window.innerWidth, window.innerHeight);
        labelRenderer.domElement.style.position = 'absolute';
        // 避免renderer.domElement影响HTMl标签定位,设置top为0px
        labelRenderer.domElement.style.top = '0px';
        labelRenderer.domElement.style.left = '0px';
        //设置.pointerEvents=none,以免模型标签HTML元素遮挡鼠标选择场景模型
        labelRenderer.domElement.style.pointerEvents = 'none';
        document.body.appendChild(labelRenderer.domElement);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
  • 相关阅读:
    2022年全球一次能源消费量:石油消耗量持续增加达190.69百亿亿焦耳,亚太地区消费量居首位[图]
    【Unity3D】调整屏幕亮度、饱和度、对比度
    美国空军发布类ChatGPT产品—NIPRGPT
    软考高级架构师下篇-16通信系统架构设计理论与实践
    电池供电遥测终端RTU 遥测终端机 低功耗遥测采集终端 智能远传 防水IP68
    IDEA clion + vim =neovim
    flink程序在消费kafka数据时出现Error sending fetch request问题
    大数据毕设选题 - 生成对抗网络的照片上色动态算法设计与实现(深度学习 opencv python)
    Dialog show的源码分析
    R语言使用dietaryindex包计算NHANES数据多种健康饮食指数 (HEI等)(1)
  • 原文地址:https://blog.csdn.net/weixin_42910765/article/details/125616838