我绘制一个由物体(星星、精灵等)组成的单位球体,这些物体在该球体的“表面”上都有 3D 坐标。相机位于它的中心,我使用透视投影来显示生成的天空图。这是从球体中心看到的视图(我的应用程序将如何向用户显示):
我读过很多关于非投影和光线投射的文章,但最终
gl-matrix
之外,我不使用任何库,只是裸露的 WebGL2)1
)由于我没有模型,除了旋转之外没有相机移动,所以我有超级简单的矩阵代码:
const projectionMatrix = mat4.create()
mat4.perspective(projectionMatrix,
degreesToRad(fov),
viewport.x / viewport.y,
0,
100)
const panningMatrix = mat4.create()
// in place of this comment, rotations are applied to it as the user pans the map
mat4.invert(panningMatrix, panningMatrix)
const groundViewMatrix = mat4.create()
mat4.multiply(groundViewMatrix, panningMatrix, groundViewMatrix)
// some-shader.vert; u_modelViewMatrix is groundViewMatrix in this case
gl_Position = u_projectionMatrix * u_modelViewMatrix * a_position;
那么,如何获取这个单位球体上的 3D 坐标:
所以步骤是:
标准化设备坐标空间是一个 3D 单位立方体,因此要将屏幕像素坐标取消投影到该立方体中,我们可以简单地执行以下操作:
ndcX = (screenX / screenWidth) * 2 - 1
ndcY = (screenY / screenHeight) * 2 - 1
ndcZ = ???
在矩阵中,这将表示为:
注意:与浏览器相比,WebGL Y 像素坐标是反转的,这意味着如果您使用
ClickEvent
s clientY
/offsetY
属性,您想要反转它。
通过上一步,我们计算了 NDC 空间内深度未知的 2D 平面上的 NDC 坐标。为了回到 3D,我们需要认识到 2D 点是 3D 空间中射线的折叠表示(投影),为此我们需要射线上至少两个点(一条线段)来表示它。对于常用的投影矩阵(例如通过 glMatrix
perspective
和 ortho
生成的矩阵),我们可以简单地选择体积的范围,从而给我们两个坐标集:
ndc1 = [ndcX, ndcY, -1]
ndc2 = [ndcX, ndcY, 1]
使用投影矩阵的逆来变换这些点将为我们提供相机空间中近剪裁平面和远剪裁平面上的点。
near = ndc1 * inverseProjectionMatrix
far = ndc2 * inverseProjectionMatrix
注意:你想在这里使用gl-matrix'es
vec3.transformMat4
要撤消相机变换,我们只需使用逆视图矩阵变换我们的两个点。
worldNear = near * inverseViewMatrix
worldFar = far * inverseViewMatrix
注意:同样,你想在这里使用 gl-matrix'es
vec3.transformMat4
为了获取单位球体上的点,我们只需将线段转换为射线表示形式,通常表示为射线原点和标准化射线方向,后者是您要查找的单位球体上的点。为此,我们只需从远处减去近处并标准化所得向量。
rayOrigin = near
rayDirection = normalize(far-near)
请注意,如果您在世界空间中指定相机位置,或者您不以任何方式平移场景(相机位置为 0,0,0),您只需要为
far
执行所有这些操作,如下所示rayOrigin
只是 cameraPosition
。