ThreeJS-3D教学十一:屏幕坐标和世界坐标转换

这篇具有很好参考价值的文章主要介绍了ThreeJS-3D教学十一:屏幕坐标和世界坐标转换。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

ThreeJS-3D教学十一:屏幕坐标和世界坐标转换,three,3d,javascript

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
  <style>
    body {
      width: 100%;
      height: 100%;
    }
    * {
      margin: 0;
      padding: 0;
    }
    .label {
      font-size: 20px;
      color: #000;
      font-weight: 700;
    }
    .circle {
      width: 20px;
      height: 20px;
      border-radius: 10px;
      position: absolute;
      left: 0;
      top: 0;
      background-color: red;
      z-index: 1000;
    }
    #container {
      position: relative;
      width: 100%;
      height: 100vh;
    }
  </style>
</head>
<body>
<div class="circle"></div>
<div id="container"></div>
<script type="importmap">
  {
    "imports": {
      "three": "../three-155/build/three.module.js",
      "three/addons/": "../three-155/examples/jsm/"
    }
  }
</script>
<script type="module">
import * as THREE from 'three';
import Stats from 'three/addons/libs/stats.module.js';
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
import { GPUStatsPanel } from 'three/addons/utils/GPUStatsPanel.js';
import { CSS2DRenderer, CSS2DObject } from 'three/addons/renderers/CSS2DRenderer.js';
let stats, labelRenderer, gpuPanel, temporaryKeep;
let camera, scene, renderer, controls, cubeBox;
const group = new THREE.Group();
let widthImg = 200;
let heightImg = 200;
const mouse = new THREE.Vector2();
init();
initHelp();
initLight();
axesHelperWord();
animate();
// 添加平面
addPlane();
// 用这个模型演示 世界坐标 转为 屏幕坐标
let point = new THREE.Vector3(-40, 20, 30);
let geometry = new THREE.BoxGeometry(2, 2, 2);
let material = new THREE.MeshLambertMaterial({color: 0xccc000});
let cube = new THREE.Mesh(geometry, material);
cube.position.x = point.x;
cube.position.y = point.y;
cube.position.z = point.z;
cube.name = 'BoxGeometry';
scene.add(cube);
// 这个演示 屏幕坐标转为世界坐标
let geometry2 = new THREE.BoxGeometry(2, 2, 2);
let material2 = new THREE.MeshLambertMaterial({color: 0x000000});
cubeBox= new THREE.Mesh(geometry2, material2);
cubeBox.name = 'BoxGeometry2';
scene.add(cubeBox);

/**
 * CylinderGeometry(radiusTop : Float, radiusBottom : Float, height : Float, radialSegments : Integer, heightSegments : Integer)
 radiusTop—顶部圆柱体的半径。默认值为1。
 radiusBottom—底部圆柱体的半径。默认值为1。
 height——圆柱体的高度。默认值为1。
 radialSegments—圆柱体圆周上的分段面数。默认值为32
 heightSegments—沿圆柱体高度的面行数。默认值为1。
 */
let geometry1 = new THREE.CylinderGeometry(15, 15, 10, 32, 1);
let material1 = new THREE.MeshLambertMaterial({color: 0xffff00});
let cylinder = new THREE.Mesh(geometry1, material1);
cylinder.position.set(30, 5, -50);
cylinder.name = 'CylinderGeometry';
scene.add(cylinder);

setTimeout(() => {
  point3DT2D();
}, 300);

