Три. js, неожиданный сдвиг позиции при масштабировании объекта - PullRequest
1 голос
/ 18 февраля 2020

Я пытаюсь создать окно масштабирования, пока мне удалось перевести позиции курсора из локали в мировые координаты и создать объект прямоугольника вокруг курсора с правым uvs.

Вот скрипка моей попытки: https://jsfiddle.net/2ynfedvk/2/

Без масштабирования прямоугольник идеально отцентрирован вокруг курсора, но если вы установите флажок масштабирования, чтобы установить масштаб zoomMesh.scale.set(1.5, 1.5, 1), положение блока сместится дальше вы перемещаете курсор из центра сцены.

Я испорчу любой CSS, например, "преобразование источника" на три. js, чтобы центрировать масштаб вокруг объекта, это правильный подход, чтобы получить это какой-то эффект масштабирования?

Я новичок в трех. js и 3d в целом, так что спасибо за любую помощь.

Ответы [ 2 ]

1 голос
/ 18 февраля 2020

Когда вы масштабируете свой me sh с помощью 1,5, это означает, что применяется матрица преобразования, которая масштабирует значения вершин.

Проблема возникает из-за изменения вершин. Вершины находятся в локальном пространстве ме sh. И когда вы устанавливаете левую верхнюю вершину квадрата, например, на [10, 10, 0], а затем применяете .scale.set(1.5, 1.5, 1) к me sh, тогда координата вершины становится [15, 15, 0]. То же самое относится ко всем остальным 3 вершинам. И именно поэтому центр квадрата не совпадает в 1,5 раза от центра изображения до указателя мыши.

Таким образом, опция заключается не в том, чтобы масштабировать me sh, а в изменении размера квадрат.

Я немного изменил вашу скрипку, так что, возможно, это будет более объяснительным:

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>
0 голосов
/ 18 февраля 2020

спасибо за ответ, не совсем то, что я искал (не только измените размер квадрата, но и увеличьте изображение), но вы указали мне правильное направление.

Как вы сказали, координаты позиций меняются с масштабом, поэтому я должен пересчитать новую позицию относительно масштаба.

Добавлены эти новые строки с новыми scale и offset переменными:

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/

Это немного грязно, с большим пересчетом (особенно для центрирования курсора вокруг квадрата), но он выполняет свою работу, и эффект масштабирования может быть достигнут с любой формой, а не только с квадратом.

Еще раз спасибо за вашу помощь.

...