没有 OpenGL 的重复 OpenGL 正交投影行为

Posted

技术标签:

【中文标题】没有 OpenGL 的重复 OpenGL 正交投影行为【英文标题】:Duplicate OpenGL orthographic projection behaviour without OpenGL 【发布时间】:2014-10-06 15:52:33 【问题描述】:

我在尝试在没有 OpenGL 的环境中复制 OpenGL 行为时遇到问题。

基本上我需要从我的程序创建的行列表中创建一个 SVG 文件。这些线条是使用立体投影创建的。

我确信这些线的计算是正确的,因为如果我尝试将它们与具有正交投影的 OpenGL 上下文一起使用并将结果保存到图像中,则图像是正确的。

当我在没有 OpenGL 的情况下使用完全相同的行时,问题就出现了。

我已经复制了 OpenGL 投影和视图矩阵,并像这样处理每个线点:

3D_output_point = projection_matrix * view_matrix * 3D_input_point

然后我像这样计算它的屏幕(SVG 文件)位置:

2D_point_x = (windowWidth / 2) * 3D_point_x + (windowWidth / 2)
2D_point_y = (windowHeight / 2) * 3D_point_y + (windowHeight / 2)

我这样计算正射投影矩阵:

float range = 700.0f;
float l, t, r, b, n, f;

l = -range;
r = range;
b = -range;
t = range;
n = -6000;
f = 8000;

matProj.SetValore(0, 0, 2.0f / (r - l));
matProj.SetValore(0, 1, 0.0f);
matProj.SetValore(0, 2, 0.0f);
matProj.SetValore(0, 3, 0.0f);

matProj.SetValore(1, 0, 0.0f);
matProj.SetValore(1, 1, 2.0f / (t - b));
matProj.SetValore(1, 2, 0.0f);
matProj.SetValore(1, 3, 0.0f);

matProj.SetValore(2, 0, 0.0f);
matProj.SetValore(2, 1, 0.0f);
matProj.SetValore(2, 2, (-1.0f) / (f - n));
matProj.SetValore(2, 3, 0.0f);

matProj.SetValore(3, 0, -(r + l) / (r - l));
matProj.SetValore(3, 1, -(t + b) / (t - b));
matProj.SetValore(3, 2, -n / (f - n));
matProj.SetValore(3, 3, 1.0f);

这样的视图矩阵:

CVettore position, lookAt, up;

position.AssegnaCoordinate(rtRay->m_pCam->Vp.x, rtRay->m_pCam->Vp.y, rtRay->m_pCam->Vp.z);
lookAt.AssegnaCoordinate(rtRay->m_pCam->Lp.x, rtRay->m_pCam->Lp.y, rtRay->m_pCam->Lp.z);
up.AssegnaCoordinate(rtRay->m_pCam->Up.x, rtRay->m_pCam->Up.y, rtRay->m_pCam->Up.z);

up[0] = -up[0];
up[1] = -up[1];
up[2] = -up[2];

CVettore zAxis, xAxis, yAxis;
float length, result1, result2, result3;

// zAxis = normal(lookAt - position)
zAxis[0] = lookAt[0] - position[0];
zAxis[1] = lookAt[1] - position[1];
zAxis[2] = lookAt[2] - position[2];
length = sqrt((zAxis[0] * zAxis[0]) + (zAxis[1] * zAxis[1]) + (zAxis[2] * zAxis[2]));
zAxis[0] = zAxis[0] / length;
zAxis[1] = zAxis[1] / length;
zAxis[2] = zAxis[2] / length;

// xAxis = normal(cross(up, zAxis))
xAxis[0] = (up[1] * zAxis[2]) - (up[2] * zAxis[1]);
xAxis[1] = (up[2] * zAxis[0]) - (up[0] * zAxis[2]);
xAxis[2] = (up[0] * zAxis[1]) - (up[1] * zAxis[0]);
length = sqrt((xAxis[0] * xAxis[0]) + (xAxis[1] * xAxis[1]) + (xAxis[2] * xAxis[2]));
xAxis[0] = xAxis[0] / length;
xAxis[1] = xAxis[1] / length;
xAxis[2] = xAxis[2] / length;

// yAxis = cross(zAxis, xAxis)
yAxis[0] = (zAxis[1] * xAxis[2]) - (zAxis[2] * xAxis[1]);
yAxis[1] = (zAxis[2] * xAxis[0]) - (zAxis[0] * xAxis[2]);
yAxis[2] = (zAxis[0] * xAxis[1]) - (zAxis[1] * xAxis[0]);

// -dot(xAxis, position)
result1 = ((xAxis[0] * position[0]) + (xAxis[1] * position[1]) + (xAxis[2] * position[2])) * -1.0f;

// -dot(yaxis, eye)
result2 = ((yAxis[0] * position[0]) + (yAxis[1] * position[1]) + (yAxis[2] * position[2])) * -1.0f;

// -dot(zaxis, eye)
result3 = ((zAxis[0] * position[0]) + (zAxis[1] * position[1]) + (zAxis[2] * position[2])) * -1.0f;

// Set the computed values in the view matrix.
matView.SetValore(0, 0, xAxis[0]);
matView.SetValore(0, 1, yAxis[0]);
matView.SetValore(0, 2, zAxis[0]);
matView.SetValore(0, 3, 0.0f);