function point3DT2D() {
  // 下面就是世界坐标转为屏幕坐标的代码
  let worldVector = new THREE.Vector3(point.x, point.y, point.z);
  // 世界坐标转标准设备坐标
  // 官方释义:将此矢量从世界空间投影到相机的标准化设备坐标(NDC)空间中。
  let standardVector = worldVector.project(camera); 
  // canvas画布的宽高尺寸
  let container = document.querySelector('#container');
  if (!container) return;
  let cavWidth = container.offsetWidth / 2;
  let cavHeight = container.offsetHeight / 2;
  console.log(standardVector);
  let x = Math.round(standardVector.x * cavWidth + cavWidth); // 设备坐标转屏幕坐标
  let y = Math.round(-standardVector.y * cavHeight + cavHeight); // 设备坐标转屏幕坐标
  /**
   * 更新立方体元素位置
   */
  console.log(x);
  console.log(y);
  let box = document.querySelector('.circle');
  box.style.left = x + 'px';
  box.style.top = y + 'px';
}

function onDocumentMouseMove(event) {

  event.preventDefault();

  // 将鼠标点击位置的屏幕坐标转成threejs中的标准坐标,具体解释见代码释义 如果 canvas有左边距 和 上边距 需 要减去
  mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
  mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
  // 新建一个三维单位向量 假设z方向就是1 或者 0.5,这个为什么是这样,有知道详情的还请赐教
  // 官方释义:将此矢量从相机的标准化设备坐标(NDC)空间投影到世界空间中。
  const vector = new THREE.Vector3(mouse.x, mouse.y, 0.5).unproject(camera);
  // 将获取的坐标设置为模型的位置
  cubeBox.position.x = vector.x;
  cubeBox.position.y = vector.y;
  cubeBox.position.z = vector.z;
}

function addPlane() {
  // 创建一个平面 PlaneGeometry(width, height, widthSegments, heightSegments)
  const planeGeometry = new THREE.PlaneGeometry(widthImg, heightImg, 1, 1);
  // 创建 Lambert 材质:会对场景中的光源作出反应,但表现为暗淡,而不光亮。
  const planeMaterial = new THREE.MeshPhongMaterial({
    color: 0xb2d3e6,
    side: THREE.DoubleSide
  });
  const plane = new THREE.Mesh(planeGeometry, planeMaterial);
  // 以自身中心为旋转轴,绕 x 轴顺时针旋转 45 度
  plane.rotation.x = -0.5 * Math.PI;
  plane.position.set(0, -4, 0);
  scene.add(plane);
}

function init() {

  camera = new THREE.PerspectiveCamera( 70, window.innerWidth / window.innerHeight, 10, 2000 );
  camera.up.set(0, 1, 0);
  camera.position.set(60, 40, 60);
  camera.lookAt(0, 0, 0);

  scene = new THREE.Scene();
  scene.background = new THREE.Color( '#ccc' );

  renderer = new THREE.WebGLRenderer( { antialias: true } );
  renderer.setPixelRatio( window.devicePixelRatio );
  renderer.setSize( window.innerWidth, window.innerHeight );
  document.getElementById( 'container' ).appendChild( renderer.domElement );

  labelRenderer = new CSS2DRenderer();
  labelRenderer.setSize( window.innerWidth, window.innerHeight );
  labelRenderer.domElement.style.position = 'absolute';
  labelRenderer.domElement.style.top = '0px';
  labelRenderer.domElement.style.left = '0px';
  labelRenderer.domElement.style.pointerEvents = 'none';
  document.getElementById( 'container' ).appendChild( labelRenderer.domElement );

  controls = new OrbitControls( camera, renderer.domElement );
  controls.mouseButtons = {
    LEFT: THREE.MOUSE.PAN,
    MIDDLE: THREE.MOUSE.DOLLY,
    RIGHT: THREE.MOUSE.ROTATE
  };
  controls.enablePan = true;
  // 设置最大最小视距
  controls.minDistance = 20;
  controls.maxDistance = 1000;

  window.addEventListener( 'resize', onWindowResize );

  stats = new Stats();
  stats.setMode(1); // 0: fps, 1: ms
  document.body.appendChild( stats.dom );

  gpuPanel = new GPUStatsPanel( renderer.getContext() );
  stats.addPanel( gpuPanel );
  stats.showPanel( 0 );

  scene.add( group );

  document.addEventListener('click', onDocumentMouseMove, false);
}

