为什么在视口边缘附近单击时我的相机光线有偏移?
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 的见解(应该相同):
使用本地鼠标位置(OnMouseMove
)
将鼠标位置转换为 OpenGL 位置
注意鼠标 y
轴与 OpenGL 方向相反,(0,0)
位于屏幕中央而不是左上角。并且范围通常是 <-1.0,+1.0>
从我看到的你没有反转 y
并且范围 <0.0,1.0>
如果我没看错的话...
另请注意,如果您的表单中有面板,那么您还应该检查您的 glViewbox
设置,因为 VCL 有时会错误地计算 ClientWidth/Height,您需要像 - Panel1->Width;
...
小心你的射线来源
在透视图中,它必须从相机焦点开始。根据您的投影矩阵,它可能会移动 znear
(焦距)。
如果不更正,这将在视图边缘产生更大的错误,所以我敢打赌这是导致问题的原因。
注意深度缓冲精度
您可以调整视角,以便 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);
我正在 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 的见解(应该相同):
使用本地鼠标位置(
OnMouseMove
)将鼠标位置转换为 OpenGL 位置
注意鼠标
y
轴与 OpenGL 方向相反,(0,0)
位于屏幕中央而不是左上角。并且范围通常是<-1.0,+1.0>
从我看到的你没有反转y
并且范围<0.0,1.0>
如果我没看错的话...另请注意,如果您的表单中有面板,那么您还应该检查您的
glViewbox
设置,因为 VCL 有时会错误地计算 ClientWidth/Height,您需要像- Panel1->Width;
...小心你的射线来源
在透视图中,它必须从相机焦点开始。根据您的投影矩阵,它可能会移动
znear
(焦距)。如果不更正,这将在视图边缘产生更大的错误,所以我敢打赌这是导致问题的原因。
注意深度缓冲精度
您可以调整视角,以便
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);