【问题标题】:Picking Ray is inaccuratePicking Ray 不准确
【发布时间】:2012-11-21 00:30:18
【问题描述】:

我正在尝试通过来自this website 的指令实现拾取射线。

现在我基本上只希望能够点击地面,命令我的小人向这个点走。 由于我的地平面是平面的、非旋转的和非平移的,所以当 y 达到 0 时,我必须找到我的拾取射线的 x 和 z 坐标。

到目前为止一切顺利,这就是我想出的:

//some constants
float HEIGHT = 768.f;
float LENGTH = 1024.f;
float fovy = 45.f;
float nearClip = 0.1f;

//mouse position on screen
float x = MouseX;
float y = HEIGHT - MouseY;

//GetView() returns the viewing direction, not the lookAt point.
glm::vec3 view = cam->GetView();
glm::normalize(view);

glm::vec3 h = glm::cross(view, glm::vec3(0,1,0) ); //cameraUp
glm::normalize(h);

glm::vec3 v = glm::cross(h, view);
glm::normalize(v);

// convert fovy to radians 
float rad = fovy * 3.14 / 180.f; 
float vLength = tan(rad/2) * nearClip; //nearClippingPlaneDistance
float hLength = vLength * (LENGTH/HEIGHT);

v *= vLength;
h *= hLength;

// translate mouse coordinates so that the origin lies in the center
// of the view port
x -= LENGTH / 2.f;
y -= HEIGHT / 2.f;

// scale mouse coordinates so that half the view port width and height
// becomes 1
x /= (LENGTH/2.f);
y /= (HEIGHT/2.f);

glm::vec3 cameraPos = cam->GetPosition();

// linear combination to compute intersection of picking ray with
// view port plane
glm::vec3 pos = cameraPos + (view*nearClip) + (h*x) + (v*y);

// compute direction of picking ray by subtracting intersection point
// with camera position
glm::vec3 dir = pos - cameraPos;

//Get intersection between ray and the ground plane
pos -= (dir * (pos.y/dir.y));

在这一点上,我希望pos 是我的拾取射线击中我的地平面的点。 然而,当我尝试它时,我得到了这样的东西: (未记录鼠标光标) 由于地面没有纹理,因此很难看清,但相机是倾斜的,就像大多数 RTS 游戏一样。 根据我的计算,我在 Blender 中模拟一个看起来很远的人的可怜尝试标志着交叉点发生。

所以看来viewdir 之间的转换在某处搞砸了,我的光线最终指向了错误的方向。 鼠标离屏幕中心越远,计算出的位置和实际位置之间的差距就会越大。

我发现:

  • 高度和长度不准确。由于 Windows 为边框切掉了几个像素,因此使用 1006,728 作为窗口分辨率会更准确。我想这可能会造成一些小的差异。
  • 如果我将 fovy 从 45 增加到大约 78,我会得到相当准确的光线。所以也许我用作fovy的东西有问题。我明确地打电话给glm::perspective(45.f, 1.38f, 0.1f, 500.f)(分别为fovy、纵横比、fNear、fFar)。

所以这就是我迷路的地方。为了获得准确的光线,我必须做什么?

PS:我知道有一些函数和库已经实现了这个,但为了学习目的,我尽量远离这些东西。

【问题讨论】:

  • “调试我的代码”这类问题几乎不适合 SO 格式。
  • @KromStern 这不像“哦,我的代码不起作用,让我们请 SO 为我修复它”。我试图让这个工作几个小时,并通过谷歌和 SO 没有找到对我有帮助的东西(部分原因是我的数学知识薄弱)。提供 SSCCE 也非常困难,因为所有相机/渲染/winapi 的东西对于拾取射线实际做某事都是必不可少的。而且因为我自己写的,它远不止几十行。老实说,我不知道如何在这里帮助自己,这就是我问的原因。谁知道——也许这只是算法中的一个小错误。
  • @KromStern PS:浏览 opengl 标记的问题,我觉得至少有三分之一的问题符合“调试我的代码”之类的问题。他们都说“这是我的代码,x 不起作用,怎么了?”。我的有什么不同?
  • 您的代码不起作用,您确实要求 SO 为您修复它。我建议你在谷歌上搜索一个工作教程并从中学习。

标签: c++ windows opengl picking ray-picking


【解决方案1】:

这是使用深度缓冲区信息将光标转换为 3D 的工作代码:

  glGetIntegerv(GL_VIEWPORT, @fViewport);
  glGetDoublev(GL_PROJECTION_MATRIX, @fProjection);
  glGetDoublev(GL_MODELVIEW_MATRIX, @fModelview);

  //fViewport already contains viewport offsets
  PosX := X;
  PosY := ScreenY - Y; //In OpenGL Y axis is inverted and starts from bottom

  glReadPixels(PosX, PosY, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, @vz);

  gluUnProject(PosX, PosY, vz, fModelview, fProjection, fViewport, @wx, @wy, @wz);
  XYZ.X := wx;
  XYZ.Y := wy;
  XYZ.Z := wz;

如果您只测试光线/平面相交,这是没有 DepthBuffer 的第二部分:

  gluUnProject(PosX, PosY, 0, fModelview, fProjection, fViewport, @x1, @y1, @z1); //Near
  gluUnProject(PosX, PosY, 1, fModelview, fProjection, fViewport, @x2, @y2, @z2); //Far

  //No intersection
  Result := False;
  XYZ.X := 0;
  XYZ.Y := 0;
  XYZ.Z := aZ;

  if z2 < z1 then
    SwapFloat(z1, z2);

  if (z1 <> z2) and InRange(aZ, z1, z2) then
  begin
    D := 1 - (aZ - z1) / (z2 - z1);
    XYZ.X := Lerp(x1, x2, D);
    XYZ.Y := Lerp(y1, y2, D);
    Result := True;
  end;

我发现它与你正在做的事情有很大不同,但也许这会更有意义。

【讨论】:

  • 好吧,正如我所说,如果可能的话,我想远离像 unproject 这样的函数。所以虽然这个解决方案可能有效,但它不是我想要的。
  • 你可以google gluUnproject源码来学习。没那么复杂。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-05-27
  • 2017-04-06
  • 2023-03-16
  • 2013-09-21
  • 2021-07-28
  • 2013-09-22
  • 1970-01-01
相关资源
最近更新 更多