为什么在视口边缘附近单击时我的相机光线有偏移?

Why does my camera ray have an offset when clicking near the edges of the viewport?

我正在 OpenGL 中处理一个 3D 项目。挑选对象是我要担心的事情,但现在我无法让主要的事情工作。这是使用鼠标从我的相机投射光线。我一直在挖掘太多的例子,包括没有“unproject”的光线投射,但它们确实给了我同样的结果!他们快要工作了,现在我几乎被困住了,不知道该怎么办。

考虑一下我最后选择的方法:

function CCamera.GetRay(x: Single; y: Single;) : TVector3;
var
  mouseX: single;
  mouseY: single;
  invVP: TMatrix4;
  screenPos: TVector4;
  worldPos: TVector4;
  worldPos3: TVector3;
  dir: TVector3;
begin

  // NDC
  mouseX := x / (fViewportWidth * 0.5) - 1.0;
  mouseY := y / (fViewportHeight * 0.5) - 1.0;

  invVP := mView * mProjection;
  // invVP.SetInversed; // Inversed doesn't even cast the ray close to where I want it cast at.
  // mProjection * mView would also generate behavior that isn't close to what I want.

  screenPos.Init(mouseX, -mouseY, 1.0, 1.0);
  worldPos := invVP * screenPos;
  worldPos3.Init(worldPos.X, worldPos.Y, worldPos.Z);

  dir := worldPos3.Normalize();

  Exit(dir);
end;

从字面上看。每一个。单身的。示例给了我相同的结果!这是我会坚持的。

然后,我会这样确定相机光线的结束位置:

更新代码:

procedure TOpenGLControl.OnMouseDown(Sender: TObject; Button: 
TMouseButton; Shift: TShiftState; X: Integer; Y: Integer);
var
  ray_start: TVector3;
  ray_end: TVector3;
  ray_dir: TVector3;
begin

    ray_start := GetCamera.GetPosition(); // Position of the camera
    ray_dir := GetCamera.GetRay(X, Y); // Pass 2D mouse coordinates
    ray_end := ray_start + ray_dir * 50.0; // Max distance of 50 units

    Invalidate();
end;

对数学函数和属性使用 Neslib.FastMath。 所以,基本上,发生的事情是我点击离世界中心(三角形所在的位置)越远,光线就会越靠近中心。检查图像。

不是 Delphi 编码员,但这里有一些来自 BCB 和 VCL 的见解(应该相同):

  1. 使用本地鼠标位置(OnMouseMove)

  2. 将鼠标位置转换为 OpenGL 位置

    注意鼠标 y 轴与 OpenGL 方向相反,(0,0) 位于屏幕中央而不是左上角。并且范围通常是 <-1.0,+1.0> 从我看到的你没有反转 y 并且范围 <0.0,1.0> 如果我没看错的话...

    另请注意,如果您的表单中有面板,那么您还应该检查您的 glViewbox 设置,因为 VCL 有时会错误地计算 ClientWidth/Height,您需要像 - Panel1->Width; ...

  3. 小心你的射线来源

    在透视图中,它必须从相机焦点开始。根据您的投影矩阵,它可能会移动 znear(焦距)。

    如果不更正,这将在视图边缘产生更大的错误,所以我敢打赌这是导致问题的原因。

  4. 注意深度缓冲精度

    您可以调整视角,以便 zfar/znear 匹配您的深度缓冲区精度。

    如果不够或不可能使用

看看这些:

  • depth buffer got by glReadPixels is always 1

解决方案!这是新的 unproject 方法。它是 100% 准确的。我设法把它们放在一起。奇怪。

pos.Init(0.0, 0.0, 0.0, 0.0);
pos.X := (x - 0.0) / fViewportWidth * 2.0 - 1.0;
pos.Y := 1 - (y - 0.0) / fViewportHeight * 2.0;
pos.Z := z * 2.0 - 1.0;
pos.W := 1;

mat := mView * mProjection;
mat.SetInversed();

pos2 := pos * mat;

pos_out.Init(pos2.X, pos2.Y, pos2.Z);

Exit(pos_out / pos2.W);