挑选雷是不准确的

时间:2012-11-21 00:30:19

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

我正在尝试通过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成为我的采光射线撞击地平面的点。 然而,当我尝试它时,我得到这样的东西: Error (未记录鼠标光标) 很难看到,因为地面没有纹理,但相机是倾斜的,就像大多数RTS游戏一样。 根据我的计算,我在Blender中对远程人类进行建模的可怜尝试标志着交叉点的发生点。

所以似乎viewdir之间的转换搞砸了,我的光线最终指向错误的方向。 计算出的位置与实际位置之间的差距越大,我越远离屏幕中心移动。

我发现了:

  • 高度和长度不准确。由于Windows为边界切割了几个像素,因此使用1006,728作为窗口分辨率会更准确。我想这可能会导致小的差异。
  • 如果我将fovy从45提高到78左右,我会获得相当准确的光线。所以也许我用作fovy的东西有问题。我明确地称glm::perspective(45.f, 1.38f, 0.1f, 500.f)(fovy,纵横比,fNear,fFar)。

所以这就是我迷失的地方。为了获得准确的射线,我该怎么做?

PS:我知道有一些函数和库已经实现了,但我试图远离这些东西用于学习目的。

1 个答案:

答案 0 :(得分:0)

这是使用深度缓冲信息执行光标到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;

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