OpenGL绘制矩形填充window

OpenGL draw a rectangle filling window

我正在尝试理解 OpenGL MVP 矩阵,作为练习,我想使用矩阵绘制一个矩形来填充我的 window。我以为我会很容易地找到一个教程,但我发现的所有这些似乎只是将随机值放入他们的 MVP 矩阵设置中。

假设我的矩形有这些坐标:

GLfloat vertices[] = {
    -1.0f,  1.0f,  0.0f, // Top-left
     1.0f,  1.0f,  0.0f, // Top-right
     1.0f, -1.0f,  0.0f, // Bottom-right
    -1.0f, -1.0f,  0.0f, // Bottom-left
};

这是我的 2 个三角形:

GLuint elements[] = {
    0, 1, 2,
    2, 3, 0
};

如果我使用恒等 MVP 矩阵绘制矩形,它会按预期填满屏幕。现在我想使用一个截锥体。这是它的设置:

float m_fov = 45.0f;
float m_width = 3840;
float m_height = 2160;
float m_zNear = 0.1f;
float m_zFar = 100.0f;

由此我可以计算我的 window 在 z-near 和 z-far 处的宽度/高度:

float zNearHeight = tan(m_fov) * m_zNear * 2;
float zNearWidth = zNearHeight * m_width / m_height;
float zFarHeight = tan(m_fov) * m_zFar * 2;
float zFarWidth = zFarHeight * m_width / m_height;

现在我可以创建视图和投影矩阵了:

glm::mat4 projectionMatrix = glm::perspective(glm::radians(m_fov), m_width / m_height, m_zNear, m_zFar);
glm::mat4 viewMatrix = glm::translate(glm::mat4(1.0f), glm::vec3(0.0f, 0.0f, -m_zNear));

我现在希望这能让我的矩形填充 window:

glm::mat4 identity = glm::mat4(1.0f);
glm::mat4 rectangleModelMatrix = glm::scale(identity, glm::vec3(zNearWidth, zNearHeight, 1));

但是这样做,我的矩形太大了。我错过了什么?


解决方案:正如@Rabbid76 所指出的,问题是我的 z-near 大小的计算,它必须是:

float m_zNearHeight = tan(glm::radians(m_fov) / 2.0f) * m_zNear * 2.0f;
float m_zNearWidth = m_zNearHeight * m_width / m_height;

此外,我现在需要在规范化视图 space ([-0.5, 0.5]) 而不是设备 space ([-1, 1]) 中指定对象坐标。因此我的顶点现在必须是:

GLfloat vertices[] = {
    -0.5f,  0.5f,  0.0f, // Top-left
     0.5f,  0.5f,  0.0f, // Top-right
     0.5f, -0.5f,  0.0f, // Bottom-right
    -0.5f, -0.5f,  0.0f, // Bottom-left
};

平行于视图的 xy 平面的物体在平面上的投影高度是

h' = h * tan(m_fov / 2) / -z

其中h是物体在平面上的高度,-z是深度,m_fov是视野角。

在你的情况下 m_fov 是 45°,-z 是 -0.1 (-m_zNear),因此 tan(m_fov / 2) / z 是 ~4,142。
由于四边形的高度为 2,因此四边形的投影高度为 ~8,282。

要创建一个恰好适合视口的四边形,请使用 90° 的视角和到 1 的对象的距离,因为 tan(90° / 2) / 1 是 1。例如:

float m_fov = 90.0f;
glm::mat4 projectionMatrix = glm::perspective(glm::radians(m_fov), m_width / m_height, m_zNear, m_zFar);
glm::mat4 viewMatrix = glm::translate(glm::mat4(1.0f), glm::vec3(0.0f, 0.0f, -1.0f));

如果tan(m_fov / 2) == -z,则底部为-1、顶部为1的对象适合视口。
由于除以 z,视口上对象的投影大小会随着到相机的距离线性减小。