OpenGL:glReadPixels 对于非投影不准确
OpenGL: glReadPixels inaccurate for unprojection
我有自己的 unproject
函数来执行屏幕点的反向投影。代码如下(用OpenTK编写):
public static Vector3 UnProject(Point screenLocation, float depth)
{
int[] viewport = GetViewport();
Vector4 pos = new Vector4();
// Map x and y from window coordinates, map to range -1 to 1
pos.X = (screenLocation.X - viewport[0]) / (float)viewport[2] * 2.0f - 1.0f;
pos.Y = 1 - (screenLocation.Y - viewport[1]) / (float)viewport[3] * 2.0f;
pos.Z = depth * 2.0f - 1.0f;
pos.W = 1.0f;
Vector4 pos2 = Vector4.Transform(pos, Matrix4.Invert(GetModelViewMatrix() * GetProjectionMatrix()));
Vector3 pos_out = new Vector3(pos2.X, pos2.Y, pos2.Z);
return pos_out / pos2.W;
}
基本上,您会为我的函数提供所需的非投影深度,它会为您提供相应的屏幕点世界坐标。假设这可以正常工作(我 99% 确定它确实如此),我在将屏幕点转换为世界坐标时遇到了问题。这种非投影适用于拾取:我会调用我的 unproject
函数两次(一次深度 = 0,另一次深度 = 1)以将屏幕点转换为射线。我执行 ray/triangle 相交以确定哪个对象与射线相交,并基于此执行拾取(非常准确)。
另外一个操作(暂且称之为操作X),我只需要知道屏幕点的世界坐标(假设鼠标光标在屏幕上的一个对象上)。为此,我使用 glReadPixel
函数获取光标下的深度。问题是我感觉读取深度缓冲区得到的Z值有点偏差。如果我计算与光线投射的交集,我会得到准确的结果,但这对于操作 X 是不可行的,因为每次触发 MouseMoved
时都需要执行操作 X。
为了证明准确性不足,这是我获得的两个数字:
glReadPixel + Unprojection 产生 (0.886105343709181, 0.12422376198582, 0.998496665566841) 作为光标下的世界坐标。
光线投射 + 相交产生 (0.885407337013061, 0.124174778008613, 1) 作为光标下的世界坐标。
Z 值中的这个 0.0015 误差对于操作 X 来说太大了(因为它对小数字非常敏感)。
glReadPixels
有什么问题我应该知道吗?发生这种情况是因为 glReadPixels
只能读取 float
值吗?
我不认为 glReadPixels
是罪魁祸首。我认为 Z 缓冲区精度是问题所在。默认情况下,您通常有一个 24 位定点深度缓冲区。如果您使用 32 位浮点深度缓冲区可能会有所帮助,但您可能需要一个 FBO。
我有自己的 unproject
函数来执行屏幕点的反向投影。代码如下(用OpenTK编写):
public static Vector3 UnProject(Point screenLocation, float depth)
{
int[] viewport = GetViewport();
Vector4 pos = new Vector4();
// Map x and y from window coordinates, map to range -1 to 1
pos.X = (screenLocation.X - viewport[0]) / (float)viewport[2] * 2.0f - 1.0f;
pos.Y = 1 - (screenLocation.Y - viewport[1]) / (float)viewport[3] * 2.0f;
pos.Z = depth * 2.0f - 1.0f;
pos.W = 1.0f;
Vector4 pos2 = Vector4.Transform(pos, Matrix4.Invert(GetModelViewMatrix() * GetProjectionMatrix()));
Vector3 pos_out = new Vector3(pos2.X, pos2.Y, pos2.Z);
return pos_out / pos2.W;
}
基本上,您会为我的函数提供所需的非投影深度,它会为您提供相应的屏幕点世界坐标。假设这可以正常工作(我 99% 确定它确实如此),我在将屏幕点转换为世界坐标时遇到了问题。这种非投影适用于拾取:我会调用我的 unproject
函数两次(一次深度 = 0,另一次深度 = 1)以将屏幕点转换为射线。我执行 ray/triangle 相交以确定哪个对象与射线相交,并基于此执行拾取(非常准确)。
另外一个操作(暂且称之为操作X),我只需要知道屏幕点的世界坐标(假设鼠标光标在屏幕上的一个对象上)。为此,我使用 glReadPixel
函数获取光标下的深度。问题是我感觉读取深度缓冲区得到的Z值有点偏差。如果我计算与光线投射的交集,我会得到准确的结果,但这对于操作 X 是不可行的,因为每次触发 MouseMoved
时都需要执行操作 X。
为了证明准确性不足,这是我获得的两个数字:
glReadPixel + Unprojection 产生 (0.886105343709181, 0.12422376198582, 0.998496665566841) 作为光标下的世界坐标。
光线投射 + 相交产生 (0.885407337013061, 0.124174778008613, 1) 作为光标下的世界坐标。
Z 值中的这个 0.0015 误差对于操作 X 来说太大了(因为它对小数字非常敏感)。
glReadPixels
有什么问题我应该知道吗?发生这种情况是因为 glReadPixels
只能读取 float
值吗?
我不认为 glReadPixels
是罪魁祸首。我认为 Z 缓冲区精度是问题所在。默认情况下,您通常有一个 24 位定点深度缓冲区。如果您使用 32 位浮点深度缓冲区可能会有所帮助,但您可能需要一个 FBO。