opengl 为什么在视口边缘附近单击时摄影机光线会有偏移?

whlutmcx  于 2022-11-04  发布在  其他
关注(0)|答案(2)|浏览(142)

我正在用OpenGL做一个3D项目。拾取对象是我要担心的事情,但现在我不能让主要的事情工作。那就是用鼠标从我的相机投射光线。我已经挖掘了太多的例子,包括没有“取消投影”的光线投射,但它们给予我的结果几乎是一样的!它们几乎是工作的,现在我几乎被卡住了,不知道该怎么做。
考虑一下我最后选择的方法:

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作为数学函数和属性。所以,基本上,发生的事情是,我从世界的中心(三角形所在的地方)点击得越远,光线就越靠近中心。检查图像。

p1tboqfb

p1tboqfb1#

不是一个 Delphi 编码器,但这里有一些来自BCB和VCL的见解(应该是相同的):
1.使用本地鼠标位置(OnMouseMove
1.将鼠标位置转换为OpenGL位置
注意鼠标y轴是在相反的方向OpenGL和(0,0)是在屏幕的中心,而不是在左上角。和范围通常是<-1.0,+1.0>从我所看到的,你不是反转y,并有范围<0.0,1.0>,如果我看到它的权利...
如果你的表单中有面板,那么你也应该检查你的glViewbox设置,因为VCL有时会错误地计算ClientWidth/Height,你需要减去缺少的东西,比如- Panel1->Width;...
1.小心你的雷源
在透视图中,它必须从相机焦点开始。根据您的投影矩阵,它可能会移动znear(焦距)。
如果不正确,这将造成更大的错误边缘的看法,所以我敢打赌,这是你的问题的原因。
1.注意深度缓冲区精度
您可以调整透视图,使zfar/znear与深度缓冲区精度相匹配。
如果数量不足或无法使用,请使用linear depth buffer
看看这些:

pbossiut

pbossiut2#

解决方案!这是新的取消项目的方法。它是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);

相关问题