function initLight() {
  const light = new THREE.DirectionalLight(new THREE.Color('rgb(253,253,253)'));
  light.position.set(100, 100, -10);
  light.intensity = 3; // 光线强度
  light.castShadow = true; // 是否有阴影
  light.shadow.mapSize.width = 2048; // 阴影像素
  light.shadow.mapSize.height = 2048;
  // 阴影范围
  const d = 80;
  light.shadow.camera.left = -d;
  light.shadow.camera.right = d;
  light.shadow.camera.top = d;
  light.shadow.camera.bottom = -d;
  light.shadow.bias = -0.0005; // 解决条纹阴影的出现
  // 最大可视距和最小可视距
  light.shadow.camera.near = 0.01;
  light.shadow.camera.far = 2000;
  const AmbientLight = new THREE.AmbientLight(new THREE.Color('rgb(255, 255, 255)'));
  scene.add( light );
  scene.add( AmbientLight );
}

function initHelp() {
  // const size = 100;
  // const divisions = 5;
  // const gridHelper = new THREE.GridHelper( size, divisions );
  // scene.add( gridHelper );

  // The X axis is red. The Y axis is green. The Z axis is blue.
  const axesHelper = new THREE.AxesHelper( 100 );
  scene.add( axesHelper );
}

function axesHelperWord() {
  let xP = addWord('X轴');
  let yP = addWord('Y轴');
  let zP = addWord('Z轴');
  xP.position.set(50, 0, 0);
  yP.position.set(0, 50, 0);
  zP.position.set(0, 0, 50);
}

function addWord(word) {
  let name = `<span>${word}</span>`;
  let moonDiv = document.createElement( 'div' );
  moonDiv.className = 'label';
  // moonDiv.textContent = 'Moon';
  // moonDiv.style.marginTop = '-1em';
  moonDiv.innerHTML = name;
  const label = new CSS2DObject( moonDiv );
  group.add( label );
  return label;
}

function onWindowResize() {
  camera.aspect = window.innerWidth / window.innerHeight;
  camera.updateProjectionMatrix();
  renderer.setSize( window.innerWidth, window.innerHeight );
}

function animate() {
  requestAnimationFrame( animate );

  stats.update();
  controls.update();
  labelRenderer.render( scene, camera );
  renderer.render( scene, camera );
}
</script>
</body>
</html>

直接复制上面的代码,将three资源路径修改后,启动即可看到效果
上面是代码实现,以下我们看下一些理论知识点:

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

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

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

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

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

原本世界坐标转换到观察空间坐标需要乘上视图矩阵 CameraMatrixWorldInverse(ViewMatrix)
随后,观察空间坐标转换到裁剪空间坐标需要乘上相机投影矩阵:ProjectMatrix
在 ThreeJS 中有一个方法 Vector3.project(camera) 综合了这两步:

// 这是之前的版本
project( camera ) {
    return this.applyMatrix4( camera.matrixWorldInverse ).applyMatrix4( camera.projectionMatrix );
}

最新的是
ThreeJS-3D教学十一:屏幕坐标和世界坐标转换,three,3d,javascript文章来源地址https://www.toymoban.com/news/detail-797859.html

