“完成的方式”是使用齐次变换和坐标。您可以采用以下步骤来处理空间中的点:
- 使用模型矩阵将其相对于相机定位。
- 使用投影矩阵将其正交或透视投影。
- 应用视口变换将其放置在屏幕上。
这有点模糊,但我会尝试涵盖重要部分并留下一些给您。我假设您了解矩阵数学的基础知识 :)。
齐次向量、点、变换
在三维空间中,齐次点是形如[x,y,z,1]的列矩阵。最后一个分量是“w”,一个缩放因子,对于向量而言是0:这样可以使向量不能平移,这在数学上是正确的。我们不会讨论这个问题,我们只谈论点。
齐次变换是4x4矩阵,使用它们可以将平移表示为矩阵乘法,而不是加法,这对于您的显卡非常快速方便。同时,我们可以通过将它们相乘来表示连续的变换。我们通过执行变换*点来对点应用变换。
主要有3种齐次变换:
还有其他一些值得探索的变换,尤其是“look at”变换。但是,我只想简要列出一些列表和几个链接。连续应用于点的移动、缩放和旋转被称为模型变换矩阵,并将它们相对于相机放置在场景中。重要的是要意识到我们所做的类似于将物体围绕相机移动,而不是相反。
正交投影和透视投影
要将世界坐标转换为屏幕坐标,您需要首先使用投影矩阵,通常有两种类型:
- 正交投影,通常用于2D和CAD。
- 透视投影,适用于游戏和3D环境。
正交投影矩阵构建如下:
参数包括:
- Top: 可见空间顶部的Y坐标。
- Bottom: 可见空间底部的Y坐标。
- Left: 可见空间左侧的X坐标。
- Right: 可见空间右侧的X坐标。
我认为这很简单。您所建立的是一个将出现在屏幕上的空间区域,您可以对其进行剪裁。这里很简单,因为可见空间的区域是一个矩形。透视剪裁更加复杂,因为出现在屏幕上或视野体积的区域是一个截头锥体。
如果您在透视投影的维基百科上遇到了困难,这里有构建合适矩阵的代码,由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;
}
变量如下:
- fov: 视角,π/4 弧度是一个好的值。
- aspect: 高宽比。
- znear, zfar: 用于裁剪,我会忽略这些。
生成的矩阵是列主序的,在上面的代码中按如下方式索引:
0 4 8 12
1 5 9 13
2 6 10 14
3 7 11 15
视口变换、屏幕坐标
这两个转换都需要另一个矩阵将物体放置在屏幕坐标中,称为视口变换。在这里描述了它,我不会涉及它(它非常简单)。
因此,对于一个点p,我们将:
- 执行模型变换矩阵* p,结果为pm。
- 执行投影矩阵* pm,结果为pp。
- 剪裁pp以适应视图体积。
- 执行视口变换矩阵* pp,结果为ps:屏幕上的点。
摘要
我希望这大部分都包括在内了。上述内容存在漏洞,在某些地方含糊不清,请在下面发表任何问题。这个主题通常值得在教科书中撰写一整章,我已尽力概括这个过程,希望对您有所帮助!
我在上面链接到了这个,但我强烈建议您阅读并下载该二进制文件。它是一个很好的工具,可以进一步理解这些转换以及如何将点放在屏幕上:
http://www.songho.ca/opengl/gl_transform.html
关于实际工作,您需要实现一个4x4矩阵类进行同构变换,以及一个同构点类可以与其相乘应用变换(请记住,[x,y,z,1])。您需要按照上述描述和链接生成变换。一旦您理解了这个过程,它并不是很困难。祝你好运 :)。