WebGL 2D Canvas Point конвертируется в 3D World Point - PullRequest
0 голосов
/ 17 февраля 2020

У меня проблема с моим кодом, или я ожидаю неверное значение. Чего я хочу добиться, так это преобразовать 2D-точки из холста в 3D-точку мирового пространства.

Если я правильно понимаю, я всегда должен получать одни и те же точки независимо от поворота камеры, потому что я не хочу ПРОСМОТРЕТЬ ПРОСТРАНСТВО, но МИРОВОЕ ПРОСТРАНСТВО Итак, представьте, что я нажимаю на лицевую стенку куба, расположенную между осями X и Z, тогда, как мне кажется, я должен получить постоянное значение Y, и оно будет работать правильно, пока я не внесу некоторые изменения в поворот камеры. Если мы сделаем так, чтобы камера смотрела на эту стену, но под некоторым углом, то для каждого щелчка мы получим разные значения для оси Y, которые должны быть постоянными, потому что каждая точка на этой стене находится в одной и той же позиции Y.

enter image description here

var r = canvas.getBoundingClientRect();
var x = clientX - r.left;
var y = height - (clientY - r.top);

var projectionMatrix = matrix4.perspective(fov , ratio, near, far);
// convert to clip space
var xClipSpace = x / width * 2.0 - 1.0;
var yClipSpace = y / height * -2.0 + 1.0;
var zClipSpace = 1;
// convert back from clip space to world space
var xyzVec3 = vector3.create(xClipSpace ,yClipSpace ,zClipSpace);
var transfrom = matrix4.multiply(projectionMatrix,viewMatrix);
var inverse = matrix4.invert(transform);
var result = vector3.transformMat4(xyzVec3,inverse);

Что я делаю не так?

1 Ответ

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

Я не уверен на 100%, что я понимаю вашу диаграмму, но в остальном ваш код выглядит нормально.

В большинстве математических выражений WebGL усеченная точка проходит на расстоянии -Z. Конечно, вы можете повернуть это на основе представления. Но в любом случае, если вы пропустите пространство клипа [x, y, -1] через обратную матрицу (проекция * вид), то вы получите некоторую точку дальней плоскости плоскости усечения вида. Поскольку вид вращается, эта точка перемещается вместе с усечением.

Если мы сделаем так, чтобы камера смотрела на эту стену, но с некоторым углом, то для каждого щелчка мы получим разные значения для оси Y, которые она должна быть постоянной, потому что каждая точка на этой стене находится в одной и той же позиции Y.

Нет: если вы поворачиваете камеру, вся стена будет вращаться, так что точки на ней будут вращаться.

Вот диаграмма, смотрящая сверху на усеченный вид в мировом пространстве. Вид вращается. Если clipX и clipY равны 0, то вычисляемая точка находится в центре дальней плоскости усеченного конуса (Z = 1 в пространстве клипа). Вы можете видеть, что эта точка вращается, даже если она остается на плоскости. Его положение в пространстве вида не изменится, но его положение в мировом пространстве изменится, потому что весь угол обзора эффективно поворачивается.

Конечно, вы получите другое значение для Y, если повернуть камеру.

const v3 = twgl.v3;
const m4 = twgl.m4;
const ctx = document.querySelector('canvas').getContext('2d');

const boxTop = [
  [-1,  1, -1],
  [-1,  1,  1],
  [ 1,  1,  1],
  [ 1,  1, -1],
];

function render(time) {
  time *= 0.001;
  
  ctx.setTransform(1, 0, 0, 1, 0, 0);
  ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);

  const fov = 60 * Math.PI / 180;
  const ratio = ctx.canvas.clientWidth / ctx.canvas.clientHeight;
  const near = 10;
  const far = 40;
  const projectionMatrix = m4.perspective(fov , ratio, near, far);
  const viewMatrix = m4.rotationY(time);
  // convert to clip space
  const xClipSpace = 0;
  const yClipSpace = 0;
  const zClipSpace = 1;
  // convert back from clip space to world space
  const xyzVec3 = v3.create(xClipSpace ,yClipSpace ,zClipSpace);
  const transform = m4.multiply(projectionMatrix, viewMatrix);
  const inverse = m4.inverse(transform);
  const result = m4.transformPoint(inverse, xyzVec3);
  
  // -------------

  ctx.setTransform(1, 0, 0, 1, 150.5, 75.5);
  
  // draw origin
  ctx.beginPath();
  for (let i = -200; i <= 200; i += 20) { 
    ctx.moveTo(-400, i);
    ctx.lineTo( 400, i);
    ctx.moveTo(i, -400);
    ctx.lineTo(i,  400);
  }
  ctx.strokeStyle = '#DDD';
  ctx.stroke();
  
  ctx.beginPath();
  ctx.moveTo(-400, 0);
  ctx.lineTo( 400, 0);
  ctx.moveTo(0, -400);
  ctx.lineTo(0,  400);
  ctx.strokeStyle = '#444';
  ctx.stroke();
  
  ctx.fillStyle = '#888';
  ctx.fillText('x', 140, 10);
  ctx.fillText('z', 5, -65);
  
  // draw frustum
  ctx.beginPath();
  for (let i = 0; i < 4; ++i) {
    const v0 = m4.transformPoint(inverse, boxTop[i]);
    const v1 = m4.transformPoint(inverse, boxTop[(i + 1) % 4]);
    drawLine(ctx, v0, v1);
  }
  ctx.strokeStyle = 'black';
  ctx.stroke();
  
  {
    ctx.beginPath();
    ctx.arc(result[0], result[2], 3, 0, Math.PI * 2);
    ctx.fillStyle = 'red';
    ctx.fill();
    ctx.fillText(`${result[0].toFixed(2)}, ${result[2].toFixed(2)}`, result[0] + 5, result[2] + 3);
  }
  
  requestAnimationFrame(render);
}
requestAnimationFrame(render);


function drawLine(ctx, v0, v1) {
  ctx.moveTo(v0[0], v0[2]);
  ctx.lineTo(v1[0], v1[2]);
}

render();
canvas { border: 1px solid black; }
<canvas></canvas>
<script src="https://twgljs.org/dist/4.x/twgl-full.js"></script>
...