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

自定义图形引擎中顶点着色器的像素空间深度偏移问题

解决点图元深度偏移与非线性深度缓冲区的适配问题

核心问题出在你直接在投影空间调整Z值——深度缓冲区的非线性特性(与1/视图空间深度正相关)导致这种硬调整无法在全深度范围生效。正确的思路是在视图空间(相机空间)完成偏移,再转换到投影空间,这样就能保证远近处的偏移量都符合像素半径对应的真实空间距离。

具体步骤

  • 将点转换到视图空间
    先把世界坐标的点通过视图矩阵转换到相机空间(视图空间),注意坐标系差异:多数引擎中视图空间Z轴指向相机外(值越大离相机越远),部分引擎(如OpenGL)Z轴指向相机内(值为负),后续计算要对应符号。

  • 计算像素半径对应的视图空间偏移量
    利用相似三角形原理,屏幕上的像素半径对应视图空间的线性距离,和深度成正比:

    1. 将垂直视场角转为弧度:fov_y_rad = radians(fov_y)(fov_y是引擎设置的垂直视场角,比如60°)
    2. 计算单个像素对应的垂直角度:pixel_angle = fov_y_rad / screen_height(screen_height是屏幕像素高度)
    3. 获取点到相机的视图空间距离:view_distance = abs(view_pos.z)
    4. 计算视图空间偏移量:view_offset = tan(pixel_angle / 2.0) * point_pixel_radius * view_distance * 2.0
      (乘2是因为tan(theta/2)是半角对应的对边,乘以2得到完整的像素半径对应的空间长度)
  • 向相机方向偏移视图空间点
    根据视图空间Z轴方向调整:

    • 若Z轴指向相机外(值越大越远):view_pos.z += view_offset(减小深度值,靠近相机)
    • 若Z轴指向相机内(值为负):view_pos.z -= view_offset(让Z值更靠近相机,即更负)
  • 转换到投影空间
    用投影矩阵将偏移后的视图空间点转换为投影空间坐标,此时的深度值会正确映射到深度缓冲区,远近偏移效果一致。

伪代码示例(GLSL风格)

// 输入参数
uniform mat4 view_matrix;
uniform mat4 proj_matrix;
uniform float fov_y; // 垂直视场角,单位:度
uniform float screen_height; // 屏幕像素高度
uniform float point_pixel_radius;
in vec3 world_pos;

void main() {
    // 转换到视图空间
    vec3 view_pos = (view_matrix * vec4(world_pos, 1.0)).xyz;
    
    // 计算视图空间偏移量
    float fov_y_rad = radians(fov_y);
    float pixel_angle = fov_y_rad / screen_height;
    float view_distance = abs(view_pos.z);
    float view_offset = tan(pixel_angle * 0.5) * point_pixel_radius * view_distance * 2.0;
    
    // 向相机方向偏移(假设视图空间Z轴指向相机外)
    view_pos.z += view_offset;
    
    // 转换到投影空间
    vec4 proj_pos = proj_matrix * vec4(view_pos, 1.0);
    gl_Position = proj_pos;
    
    // 保持点的恒定像素大小(你已实现的部分)
    gl_PointSize = point_pixel_radius;
}

关键说明

  • 你之前用proj_vertex_pos.z -= point_pixel_radius / proj_vertex_pos.w * 100.0能在近处生效,是因为近处proj_vertex_pos.w(即视图空间Z值的绝对值)较小,除以w后的值相对大;但远处w值大,除以w后的值趋近于0,乘100也无法弥补非线性映射的问题,属于凑数的临时方案。
  • 如果是正交投影,无需计算角度,直接将像素半径转换为正交投影的单位长度即可(正交投影是线性深度,偏移量恒定)。
  • 偏移后要注意避免点超出近裁剪面,可添加view_pos.z = max(view_pos.z, near_plane + view_offset)这类限制。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.11 03:21:12