0

我想根据 glm::project 的输出计算对象位置的 z 缓冲区。下面代码中 z-buffer 的计算来自https://en.wikipedia.org/wiki/Z-buffering

我试过的

int windowWidth = 800;
int windowHeight = 600;
float positions[] = {-42.5806f, 27.8838f, 49.9729f} // Example point
glm::mat4 model;
glm::mat4 view;
glm::mat4 proj;

view = glm::lookAt(
    glm::vec3( 0.0f, 0.0f, 2.0f ),
    glm::vec3( 0.0f, 0.0f, 0.0f ),
    glm::vec3( 0.0f, 1.0f, 0.0f )
    );

proj = glm::perspective( 45.0f, aspect, 0.1f, 10.0f );

// Get screen coordinates from an object point
glm::vec3 screenCoords = glm::project(
   glm::vec3( positions[0], positions[1] , positions[2] ),
   view*model,
   proj,
   glm::vec4( 0, 0, windowWidth, windowHeight )
);

// Calculating the z-buffer
int zFar = 10;
int zNear = 0.1;
float zBufferValue = (zFar+zNear ) / ( zFar-zNear ) + ( 1/screenCoords.z) * ( ( -2*zFar*zNear ) / ( zFar - zNear ) );

问题

无论我如何旋转模型或使用哪个点,zBufferValue 的值都是 1。根据 wiki 页面,该值应介于 -1(近平面)和 1(远平面)之间。

我在计算中做错了什么?

4

2 回答 2

2

您的最后一行代码是多余的。深度计算是在投影变换(以及随后的透视划分)期间完成的。本质glm::project上是这样的:

// P:  projection matrix
// MV: modelview matrix
// v:  vertex to convert to screen space

vec4 result = P * MV * vec4(v, 1.0f);
result /= result.w;     // perspective divide

// convert X/Y/Z from normalized device coords to screen coords...
result.z = (result.z + 1.0f) * 0.5f;
// ...

return vec3(result);

它还将 X/Y 坐标从规范化的设备空间[(-1, -1), (1, 1)]转换为屏幕空间[(0, 0), (viewport_width, viewport_height)],但由于您只关心深度缓冲区,所以我在上面留下了这一步。

因此,忽略代码的最后 3 行,screenCoords.z应该包含与从glReadPixels获得的深度缓冲区值相等的深度缓冲区值。

当然,存储在显卡上的实际位数取决于深度缓冲区的大小以及 OpenGL 是如何设置使用它的。特别是,如果您使用自定义glDepthRange,则上述值将与深度缓冲区中存储的值不同。

于 2013-06-22T15:31:07.760 回答
1

您将该 Wikipedia 文章中的公式应用于错误的值。您已经使用 glm::project 应用了投影矩阵,这就是z' = ...公式的作用。所以你基本上在你的代码中应用了两次投影矩阵。

OpenGL 中的深度缓冲区值在窗口坐标中,它们在 [n,f] 范围内,其中 n 和 f 使用设置glDepthRange(n, f)(默认为 0 和 1)。您可以在规范中的 13.6.1 中阅读它。这些值与投影矩阵中使用的 zNear 和 zFar 值无关。

glm::project简单地假设这些默认值,并且由于它输出窗口坐标,这就是写入深度缓冲区的值。所以正确的代码很简单:

float zBufferValue = screenCoords.z;
于 2013-06-22T15:30:01.023 回答