You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

WebGL屏幕转世界坐标:深度缓冲区值获取异常问题排查

3D地球模型点击检测的深度值转换问题

背景

我正在创建一个带高程的3D地球模型:
带高程的3D地球模型

现在需要添加点击检测功能,通过2D屏幕XY坐标+深度值获取3D世界XYZ坐标。我基于gl-matrix实现了反投影计算代码:

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]范围的立方体:
分布在z-near和z-far间的立方体

隐藏球体后的效果:
隐藏球体后的立方体分布

立方体正确分布在z-near和z-far平面之间,但z-near附近的分辨率远高于z-far(立方体沿轴线分布不均)。缩小z-near会加剧该效果(极端情况所有立方体都在z-near上),这符合深度测试的原理,但不确定是否存在问题。

问题

我通过测试找到与球体相交的紫色立方体的深度值约为0.962(z-near=1,z-far=500),现在需要从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中实现了相同的编码方法,验证编码解码功能正常(仅存在微小精度误差,例如编码解码0.962得到0.9619999999999997)。

但通过该方法从OpenGL获取的深度值远小于预期:实际得到约0.268,而预期为0.962。请问我是否遗漏了对深度缓冲区值的转换?

内容的提问来源于stack exchange,提问作者Tim Autin

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.23 05:07:35