【问题标题】:Three.js, unexpected position shift when scaling object三.js,缩放对象时意外的位置偏移
【发布时间】:2020-02-18 03:18:03
【问题描述】:

我正在尝试创建一个缩放框,到目前为止,我设法将光标位置从语言环境转换为世界坐标,并使用正确的 uvs 在光标周围创建一个框对象。

这是我的尝试:https://jsfiddle.net/2ynfedvk/2/

在不缩放的情况下,框完全以光标为中心,但如果您切换缩放复选框以设置比例zoomMesh.scale.set(1.5, 1.5, 1),则框位置会随着您将光标从场景中心移动得越远。

我是否弄乱了诸如“转换原点”之类的 CSS,以便让 three.js 以对象周围的比例居中,这是获得这种缩放效果的正确方法吗?

我是一般的 three.js 和 3d 的新手,所以感谢您的帮助。

【问题讨论】:

    标签: three.js position scale


    【解决方案1】:

    当您使用 1.5 缩放网格时,这意味着应用缩放顶点值的变换矩阵。

    问题来自于顶点的变化。顶点位于网格的局部空间中。而当你将正方形的左上顶点设置为[10, 10, 0],然后将.scale.set(1.5, 1.5, 1)应用于网格,那么顶点的坐标就变成了[15, 15, 0]。对所有其他 3 个顶点相同。这就是为什么正方形的中心在图片中心到鼠标指针的1.5倍处不匹配的原因。

    因此,一个选项不是缩放网格,而是更改正方形的大小。

    我稍微改变了你的小提琴,所以也许它会更解释:

    const
      [width, height] = [500, 300],
      canvas = document.querySelector('canvas'),
      scaleCheckBox = document.querySelector('input')
    ;
    
    console.log(scaleCheckBox)
    
    canvas.width = width;
    canvas.height = height;
    
    const
      scene = new THREE.Scene(),
      renderer = new THREE.WebGLRenderer({canvas}),
      camDistance = 5,
      camFov = (2 * Math.atan( height / ( 2 * camDistance ) ) * ( 180 / Math.PI )),
      camera = new THREE.PerspectiveCamera(camFov, width/height, 0.1, 1000 )
    ;
    
    camera.position.z = camDistance;
    
    const
      texture = new THREE.TextureLoader().load( "https://picsum.photos/500/300" ),
      imageMaterial = new THREE.MeshBasicMaterial( { map: texture , side : 0 } )
    ;
    
    texture.minFilter = THREE.LinearFilter;
    texture.magFilter = THREE.LinearFilter;
    texture.format = THREE.RGBFormat;
    
    const
      planeGeometry = new THREE.PlaneGeometry( width, height ),
      planeMesh = new THREE.Mesh( planeGeometry, imageMaterial )
    ;
    
    const
      zoomGeometry = new THREE.BufferGeometry(),
      zoomMaterial = new THREE.MeshBasicMaterial( { map: texture , side : 0 } ),
      zoomMesh = new THREE.Mesh( zoomGeometry, zoomMaterial )
    ;
    
    zoomMaterial.color.set(0xff0000);
    
    zoomGeometry.setAttribute('position', new THREE.BufferAttribute(new Float32Array([
      0, 0, 0,
      0, 0, 0,
      0, 0, 0,
      0, 0, 0
    ]), 3));
    
    zoomGeometry.setIndex([
      0, 1, 2,
      2, 1, 3
    ]);
    
    scene.add( planeMesh );
    scene.add( zoomMesh );
    
    var zoom = 1.;
    
    function setZoomBox(e){
    
      const
        size = 50 * zoom, 
        x = e.clientX - (size/2),
        y = -(e.clientY - height) - (size/2),
        coords = [
          x,
          y,
          x + size,
          y + size
        ]
      ;
      
      const [x1, y1, x2, y2] = [
        coords[0] - (width/2),
        coords[1] - (height/2),
        coords[2] - (width/2),
        coords[3] - (height/2)
      ];
      
      zoomGeometry.setAttribute('position', new THREE.BufferAttribute(new Float32Array([
        x1, y1, 0,
        x2, y1, 0,
        x1, y2, 0,
        x2, y2, 0
      ]), 3));
      
      const [u1, v1, u2, v2] = [
        coords[0]/width,
        coords[1]/height,
        coords[2]/width,
        coords[3]/height
      ]
      
      zoomGeometry.setAttribute('uv',
      new THREE.BufferAttribute(new Float32Array([
        u1, v1,
        u2, v1,
        u1, v2,
      
        u2, v2,
        u1, v1,
        u1, v2
      ]), 2));
    
    }
    
    function setScale(e){
      //zoomMesh.scale.set(...(scaleCheckBox.checked ? [1.5, 1.5, 1] : [1, 1, 1]));
      zoom = scaleCheckBox.checked ? 1.5 : 1 ;
      
    }
    
    function render(){
    
      renderer.render(scene, camera);
    
      requestAnimationFrame(render);
    }
    
    render();
    
    canvas.addEventListener('mousemove', setZoomBox);
    scaleCheckBox.addEventListener('change', setScale);
    html, body {
        margin: 0;
        height: 100%;
    }
    
    body{
        background: #333;
        color: #FFF;
        font: bold 16px arial;
    }
    
    canvas{
        
    }
    <script src="https://threejs.org/build/three.min.js"></script>
    
    <canvas></canvas>
    <div>Toggle scale <input type="checkbox" /></div>

    【讨论】:

      【解决方案2】:

      感谢您的回答,不是我想要的(不仅调整正方形的大小,还放大图像),但您指出了正确的方向。

      就像你说的位置坐标随着比例移动,所以我必须重新计算相对于比例的新位置。

      添加了这些新行,带有新的 scaleoffset 变量:

      if(scaleCheckBox.checked){
          const offset = scale - 1;
          zoomMesh.position.set(
              -(x1 * offset) - (size*scale)/2) -(size/2),
              -((y1 * offset) + (size*scale)/2) -(size/2)),
              0
          );
      }
      

      这是工作小提琴:https://jsfiddle.net/dc9f5v0m/

      这有点乱,需要重新计算很多(尤其是将光标围绕正方形居中),但它可以完成工作,并且可以使用任何形状来实现缩放效果,而不仅仅是正方形。

      再次感谢您的帮助。

      【讨论】:

        猜你喜欢
        • 2013-07-02
        • 1970-01-01
        • 2018-09-30
        • 1970-01-01
        • 1970-01-01
        • 2023-03-07
        • 2019-04-14
        • 1970-01-01
        • 2014-05-07
        相关资源
        最近更新 更多