在相机后面的位置进行广告牌时的伪影

Posted

技术标签:

【中文标题】在相机后面的位置进行广告牌时的伪影【英文标题】:Artefacts when billboarding at positions just behind camera 【发布时间】:2021-08-21 10:07:14 【问题描述】:

我在 opengl/rust 中绘制了一堆广告牌/三角形。我得到这些人工制品,其中一半的屏幕颜色模糊。经过调试,我发现经过变换后,一些顶点变得非常大。我将在下面用 glam 矩阵库在 rust 中放置一个可重复性最低的示例。

    let camera_position = [-696.2638, 31.615665, 346.61575];
    let camera_front = [0.72691596, -0.2045585, 0.65555245];
    let vtx_pos = [-714.08, 21.8639, 363.706];
    let fov: f32 = 1.012;
    let width = 1920_f32;
    let height = 1080_f32;
    let up = vec3(0.0, 1.0, 0.0).normalize();

    
    let camera_position = vec3(camera_position[0], camera_position[1], camera_position[2]);
    let camera_front = vec3(camera_front[0], camera_front[1], camera_front[2]);
    let camera_dvec =  camera_position + camera_front;
    dbg!(camera_dvec);
    // make matrix
    let view = Mat4::look_at_lh(camera_position, camera_dvec, up);
    dbg!(view);
    let projection = Mat4::perspective_lh(fov, width / height, 1.0, 5000.0);
    dbg!(projection);
    let vp = projection * view;
    dbg!(vp);

    // left bottom vertex
    let world_vtx = vec4(vtx_pos[0], vtx_pos[1], vtx_pos[2], 1.0);
    dbg!(world_vtx);
    let clip_vtx = vp * world_vtx;
    dbg!(clip_vtx);
    let ndc_vtx = clip_vtx.xyz() / clip_vtx.w;
    dbg!(ndc_vtx);

从上面的代码,我们得到


[src/main.rs:29] world_vtx = Vec4(
    -714.08,
    21.8639,
    363.706,
    1.0,
)
[src/main.rs:31] clip_vtx = Vec4(
    -24.995728,
    -17.885654,
    -0.7529907,
    0.24725342,
)
[src/main.rs:33] ndc_vtx = Vec3(
    -101.09356,
    -72.33734,
    -3.045421,
)

我认为 clip_vtx 的 W 部分应该在 -Z 的值附近。但其较小的值导致透视分割后的归一化设备坐标变得巨大。这使得三角形大得令人讨厌,每当该三角形的一部分进入我的视锥体时,我就会得到奇怪的颜色伪影。

有人可以帮我找出我做错了什么吗?或者这是教程没有教给我的矩阵变换的正常部分。

从我在 gamedev discords 中了解到的问题: 我在某个位置制作了一个广告牌,就像在相机后面一样。这会导致某种不连续性,其中左顶点停留在最左边,而右顶点由于变化的符号在 ndc 中变得非常大,如果我不手动剪辑它们就会产生问题。我仍然不知道如何处理不连续性的问题。现在我只是在计算左右顶点之间的距离,如果它在 ndc 中大于 2.0,我会跳过渲染那个广告牌。

有没有更好的方法来确定哪些位置会导致广告牌变得巨大?

【问题讨论】:

检查您的坐标,您的vtx_pos 似乎在摄像机后面。当您从-696.26380.72691596 方向看(x 轴)时,-714.08 不在相机前面。您还可以很容易地看到,因为剪辑空间中的可见点必须在 [-w, w] 范围内具有 x,y,z。 @BDL 是的,但是那个顶点是两个三角形的一部分,它们都形成了一个巨大的三角形,当我稍微旋转我的相机时,那个三角形的一部分进入了我的视锥体。这有点导致问题。我应该怎么做才能避免这种情况? 为什么要“避免”这种情况?如果您的世界中有一个巨大的三角形,并且它与您的视锥相交,则输出是正确的。您在这里真正要解决的问题是什么? @derhass 我没有大三角形。我在某个位置制作了一个广告牌,就像在相机后面一样。这会导致某种不连续性,其中左顶点停留在最左边,而右顶点由于变化的符号在 ndc 中变得非常大,如果我不手动剪辑它们就会产生问题。在多次不和谐询问后我意识到了这一点,但我仍然不知道如何处理不连续的问题。现在我只是在计算左右顶点之间的距离,如果它在 ndc 中大于 2.0,我会跳过渲染那个广告牌。 这个问题根本不清楚。 OpenGL 会自动进行所有裁剪,所以这个问题完全是误导性的。如果您编写自己的渲染器,那么剪裁将是必不可少的,否则如果几何部分位于相机后面,您会得到荒谬的伪影。 【参考方案1】:

花了几个小时询问和戳其他有经验的图形编程不和谐的人,但我对正在发生的事情有了更好的了解。 现在我改变了我的问题,过去我可以在堆栈溢出中看到类似的问题。我只是检查了其中一些并找到了一个很好的答案 https://***.com/a/65168709/7484780.

剪裁的整个问题是因为我将三角形/广告牌顶点作为 vec3 发送,而在顶点着色器中我做gl_Position = vec4(pos, 1.0),所以 opengl 不做任何剪裁。同时,在进行透视分割之前,我也不做任何剪辑。所以,未剪辑的广告牌是我得到的人工制品,尤其是在相机后面。

【讨论】:

如果您手动进行透视分割,并且只是将 NDC 坐标输入到gl_Position(出于某种原因,它位于 剪辑空间 中),那么您不仅可以绕过剪辑, 也是插值的透视校正。

以上是关于在相机后面的位置进行广告牌时的伪影的主要内容,如果未能解决你的问题,请参考以下文章

使用 Accelerate 缩放 Ycbcr (420f) 时的伪影

光线行进时的 GLSL 伪影

SceneKit + ARKit:无需相机滚动的广告牌

菱形算法中的伪影

从 numpy 数组中删除类似方波的伪影

Three.js - 广告牌效果,相机平移后保持方向