在WebGL程序中在鼠标和2D“世界”位置之间进行转换

huangapple go评论73阅读模式
英文:

Converting between mouse and 2D "world," positions in webGL program

问题

我正在制作一个带有平移和缩放功能的简单绘画程序。

您可以在此链接找到关于该问题的最小重现案例:https://codesandbox.io/s/mouse-coords-min-repro-4rfg1v?file=/src/index.ts

问题出在当我尝试在calcMouseWorldPos()函数中转换鼠标坐标和"世界"坐标时发生的情况。由于画布宽度为300个单位,每个角应该在"世界空间"中恰好位于+/-160(320的一半),这意味着如果我成功地将鼠标坐标转换为世界空间,悬停在每个角上应该在控制台中打印出{x: +/-160, y: +/-160}

目前当我缩放时它是可以正常工作的,但一旦开始平移,它就会出现严重的错误。我来自ThreeJS/Unreal背景,试图学习更基础的图形编程知识,这个问题让我有些困惑。这不应该只是一个简单的逆矩阵问题吗?我在这里做错了什么?

编辑:以下是相关的代码部分:

const vertexSource = `
uniform mat3 u_viewMatrix;

attribute vec2 a_position;
attribute vec2 a_uv;

varying vec2 vUv;

void main(){
  vec3 aspPos = vec3(a_position, 1.0) * u_viewMatrix;

  vUv = a_uv;

  gl_Position = vec4(aspPos, 1.0);
}
`;

function updateViewMatrix(): Mat3 {
  const { x, y } = offset;
  const dimensions = new Vector(canvas.clientWidth / 2, canvas.clientHeight / 2);
  const zoomMat = new Mat3([zoom, 0, 0, 0, zoom, 0, 0, 0, 1]);
  const transMat = new Mat3([1, 0, x, 0, 1, y, 0, 0, 1]);
  const aspectMat = new Mat3([
      1.0 / dimensions.x, 0, 0,
      0, 1.0 / dimensions.y, 0,
      0, 0, 1.0
  ]);
  viewMatrix.copy(zoomMat.multiply(aspectMat).multiply(transMat));
  viewMatrixUniform.set(false, viewMatrix.data);
  return viewMatrix;
}

function calcMouseWorldPos(event: MouseEvent): void {
  const mousePos = new Vector(event.offsetX, event.offsetY).multiplyScalar(devicePixelRatio);
  const windowDimensions = new Vector(canvas.width, canvas.height);

  //convert from pixels to clip space
  mousePos.divide(windowDimensions).subtractScalar(0.5).multiplyScalar(2);
  mousePos.y *= -1;

  //convert from clip space to world space
  mousePos.multiplyMat3(updateViewMatrix().clone().inverse());

  console.log(mousePos.toObject());
}
英文:

I'm making a simple painting program with panning and zooming.

You can find a minimum reproduction of the issue here: https://codesandbox.io/s/mouse-coords-min-repro-4rfg1v?file=/src/index.ts

The issue is what's happening when I try to convert between mouse coordinates and "world," coordinates in calcMouseWorldPos(). Since the canvas is 300 units wide, each corner should be at exactly +/-160 (half of 320) in "world space," meaning if I were to successfully convert mouse coordinates to world space, hovering over each corner should print {x: +/-160, y: +/-160} to the console.

It currently works that way when I zoom in/out, but the second I start panning it breaks horribly. I'm coming from ThreeJS/Unreal background and trying to learn more ground level graphics programming and this one is making my head spin a bit. Shouldn't this just be a simple inverse-matrix problem? What am I doing wrong here?

EDIT: Here's the important bits in question

const vertexSource = `
uniform mat3 u_viewMatrix;
attribute vec2 a_position;
attribute vec2 a_uv;
varying vec2 vUv;
void main(){
vec3 aspPos = vec3(a_position, 1.0) * u_viewMatrix;
vUv = a_uv;
gl_Position = vec4(aspPos, 1.0);
}
function updateViewMatrix(): Mat3 {
const { x, y } = offset;
const dimensions = new Vector(canvas.clientWidth / 2, canvas.clientHeight / 2);
const zoomMat = new Mat3([zoom, 0, 0, 0, zoom, 0, 0, 0, 1]);
const transMat = new Mat3([1, 0, x, 0, 1, y, 0, 0, 1]);
const aspectMat = new Mat3([
1.0 / dimensions.x, 0, 0,
0, 1.0 / dimensions.y, 0,
0, 0, 1.0
]);
viewMatrix.copy(zoomMat.multiply(aspectMat).multiply(transMat));
viewMatrixUniform.set(false, viewMatrix.data);
return viewMatrix;
}
`;
function calcMouseWorldPos(event: MouseEvent): void {
const mousePos = new Vector(event.offsetX, event.offsetY).multiplyScalar(devicePixelRatio);
const windowDimensions = new Vector(canvas.width, canvas.height);
//convert from pixels to clip space
mousePos.divide(windowDimensions).subtractScalar(0.5).multiplyScalar(2);
mousePos.y *= -1;
//convert from clip space to world space
mousePos.multiplyMat3(updateViewMatrix().clone().inverse());
console.log(mousePos.toObject());
}

答案1

得分: 0

计算鼠标位置到世界坐标的原始数学代码如下:

clipSpace = ((mousePos / windowDimensions) - 0.5) * 2
clipSpace.y *= -1

worldSpace = clipSpace * viewMatrix.inverse()

通过将最后一行替换为以下代码,一切都正常工作。我甚至没有在我的矩阵库中有一个本地的转置方法,但决定尝试一下,结果证明这是必要的。不知道为什么需要,因为我以前从未见过它被提到过,而且我的矩阵库的inverse()方法的输出与所有在线计算器的输出匹配,但我猜这里出于某种原因是必要的。

现在我知道了,当怀疑时,转置!

此外,任何了解矩阵更好的人,我仍然想知道为什么它是必要的,这样我就能更好地直观地处理将来的类似情况,简短的解释将会很棒。

英文:

Original math for calculating mouse -> world-pos

clipSpace = ((mousePos / windowDimensions) - 0.5) * 2;
clipSpace.y *= -1;
worldSpace = clipSpace * viewMatrix.inverse()

by swapping out the last line with

worldSpace = clipSpace * viewMatrix.inverse().transpose()

everything works. I didn't even have a native transpose method in my matrix library yet, but decided to try it out as a Hail Mary last ditch effort, and it turns out that's what was needed. No idea why it was needed, as I've never seen it mentioned before and the output of my matrix library's inverse() method matches up with the output of all the online calculators, but I guess it was needed here for some reason.

Now I know, when in doubt, transpose!

Also, anyone reading this who understands matrices better than me, I'd still love to know why it was needed so I could better intuit situations like this in the future, a quick explanation would be fantastic.

huangapple
  • 本文由 发表于 2023年3月1日 10:44:18
  • 转载请务必保留本文链接:https://go.coder-hub.com/75599131.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定