WebGL 屏幕到世界坐标

问题描述 投票:0回答:1

我正在创建一个具有高度的 3D 地球仪:

enter image description here

我现在想要添加点击检测,以从 2D 屏幕 xy + 深度(通过点击)获取 3D 世界 xyz 位置。我使用以下代码(灵感来自从深度缓冲区值获取世界位置)将 2D 坐标 + 深度(使用 gl-matrix 进行计算)投影到 3D 空间中:

static unProject(screenX: number, screenY: number, depth: number, projMatrix: mat4, viewMatrix: mat4): vec3 {

  // Convert x, y, depth from [0 .. 1] to NDC [-1 .. 1]
  const clipSpacePosition: vec4 = vec4.fromValues(
    screenX * 2.0 - 1.0,
    screenY * 2.0 - 1.0,
    depth * 2.0 - 1.0,
    1.0
  );

  let viewSpacePosition: vec4 = vec4.transformMat4(vec4.create(), clipSpacePosition, mat4.invert(mat4.create(), projMatrix));

  // Normalize eye coordinates
  viewSpacePosition[0] /= viewSpacePosition[3];
  viewSpacePosition[1] /= viewSpacePosition[3];
  viewSpacePosition[2] /= viewSpacePosition[3];
  viewSpacePosition[3] /= viewSpacePosition[3];

  const worldSpacePosition: vec4 = vec4.transformMat4(vec4.create(), viewSpacePosition, mat4.invert(mat4.create(), viewMatrix));

  // Normalize world coordinates
  worldSpacePosition[0] /= worldSpacePosition[3];
  worldSpacePosition[1] /= worldSpacePosition[3];
  worldSpacePosition[2] /= worldSpacePosition[3];

  return vec3.fromValues(worldSpacePosition[0], worldSpacePosition[1], worldSpacePosition[2]);
}

使用此函数,我单击了法国东南部并绘制了 1000 个深度在 [0 .. 1] 范围内的立方体:

enter image description here

没有球体:

enter image description here

立方体似乎在我的 z 近平面和 z 远平面之间正确显示,但 z 近平面附近的分辨率远高于 z 远平面附近的分辨率(立方体沿轴分布不均匀)。如果我减少 z 轴附近,则会恶化这种效果(并且在极端情况下,所有立方体都绘制在 z 轴附近)。根据https://learnopengl.com/Advanced-OpenGL/Depth-testing,这是正常的,但可能表明存在问题?

根据经验,我找到了与我的球体相交的立方体的深度值(紫色的那个)。我现在需要从 OpenGL 深度缓冲区获取它。因此,我使用简化的片段着色器再次绘制了场景,将深度 (gl_FragCoord.z) 编码为 RGBA:

#version 300 es
precision highp float;

vec4 encode(float depth) {
    vec4 bitSh = vec4(256.0 * 256.0 * 256.0, 256.0 * 256.0, 256.0, 1.0);
    vec4 bitMsk = vec4(0.0, 1.0 / 256.0, 1.0 / 256.0, 1.0 / 256.0);
    vec4 enc = fract(depth * bitSh);
    enc -= enc.xxyz * bitMsk;
    return enc;
}

void main(void) {
    color = encode(gl_FragCoord.z);
}

并以这种方式解码 JS 端的值:

decode(color: vec4) {
  const bitSh: vec4 = vec4.fromValues(1.0 / ( 256.0 * 256.0 * 256.0 ), 1.0 / ( 256.0 * 256.0 ), 1.0 / 256.0, 1.0);
  return vec4.dot(color, bitSh);
}

我还在JS中编写了encode方法,以确保编码/解码一个值正常工作,它确实有效(有一点精度误差,例如,如果我编码/解码0.962,我得到0.9619999999999997)。

使用此方法,从 OpenGL 检索到的深度值太小(在我的示例中,z-near 为 1,z-far 为 500,我预计约为 0.962,实际结果约为 0.268)。

我是否缺少深度缓冲区值的转换?

webgl projection depth-buffer
1个回答
0
投票

好吧,这是一个愚蠢的错误,我没有读取缓冲区映射中的正确值(我忘记反转 y)。

上面的代码正在工作,尽管我在 gl_FragCoord.z 的编码/解码中似乎存在精度问题,当单击网格时,该点放置在它的正下方。

我现在通过启用 EXT_color_buffer_float 和 EXT_float_blend 扩展来修复它,这允许我直接将 gl_FragCoord.z 作为浮点数读取,现在这些点已正确放置在网格上。

© www.soinside.com 2019 - 2024. All rights reserved.