【问题标题】:Confusion about zFar and zNear plane offsets using glm::perspective使用 glm::perspective 混淆 zFar 和 zNear 平面偏移
【发布时间】:2021-11-13 13:20:29
【问题描述】:

我一直在使用 glm 来帮助构建用于自学的软件光栅化器。在我的相机类中,我使用 glm::lookat() 创建视图矩阵,并使用 glm::perspective() 创建透视矩阵。

我似乎得到了我对左、右顶部和底部剪裁平面的期望。但是,我似乎对我的近/远飞机做错了什么,或者我的理解有误。我的“google-fu”已经到了让我失望的地步。

在我正确地从我的 glm::perspective 矩阵中提取剪切平面的假设下进行操作,并使用一般平面方程:

aX+bY+cZ+d = 0

我的 zNear 和 zFar 平面出现了奇怪的 d 或 "offset" 值。 据我了解,d 值是我将沿法线向量移动/平移平面的点 P0 的值。

它们分别是 0.200200200 和 -0.200200200。但是,对于垂直于我的 z 基向量的平面,我的法线正确地定向为沿 z 轴的 +1.0f 和 -1.f。

因此,当针对这些平面测试 (0, 0, -5) 世界空间之类的点时,它会被我的视图矩阵转换为:

(0, 0, 5.81181192)

因此在剪辑链中针对这些平面进行测试,所述示例顶点将被剔除。

这是建立相关矩阵的相机类的开始:

static constexpr glm::vec3 UPvec(0.f, 1.f, 0.f);
static constexpr auto zFar = 100.f;
static constexpr auto zNear = 0.1f;


Camera::Camera(glm::vec3 eye, glm::vec3 center, float fovY, float w, float h) :

viewMatrix{ glm::lookAt(eye, center, UPvec) },
perspectiveMatrix{ glm::perspective(glm::radians<float>(fovY), w/h, zNear, zFar) },

frustumLeftPlane {setPlane(0, 1)},
frustumRighPlane {setPlane(0, 0)},
frustumBottomPlane {setPlane(1, 1)},
frustumTopPlane {setPlane(1, 0)},
frstumNearPlane  {setPlane(2, 0)},
frustumFarPlane {setPlane(2, 1)},

平截头体对象基于以下结构:

struct Plane
{
    glm::vec4 normal;
    float offset;
};

我从透视矩阵中提取了 6 个剪切平面,如下所示:

Plane Camera::setPlane(const int& row, const bool& sign)
{
    float temp[4]{};
    Plane plane{};
    if (sign == 0)
    {
        for (int i = 0; i < 4; ++i)
        {
            temp[i] = perspectiveMatrix[i][3] + perspectiveMatrix[i][row];
        }
    }
    else
    {
        for (int i = 0; i < 4; ++i)
        {
            temp[i] = perspectiveMatrix[i][3] - perspectiveMatrix[i][row];
        }
    }

    plane.normal.x = temp[0];
    plane.normal.y = temp[1];
    plane.normal.z = temp[2];
    plane.normal.w = 0.f;
    plane.offset = temp[3];
    plane.normal = glm::normalize(plane.normal);

    return plane;
}

任何帮助将不胜感激,因为我现在不知所措。

非常感谢。

【问题讨论】:

  • 您到底想实现什么?裁剪(由 OpenGL 完成)发生在投影步骤之后,并且不需要您从投影矩阵中获取裁剪平面。这是不相关的:使用布尔然后通过/测试数值是一种不好的做法。如果你有一个布尔值,请使用 true、false。
  • 我没有直接使用 OpenGL。我正在创建一个软件渲染器,所以我必须自己实现所有的剪辑逻辑。我只是很困惑,为什么我的 zNear 和 zFar 平面如此靠近。

标签: matrix opengl graphics glm-math


【解决方案1】:

平面方程的 d 参数描述了平面相对于原点沿平面法线的偏移量。这也考虑了法线的长度。

不能只对法线进行归一化而不调整 d 参数,因为归一化会改变法线的长度。如果要对平面方程进行归一化,则还必须将除法步骤应用于 d 坐标:

float normalLength = sqrt(temp[0] * temp[0] + temp[1] * temp[1] + temp[2] * temp[2]);

plane.normal.x = temp[0] / normalLength;
plane.normal.y = temp[1] / normalLength;
plane.normal.z = temp[2] / normalLength;
plane.normal.w = 0.f;
plane.offset = temp[3] / normalLength;

旁注 1:通常,将平面方程的偏移量存储在 vec4 的 w 坐标中,而不是单独的变量中。原因是您使用它执行的典型操作是点到平面距离检查,例如dist = n * x - d(对于给定的点 x,正常 n,偏移 d,* 是点积),然后可以写为 dist = [n, d] * [x, -1] .

附注 2:大多数软件和硬件光栅器在投影步骤之后执行剪辑,因为它更便宜且更易于实施。

【讨论】:

  • 感谢您的建议和反馈。真的很感激。我是否错误地假设投影步骤是除以 W 之前的步骤,即进入 NDC?
  • 这是正确的。裁剪通常发生在投影之后和透视分割之前。您基本上需要将 x,y,z 与 w 进行比较。如果在所有三个轴的剪辑空间-w &lt;= x &lt;= w 中,则该点位于可见区域内。
  • 非常感谢。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2023-03-17
  • 2018-02-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多