由于令人惊讶的是,几乎没有关于webGL的信息(或者我只是不知道如何搜索),所以我有一个关于如何将鼠标坐标转换为3D坐标的问题,因此想知道我在屏幕上的确切位置点击。
所以我的情况是,我有一个非常简单的天空盒,相机位于[0,0,0],可以通过单击和拖动来环顾四周。我想做的是能够单击该Skybox上的某个位置,并知道要单击的位置,因为我需要在该位置上添加注释(一些文本或html元素)。而且该html元素必须移动,并且在我转向另一侧时不可见。因此,我需要一种方法来单击鼠标,找出我单击的立方体的哪一侧以及在什么坐标处,以便可以正确放置批注。
我使用的是普通的WebGL,我不使用THREE.js或类似的东西。因为它只是一个立方体,所以我只能假设找到相交不会那么困难,也不需要额外的库。
您肯定是对的,很难找到一个例子😭
[一个通用的webgl着色器使用3D或类似代码进行投影]
gl_Position = matrix * position;
或
gl_Position = projection * modelView * position;
或
gl_Position = projection * view * world * position;
基本上都是相同的东西。他们取position
并将其乘以矩阵以转换为剪辑空间。您需要做相反的操作,否则,在剪辑空间中占据一个位置,然后隐蔽回到position
空间,即[>
inverse (projection * view * world) * clipSpacePosition
因此,请使用3D库并计算要传递给WebGL的矩阵的逆。例如,这里是一些代码,这些代码正在计算矩阵以使用twgl's数学库
const fov = 30 * Math.PI / 180; const aspect = gl.canvas.clientWidth / gl.canvas.clientHeight; const zNear = 0.5; const zFar = 10; const projection = m4.perspective(fov, aspect, zNear, zFar); const eye = [1, 4, -6]; const target = [0, 0, 0]; const up = [0, 1, 0]; const camera = m4.lookAt(eye, target, up); const view = m4.inverse(camera); const viewProjection = m4.multiply(projection, view); const world = m4.rotationY(time);
对于有效执行此操作的着色器
gl_Position = viewProjection * world * position
所以我们需要逆
const invMat = m4.inverse(m4.multiply(viewProjection, world));
然后我们需要一个剪辑空间射线。我们正在从2D到3D,因此我们将使用-1和+1作为Z值,使光线从zNear到zFar切成平截头体。
canvas.addEventListener('mousemove', (e) => { const rect = canvas.getBoundingClientRect(); const x = e.clientX - rect.left; const y = e.clientY - rect.top; const clipX = x / rect.width * 2 - 1; const clipY = y / rect.height * -2 + 1; const start = m4.transformPoint(invMat, [clipX, clipY, -1]); const end = m4.transformPoint(invMat, [clipX, clipY, 1]); ... do something with start/end });
start
和end
现在相对于position
(几何图形中的数据),因此您现在必须在JavaScript中使用一些射线到三角代码来遍历所有三角形,并查看射线是否从头到尾结束与一个或多个三角形的相交。请注意,如果您要的只是世界空间中的光线,而不是位置空间,那么您将使用
const invMat = m4.inverse(viewProjection);