我最近设置了一个使用OpenGL(通过C#Wrapper Library OpenTK)的项目,该项目应该执行以下操作:
最后一步(光线拾取)在我的3D预览(GLControl)上看起来没问题,但返回无效结果,如Vector3d(1,86460186949617; -45,4086124979203; -45,0387025610247)。我不知道为什么会这样!
我使用以下代码设置我的视口:
this.RenderingControl.MakeCurrent();
int w = RenderingControl.Width;
int h = RenderingControl.Height;
// Use all of the glControl painting area
GL.Viewport(0, 0, w, h);
GL.MatrixMode(MatrixMode.Projection);
GL.LoadIdentity();
Matrix4 p = Matrix4.CreatePerspectiveFieldOfView(MathHelper.PiOver4, w / (float)h, 0.1f, 64.0f);
GL.LoadMatrix(ref p);
我使用此方法进行反投影:
/// <summary>
/// This methods maps screen coordinates to viewport coordinates.
/// </summary>
/// <param name="screen"></param>
/// <param name="view"></param>
/// <param name="projection"></param>
/// <param name="view_port"></param>
/// <returns></returns>
private Vector3d UnProject(Vector3d screen, Matrix4d view, Matrix4d projection, int[] view_port)
{
Vector4d pos = new Vector4d();
// Map x and y from window coordinates, map to range -1 to 1
pos.X = (screen.X - (float)view_port[0]) / (float)view_port[2] * 2.0f - 1.0f;
pos.Y = (screen.Y - (float)view_port[1]) / (float)view_port[3] * 2.0f - 1.0f;
pos.Z = screen.Z * 2.0f - 1.0f;
pos.W = 1.0f;
Vector4d pos2 = Vector4d.Transform(pos, Matrix4d.Invert(Matrix4d.Mult(view, projection)));
Vector3d pos_out = new Vector3d(pos2.X, pos2.Y, pos2.Z);
return pos_out / pos2.W;
}
我使用此代码定位我的相机(包括旋转)并进行光线拾取:
// Clear buffers
GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
// Apply camera
GL.MatrixMode(MatrixMode.Modelview);
Matrix4d mv = Matrix4d.LookAt(EyePosition, Vector3d.Zero, Vector3d.UnitY);
GL.LoadMatrix(ref mv);
GL.Translate(0, 0, ZoomFactor);
// Rotation animation
if (RotationAnimationActive)
{
CameraRotX += 0.05f;
}
if (CameraRotX >= 360)
{
CameraRotX = 0;
}
GL.Rotate(CameraRotX, Vector3.UnitY);
GL.Rotate(CameraRotY, Vector3.UnitX);
// Apply useful rotation
GL.Rotate(50, 90, 30, 0f);
// Draw Axes
drawAxes();
// Draw vertices of my 3d objects ...
// Picking Test
int x = MouseX;
int y = MouseY;
int[] viewport = new int[4];
Matrix4d modelviewMatrix, projectionMatrix;
GL.GetDouble(GetPName.ModelviewMatrix, out modelviewMatrix);
GL.GetDouble(GetPName.ProjectionMatrix, out projectionMatrix);
GL.GetInteger(GetPName.Viewport, viewport);
// get depth of clicked pixel
float[] t = new float[1];
GL.ReadPixels(x, RenderingControl.Height - y, 1, 1, OpenTK.Graphics.OpenGL.PixelFormat.DepthComponent, PixelType.Float, t);
var res = UnProject(new Vector3d(x, viewport[3] - y, t[0]), modelviewMatrix, projectionMatrix, viewport);
GL.Begin(BeginMode.Lines);
GL.Color3(Color.Yellow);
GL.Vertex3(0, 0, 0);
GL.Vertex3(res);
Debug.WriteLine(res.ToString());
GL.End();
我从我的光线选择器中得到以下结果:
此向量在附加的屏幕截图中显示为黄线。
为什么Y和Z位置不在-1 / + 1的范围内?这些值如-45来自何处以及为什么在屏幕上正确渲染光线?
如果你只有一个关于什么可能被打破的提示我也很感谢你的回复!
截图:
答案 0 :(得分:2)
如果您将从屏幕到世界的变换分解为单个矩阵,则打印出M,V和P矩阵的反转,并从屏幕打印出每个(矩阵逆)*(点)计算的中间结果对于世界/模型,那么我认为你会看到问题。或者至少你会发现使用M-V-P矩阵的逆矩阵然后直观地掌握解决方案存在问题。或者只是阅读下面的步骤列表,看看是否有帮助。
这是我用过的方法:
(*我使用的框架允许将3D矢量乘以4x4矩阵,因为它将3D矢量视为4D矢量。如果需要,我可以在以后更清楚地说明这一点,但我希望这一点相当清楚。 )
这对我有用。这组步骤也适用于Ortho投影,但是对于Ortho,你可以作弊并编写更简单的代码,因为投影矩阵不是傻瓜。
我写这篇文章已经很晚了,我可能误解了你的问题。我可能也误解了你的代码,因为我使用了不同的UI框架。但是我知道OpenGL的光线投射是如何加剧的,所以我希望至少我写的一些内容是有用的,并且我可以因此减轻一些人类的痛苦。
Postscript。说到痛苦:我找到了很多论坛帖子和博客页面,为OpenGL解决了光线投射问题,但大多数帖子都是从以下的一些变体开始的:“首先,你必须知道X “[其中X无需知道];或者“查看unproject函数[在存储库Y中的库X中,您需要客户端应用程序Z ... ..]”;或者是我最喜欢的:“回顾一下关于线性代数的教科书。”
当您需要调试光线投射时,不得不翻阅OpenGL渲染管道或OpenGL转换conga线的另一个描述 - 一个常见的问题 - 就像当您发现自己的时候必须听一听液压系统讲座刹车踏板不起作用。