Javascript 如何使用J3DIMath.js库将WebGL画布中的鼠标坐标转换为三维空间中的光线?

Javascript 如何使用J3DIMath.js库将WebGL画布中的鼠标坐标转换为三维空间中的光线?,javascript,matrix,3d,webgl,Javascript,Matrix,3d,Webgl,我有一个WebGL应用程序,我使用J3DIMatrix4类来计算模型视图透视矩阵(因为每个教程都是这样做的) 现在我想知道鼠标在什么物体上,我需要将鼠标位置转换为世界空间中的光线。我正在使用和下面的代码来计算透视矩阵 this.perspectiveMatrix = new J3DIMatrix4(); this.perspectiveMatrix.perspective(30, canvas.clientWidth / canvas.clientHeight, 1, 10000); this.

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

现在我想知道鼠标在什么物体上,我需要将鼠标位置转换为世界空间中的光线。我正在使用和下面的代码来计算透视矩阵

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);
顶点着色器是bog标准

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.
不幸的是,这给了我一些非常奇怪的结果,比如[8121362,-8120]。我期望在[4,4,6]附近有更多的结果

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

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

解决方案将根据清晰度和长度进行判断(如果您依赖于J3DIMath以外的外部库,则该库将添加到行数中)

(由于它的长度和它依赖于Jax的事实,这个问题的答案大部分是难以理解的)

如果你仔细研究一下,我希望他们能清楚地说明透视矩阵的作用仅限于此。在你到达clipspace之前,还有一个除以w的过程

因此,如果原始世界->剪辑空间

tempPoint = projectionMatrix * viewMatrix * worldSpacePoint
clipSpacePoint = tempPoint / tempPoint.w
然后向后看

tempPoint = clipSpacePoint * tempPoint.w
worldSpacePoint = inverse(projectMatrix * viewMatrix) * tempPoint
我们从projectMatrix知道,
tempPoint.w
在平截头体的近平面时为
zNear
,在远平面时为
zFar

因此,从鼠标后退到3D,首先必须将鼠标转换为clipspace(-1+1)。假设您有画布相对鼠标坐标

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)
所以

现在需要乘以
zNear
zFar
。我们本可以那样做的 第一步

tempNear = [clipX * zNear, clipY * zNear, -zNear, zNear];
tempFar  = [clipX * zFar , clipY * zFar ,  zFar ,  zFar];
或者我们可以使用一些功能。据我所知,J3DImath没有一个函数可以用来实现这一点

现在,在应用透视矩阵之后,您将返回到该值,以便最终可以乘以反向透视或反向viewPerspective(如果您有摄影机),就像不从0,0,0进行查看一样

不幸的是,据我所知。我看到的唯一函数是
J3DIVector3.prototype.multVecMatrix
,但该函数假设
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);
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);