使用 glm::perspective 对 zFar 和 zNear 平面偏移的混淆

Confusion about zFar and zNear plane offsets using glm::perspective

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

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

假设我正确地从我的 glm::perspective 矩阵中提取裁剪平面,并使用通用平面方程:

aX+bY+cZ+d = 0

我的 zNear 和 zFar 平面的 d"offset" 值变得很奇怪。 据我了解,d 值是 shifting/translatin 平面沿法向量的点 P0 的值。

分别是0.200200200和-0.200200200。但是,对于垂直于我的 z 基向量的平面,我的法线在 z 轴上的 +1.0f 和 -1.f 方向是正确的。

所以当针对这些平面测试诸如 (0, 0, -5) 世界 space 的点时,它被我的视图矩阵转换为:

(0, 0, 5.81181192)

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

这里是一个摄像头class建立相关矩阵的开始:

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;
}

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

非常感谢。

平面方程的 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:大多数软件和硬件光栅器在投影步骤之后执行裁剪,因为它更便宜且更容易实现。