c++ - 为什么在opengl中将gl_FragCoord.z与深度缓冲区一偏离?
问题描述
我一直在尝试寻找一种方法来读取特定鼠标坐标(x,y)的深度值。使用 opengl 4.x 在 win10 上一切正常,但不适用于 opengl es 3.x
我的方法:
- glReadPixels() 不适用于深度缓冲区的 openGL es
- 光线投射不适合,因为我使用的是大型地形模型
随后的方法就足够了,但不幸的是太不准确了,win10也是如此,但为什么呢?
#version 420 uniform vec2 screenXy; uniform vec2 screenSize; out vec4 fragColor; void main(void) { if((int(gl_FragCoord.x) == int(screenXy.x)) && ((int(screenSize.y) - int(gl_FragCoord.y)) == int(screenXy.y))) { fragColor.r = gl_FragCoord.z; } else { fragColor = vec4(1, 1, 1, 1.0); } }
我将鼠标 xy 坐标提交给片段着色器(screenXy)。如果单击的像素在行中,我将深度值写入颜色缓冲区。这可行,但值 gl_FragCoord.z 和深度缓冲区中的值并不完全相同(我知道深度缓冲区中的值是正确的)。虽然 gl_FragCoord.z 和深度缓冲区的值是浮动的,所以我认为是 32bit。
GLfloat zd; // from depth buffer
GLfloat zc[4]; // from color buffer
m_func->glReadPixels(xy.x(), m_pFbo->height() - xy.y(), 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &zd);
m_func->glReadPixels(xy.x(), m_pFbo->height() - xy.y(), 1, 1, GL_RGBA, GL_FLOAT, zc);
原因:
- 偏差通过内部类型转换发生,但在哪里?
- 因为 GL_DEPTH_TEST 是在 fragmentshader gl_FragCoord.z 不是最近的(离相机)之后执行的,而是保存在深度缓冲区中。因此,将 gl_FragCoord.z 保存在单独的 Frambuffer 中也是没有意义的,因为它不是正确的值。
也许有人可以帮我解决这个问题,因为我找不到任何其他解释?
这里有一些测量值:
zc 0.984314
zd 0.985363
zc 0.552941
zd 0.554653
zc 1 -> extremly critical
zd 0.999181
解决方案
由于 0.984314 * 255.0 正好是 251.0,我假设颜色平面的内部格式是GL_RGBA8
. 这意味着每个颜色通道有 1 个字节,并且zc
只能有 256 个不同的值,从 0.0 到 1.0,步长为 1/256。
如果您使用的 OpenGL ES 版本支持它,那么您可以更改渲染缓冲区存储的格式(例如GL_R32F
- 只有红色通道,但 32 位浮点)。
或者您可以将深度编码为 4 * 8 位颜色平面的 4 个通道:
vec4 PackDepth( in float depth )
{
depth *= (256.0*256.0*256.0 - 1.0) / (256.0*256.0*256.0);
vec4 encode = fract( depth * vec4(1.0, 256.0, 256.0*256.0, 256.0*256.0*256.0) );
return vec4( encode.xyz - encode.yzw / 256.0, encode.w ) + 1.0/512.0;
}
....
fragColor = PackDepth(gl_FragCoord.z);
您可以在读取值后对其进行解码:
GLfloat zc[4]; // from color buffer
m_func->glReadPixels(xy.x(), m_pFbo->height() - xy.y(), 1, 1, GL_RGBA, GL_FLOAT, zc);
float depth = zc[0] + zc[1]/256.0 + zc[2]/(256.0*256.0) + zc[3]/(256.0*256.0*256.0);
depth = depth * (255.0f/256.0f) * (256.0*256.0*256.0) / (256.0*256.0*256.0 - 1.0);
推荐阅读
- ios - RealityKit:在接地阴影下渲染对象
- django - 基于HTTP方法的Django rest框架non_atomic_requests
- c# - 方面未触发 CQRS
- actions-on-google - 拒绝帐户关联后奇怪的 Google 助理行为
- flutter - 使用 Flutter 中的 get_it 包导航出屏幕并返回时,StreamBuilder 中不会收到流中的数据
- sql - 用 SQL 中的平均值更新列
- python - 使用多个浏览器实例运行多线程 Selenium 自动化
- javascript - vue-router:为带有分页的过滤页面定义漂亮的 URL
- java - 列表视图上的搜索过滤器在单击时返回错误结果
- regex - 如何编写识别特定字母和最少数字的正则表达式