假设我有一个如下所示的数据结构:
Camera {
double x, y, z
/** ideally the camera angle is positioned to aim at the 0,0,0 point */
double angleX, angleY, angleZ;
}
SomePointIn3DSpace {
double x, y, z
}
ScreenData {
/** Convert from some point 3d space to 2d space, end up with x, y */
int x_screenPositionOfPt, y_screenPositionOfPt
double zFar = 100;
int width=640, height=480
}
...
如果没有屏幕剪辑或其他任何东西,我将如何计算给定空间中某个 3d 点的某个点的屏幕 x,y 位置。我想将该 3d 点投影到 2d 屏幕上。
Camera.x = 0
Camera.y = 10;
Camera.z = -10;
/** ideally, I want the camera to point at the ground at 3d space 0,0,0 */
Camera.angleX = ???;
Camera.angleY = ????
Camera.angleZ = ????;
SomePointIn3DSpace.x = 5;
SomePointIn3DSpace.y = 5;
SomePointIn3DSpace.z = 5;
ScreenData.x 和 y 是空间中 3d 点的屏幕 x 位置。我如何计算这些值?
我可以使用这里找到的方程,但我不明白屏幕宽度/高度是如何起作用的。另外,我在 wiki 条目中不明白观看者的位置与相机位置的关系。
http://en.wikipedia.org/wiki/3D_projection
最佳答案
“完成的方式”是使用同构变换和坐标。你在空间中取一个点,然后:
这变得非常模糊,但我会尝试涵盖重要的部分,并将其中的一些留给您。我假设您了解矩阵数学的基础知识:)。
齐次 vector 、点、变换
在 3D 中,齐次点将是 [x, y, z, 1] 形式的列矩阵。最后一个组件是“w”,一个缩放因子,对于 vector 来说,它是 0:这会导致你无法转换 vector ,这在数学上是正确的。我们不会去那里,我们在讨论要点。
齐次变换是 4x4 矩阵,使用它们是因为它们允许将转换表示为矩阵乘法,而不是加法,这对您的视频卡来说既好又快。也很方便,因为我们可以通过将它们相乘来表示连续的变换。我们通过执行transformation * point 对点进行变换。
有 3 个主要的齐次变换:
还有其他值得探索的,特别是“观察”转换。但是,我只想提供一个简短的列表和一些链接。应用于点的移动、缩放和旋转的连续应用统称为模型变换矩阵,并将它们放置在场景中,相对于相机。重要的是要意识到我们所做的类似于在相机周围移动物体,而不是相反。
正交和透视
要将世界坐标转换为屏幕坐标,您首先要使用投影矩阵,通常有两种形式:
正交投影矩阵的构造如下:
其中参数包括:
我认为这很简单。您建立的是一个将出现在屏幕上的空间区域,您可以对其进行剪辑。这里很简单,因为可见空间区域是一个矩形。透视剪裁更复杂,因为出现在屏幕或观看体积上的区域是 frustrum .
如果您对透视投影的维基百科有困难,这是构建合适矩阵的代码,courtesy of geeks3D
void BuildPerspProjMat(float *m, float fov, float aspect,
float znear, float zfar)
{
float xymax = znear * tan(fov * PI_OVER_360);
float ymin = -xymax;
float xmin = -xymax;
float width = xymax - xmin;
float height = xymax - ymin;
float depth = zfar - znear;
float q = -(zfar + znear) / depth;
float qn = -2 * (zfar * znear) / depth;
float w = 2 * znear / width;
w = w / aspect;
float h = 2 * znear / height;
m[0] = w;
m[1] = 0;
m[2] = 0;
m[3] = 0;
m[4] = 0;
m[5] = h;
m[6] = 0;
m[7] = 0;
m[8] = 0;
m[9] = 0;
m[10] = q;
m[11] = -1;
m[12] = 0;
m[13] = 0;
m[14] = qn;
m[15] = 0;
}
变量是:
生成的矩阵是列主矩阵,在上面的代码中索引如下:
0 4 8 12
1 5 9 13
2 6 10 14
3 7 11 15
视口(viewport)变换,屏幕坐标
这两种变换都需要另一个矩阵矩阵来将事物置于屏幕坐标中,称为视口(viewport)变换。 That's described here, I won't cover it (it's dead simple) .
因此,对于点 p,我们将:
摘要
我希望能涵盖大部分内容。上面有漏洞,有些地方含糊不清,有什么问题可以在下面留言。这个主题通常值得一整章教科书,我已经尽我所能提炼这个过程,希望对你有利!
我在上面链接到了这个,但我强烈建议你阅读这个,并下载二进制文件。这是一个很好的工具,可以进一步了解这些转换以及它如何在屏幕上获得点数:
http://www.songho.ca/opengl/gl_transform.html
就实际工作而言,您需要为齐次变换实现一个 4x4 矩阵类以及一个齐次点类,您可以将其与它相乘以应用变换(请记住,[x, y, z, 1])。您需要按照上述和链接中的说明生成转换。一旦你理解了程序,这并不是那么困难。祝你好运:)。
关于c# - 基本渲染 3D 透视投影到带摄像头的 2D 屏幕(不带 opengl),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8633034/