javascript - 如何使用 J3DIMath.js 库将 WebGL Canvas 中的鼠标坐标转换为 3D 空间中的射线?

标签 javascript matrix 3d webgl

我有一个 WebGL 应用程序,我正在使用 J3DIMatrix4 类来计算模型 View 透视矩阵(因为这是每个教程所做的)。

现在我想弄清楚鼠标在哪个对象上,我需要将鼠标位置转换为世界空间中的射线。我正在使用 J3dIMath.js library和下面的代码来计算我的透视矩阵

this.perspectiveMatrix = new J3DIMatrix4();
this.perspectiveMatrix.perspective(30, canvas.clientWidth / canvas.clientHeight, 1, 10000);
this.perspectiveMatrix.lookat(0,0,7, 0,0,0, 0,1,0)

当需要实际绘制对象时,我将其与对象的矩阵混合:

this.mvpMatrix.load(this.perspectiveMatrix);
this.mvpMatrix.multiply(this.mvMatrix);
this.mvpMatrix.setUniform(gl, this.u_modelViewProjMatrixLoc, false);

顶点着色器是一个沼泽标准

uniform mat4 u_modelViewProjMatrix;
attribute vec2 vTexCoord;
attribute vec4 vPosition;
varying vec2 v_texCoord;
void main()
{
    gl_Position = u_modelViewProjMatrix * vPosition;
    v_texCoord = vTexCoord;
}

我尝试反转透视矩阵并使用

var mat = new J3DIMatrix4()
mat.load(this.perspectiveMatrix)
mat.multiply(this.mvMatrix)
mat.invert()
var coord = new J3DIVector3(0.7, 0.5, 1)
coord.multVecMatrix(mat)
debug_log(coord)
//I picked [0.7,0.5,1] because I figured it likely represented an on-screen point in camera space.

不幸的是,这给了我一些非常奇怪的结果,比如 [8121,362, -8120]。我更期待 [4,4,6] 附近的结果

我认为这是因为 .perspective() 调用正在创建一个非仿射矩阵。我想我需要一些更像是 blender 的相机对象矩阵的东西,它编码“眼球”的方向和位置,但没有透视调整。

鉴于我为视角和外观选择的值,我将如何构建仿射和可逆相机矩阵? (然后我会用它来计算焦点并将鼠标坐标映射到点击射线上的一个点)

解决方案将根据清晰度和长度来判断(如果您依赖 J3DIMath 以外的某些外部库,那将被添加到您的行数中)

(How to get object in WebGL 3d space from a mouse click coordinate 的答案大多难以理解,因为它的长度和它依赖于 Jax 的事实)

最佳答案

如果你通过these tutorials我希望他们能明确表示透视矩阵只能到此为止。在您到达裁剪空间之前,仍然会发生除以 w 的情况。

所以如果原来的世界->裁剪空间是

tempPoint = projectionMatrix * viewMatrix * worldSpacePoint
clipSpacePoint = tempPoint / tempPoint.w

然后往回走是

tempPoint = clipSpacePoint * tempPoint.w
worldSpacePoint = inverse(projectMatrix * viewMatrix) * tempPoint

我们从 projectMatrix 中得知 tempPoint.w 在平截头体近平面时为 zNear,在平截头体近平面时为 zFar远平面。

因此,从鼠标向后返回到 3D,您首先必须将鼠标转换为裁剪空间 (-1 + 1)。假设你有 Canvas 相对鼠标坐标

clipX = mouseX / gl.canvas.clientWidth  * 2 - 1;    
clipY = mouseY / gl.canvas.clientHeight * -2 + 1;   // because GL is 0 at bottom
clipZ = -1 (for close) +1 for (far)

所以

clipNear = [clipX, clipY, -1, 1];
clipFar  = [clipX, clipY,  1, 1];

现在您需要乘以 zNearzFar。我们本可以那样做 在第一步

tempNear = [clipX * zNear, clipY * zNear, -zNear, zNear];
tempFar  = [clipX * zFar , clipY * zFar ,  zFar ,  zFar];

或者我们可以使用一些函数。据我所知,J3DImath 没有我们可以用来执行此操作的函数。

现在您回到应用透视矩阵后的值,因此如果您有相机,您最终可以乘以逆透视或逆 viewPerspective,就像您不是从 0,0 观察一样, 0.

不幸的是,据我所知,J3DIMath doesn't have a function to do this .我看到的唯一函数是 J3DIVector3.prototype.multVecMatrix 但是 looking at the source该函数假定 w 将为 1,但我们可以看到上面的 w 不是 1。

因此,我建议使用另一个数学库

同时这段代码应该可以工作。

function multVec4J3DIMatrix function(vec4, matrix) {
    var x = vec4[0];
    var y = vec4[1];
    var z = vec4[2];
    var w = vec4[3];

    var m = matrix.$matrix;

    return [
      x * m.m11 + y * m.m21 + z * m.m31 + w * m.m41, 
      x * m.m12 + y * m.m22 + z * m.m32 + w * m.m42,
      x * m.m13 + y * m.m23 + z * m.m33 + w * m.m43,
      x * m.m14 + y * m.m24 + z * m.m34 + w * m.m44,
    ];
}

所以从你上面的例子来看

var mat = new J3DIMatrix4()
mat.load(this.perspectiveMatrix)
mat.multiply(this.mvMatrix)
mat.invert()

// from this line we see zNear and zFar
// this.perspectiveMatrix.perspective(30, canvas.clientWidth / canvas.clientHeight, 1, 10000);
zNear = 1;
zFar  = 10000;

// var coord = new J3DIVector3(0.7, 0.5, 1)
// I'm going to assume since you put 1 for z you wanted zFar
coord = [0.7 * zNear, 0.7 * zNear, zFar, zFar];
world = multVec4J3DIMatrix(coord, mat);

关于javascript - 如何使用 J3DIMath.js 库将 WebGL Canvas 中的鼠标坐标转换为 3D 空间中的射线?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29684811/

相关文章:

javascript - html按钮在没有被禁用的情况下不可点击

matlab - MATLAB 中矩阵中补丁的循环移位

c++ - OpenGL 透视矩阵不起作用

java - 你如何在 Android Studio 中使用 3D 图形?

c++ - 如何从其回调函数中删除节点?

javascript - 在 webOS 应用程序中提交和存储数据?

javascript - SQL 查询错误 - 修复建议?

javascript - 在 div 内水平居中 li 溢出 :auto

algorithm - 微软面试 : transforming a matrix

javascript - ThreeJs 中的门户