到了这里,关于ThreeJS-3D教学十一:屏幕坐标和世界坐标转换的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Cocos Creator 世界坐标转屏幕坐标

    Cocos creator某一坐标转屏幕坐标(screen position),以前都是屏幕坐标转世界坐标。 先上代码为敬(只是提供一种思路,有更好的实现和建议欢迎留言) 以上是获取屏幕坐标的代码,代码简陋只提供一种思路,不同机型测试显示的内容都会正确覆盖在panel上。

    2024年02月14日
    浏览(43)
  • ThreeJS-3D教学五-材质

    我们在ThreeJS-3D教学二:基础形状展示中有简单介绍过一些常用的材质,这次我们举例来具体看下效果: 代码是这样的: 图片我依次放进来,方便大家本地看效果 stone.jpg stone-bump.jpg normal2.jpg normal1.jpg earth.jpg earthSpec.png 具体的注释也都放代码里了!感觉不错的点个赞,光白嫖

    2024年02月07日
    浏览(45)
  • ThreeJS-3D教学七-交互

    在threejs中想要选中一个物体,点击或者鼠标悬浮,又或者移动端的touch事件,核心都是通过new THREE.Raycaster完成的。这里用到了一个概念,即我们点击时的 屏幕坐标 转换为 three中的3D坐标。 先看效果图: 代码是: 具体思路代码中都有注释,实在不懂也没关系,函数一封装先

    2024年02月08日
    浏览(43)
  • ThreeJS-3D教学一:基础场景创建

    Three.js 是一个开源的 JS 3D 图形库,用于创建和展示高性能、交互式的 3D 图形场景。它建立在 WebGL 技术之上,并提供了丰富的功能和工具,使开发者可以轻松地构建令人惊叹的 3D 可视化效果。 Three.js 提供了一套完整的工具和 API,用于创建和管理 3D 场景、几何体、纹理、光照

    2024年02月07日
    浏览(56)
  • ThreeJS-3D教学六-物体位移旋转

    之前文章其实也有涉及到这方面的内容,比如在ThreeJS-3D教学三:平移缩放+物体沿轨迹运动这篇中,通过获取轨迹点物体动起来,其它几篇文章也有旋转的效果,本篇我们来详细看下,另外加了tween.js知识点,tween可以很好的协助 three 做动画,与之相似的还有 gsap.js方法类似。

    2024年02月04日
    浏览(49)
  • 【js&threeJS】入门three,并实现3D汽车展示厅,附带全码

    首先放个最终效果图:   三维(3D)概念: 三维(3D)是一个描述物体在三个空间坐标轴上的位置和形态的概念。相比于二维(2D)只有长度和宽度的平面,三维增加了高度或深度这一维度 在三维空间中,我们使用三个独立的坐标轴来描述物体的位置。通常使用笛卡尔坐标系

    2024年02月11日
    浏览(130)
  • ThreeJS-3D教学八-OBJLoader模型加载+动画

    先看效果图: 本篇给大家介绍下模型加载的知识,用到了OBJLoader对应的模型,为了增加趣味性,花了些时间,利用new THREE.Points获取到模型上的点,做了一个动画效果,其实就是操作 Y轴上的点,先降低上0,然后再还原,代码如下: 如果有同学从我第一篇文章开始学到现在,

    2024年04月24日
    浏览(41)
  • ThreeJS-3D教学十-有宽度的line

    webgl中线是没有宽度的,现实的应用中一般做法都是将线拓宽成面来绘制。默认threejs的线宽是无法调节的,需要用有厚度的线 THREE.Line2。 先看效果图: 看下代码:

    2024年02月07日
    浏览(41)
  • Three.js--》前端开发者掌握3d技术不再是梦,初识threejs

            这十年来 web 得到了快速的发展,随着 webgl 的普及,网页的表现能力越来越强大,网页上已经开始可以做出很多复杂的动画和精美的效果,还可以通过 webgl 在网页中绘制高性能的3d图形,别的不说,凡是入门程序员都离不开github这个网站,细心的人都会发现,gi

    2024年02月01日
    浏览(66)
  • ThreeJS-3D教学三:平移缩放+物体沿轨迹运动

    我们在项目中会有一些这样的需求,我们可视化一个场景,需要俯视、平移、缩放,方便观察场景中的数据或者模型,之所以把这个案例拿出来 1、这是个很实用的需求,我相信很多人会用到 2、我自己认为在实际案例中我们可以学习相关知识点更易吸收些 为了丰富本篇文章

    2024年02月03日
    浏览(52)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包