matView.SetValore(1, 0, xAxis[1]);
matView.SetValore(1, 1, yAxis[1]);
matView.SetValore(1, 2, zAxis[1]);
matView.SetValore(1, 3, 0.0f);

matView.SetValore(2, 0, xAxis[2]);
matView.SetValore(2, 1, yAxis[2]);
matView.SetValore(2, 2, zAxis[2]);
matView.SetValore(2, 3, 0.0f);

matView.SetValore(3, 0, result1);
matView.SetValore(3, 1, result2);
matView.SetValore(3, 2, result3);
matView.SetValore(3, 3, 1.0f);

我从 OpenGL 和 SVG 输出得到的结果完全不同,但两天后我想不出一个解决方案。

这是 OpenGL 输出

这是我的 SVG 输出

如您所见,它的旋转不正确。

知道为什么吗?希望线点是一样的,矩阵也是一样的。


粘贴我正在创建的矩阵不起作用。我的意思是,我认为矩阵是错误的,因为 OpenGL 没有显示任何内容。 所以我尝试做相反的事情,我在 OpenGL 中创建了矩阵并将它们与我的代码一起使用。结果更好,但还不完美。

现在我认为我在将 3D 点映射到 2D 屏幕点时做错了,因为我得到的点在 Y 中是倒置的,而且我仍然有一些线不完全匹配。

这是我使用 OpenGL 矩阵和我之前将 3D 点映射到 2D 屏幕空间的方法(这是 SVG,而不是 OpenGL 渲染):


好的,这是我从 OpenGL 获得的视图矩阵的内容:

这是我从 OpenGL 得到的投影矩阵:

这是我用这些矩阵得到的结果,通过改变我的 2D 点 Y 坐标计算,就像 bofjas 说的:

似乎缺少一些旋转。我的相机在 X 轴和 Y 轴上都旋转了 30°,看起来它们的计算不正确。

现在我正在使用与 OpenGL 相同的矩阵。所以我认为当我将 3D 点映射到 2D 屏幕坐标时,我做了一些错误的计算。

【问题讨论】:

仅供参考:您的方法在投影变换后缺乏同质除法。这对于正交投影无关紧要,但对于透视投影的工作至关重要。至于您的 SVG 案例中的不良轮换,我将不得不对此进行调查。 顺便说一句:如果您将创建的矩阵加载到 OpenGL 中并尝试使用这些矩阵渲染图像会发生什么? glMatrixMode(...); glLoadMatrix(...) 如果您使用的是固定功能管道。 顺便说一句,您可以打印矩阵的内容。 在 OpenGL 中,左下角是 0,0。尝试切换到:2D_point_y = (windowHeight / 2) * -3D_point_y + (windowHeight / 2) 用于 Y 轴。至少在使用 OpenGL 矩阵时它是正确的 OpenGL 为恒定 X 的线生成精确的垂直线,为恒定 Y 的线生成倾斜线,而您的代码为恒定 X 的线生成倾斜的线,为恒定的 Y 线生成精确的水平线. 我猜这表明你的代码中有一个无意的 x-y 交换。 【参考方案1】:

您可以使用transform feedback 来使用OpenGL 管道计算线条的投影,而不是调试您自己的代码。您可以将它们捕获在内存缓冲区中,然后直接保存到 SVG,而不是在屏幕上将它们光栅化。设置这个有点复杂,取决于你的 OpenGL 代码路径的确切设置,但它可能是一个更简单的解决方案。

根据您自己的代码,看起来您要么在某处混合了 x 和 y 坐标,要么是行优先和列优先矩阵。

【讨论】:

【参考方案2】:

我以非常简单的方式解决了这个问题。因为当我使用 OpenGL 绘图时它正在工作,我刚刚在 OpenGL 中创建了矩阵,然后使用 glGet() 检索它们。使用这些矩阵一切正常。

【讨论】:

在您帖子的最后一部分中,您说“我正在使用与 OpenGL 相同的矩阵”,但您仍然遇到问题。如果不使用glGet 命令,您还能如何确定它们是相同的矩阵?【参考方案3】:

您正在寻找一种特殊版本的正交(斜)投影,称为等距投影。如果您想知道矩阵内部的内容,数学计算非常简单。看看Wikipedia

【讨论】:

【参考方案4】:

OpenGL在major列(与c++相反)加载矩阵。例如这个矩阵:

[1 ,2 ,3 ,4 ,
 5 ,6 ,7 ,8 ,
 9 ,10,11,12,
 13,14,15,16]

以这种方式加载到内存中:

|_1 _|
|_5 _|
|_9 _|
|_13_|
|_2 _|
.
.
.

所以我想你应该从 openGL 转置这些矩阵(如果你正在做的是行专业)

【讨论】:

以上是关于没有 OpenGL 的重复 OpenGL 正交投影行为的主要内容,如果未能解决你的问题,请参考以下文章

OpenGL - 正交投影矩阵,glViewport

OpenGL中的正交投影问题

OpenGL 3+,具有定向光的正交投影

opengl 的投影和正交矩阵

openGL - 正交投影矩阵

在 OpenGL 的正交投影中使用纹理