光栅化:一种实际的实现
Posted geniushuai
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了光栅化:一种实际的实现相关的知识,希望对你有一定的参考价值。
快速复审
在上一章中,我们对光栅化渲染技术进行了高级概述。它可以分解为两个主要阶段:首先,将三角形的顶点投影到画布上,然后对三角形本身进行光栅化。在这种情况下,光栅化实际上意味着将三角形的形状“分解”成像素或光栅元素正方形; 这就是过去调用像素的方式。在本章中,我们将回顾第一步。这种方法在前两节课中已经介绍过了,这里不再赘述。如果您对透视投影背后的原理有任何疑问,请再次查看这些课程。但是,在本章中,我们将学习一些与投影相关的新技巧,这些技巧在我们学习透视投影矩阵时会很有用。我们将学习一种将投影顶点坐标从屏幕空间重新映射到 NDC 空间的新方法。我们还将详细了解 z 坐标在光栅化算法中的作用以及在投影阶段应如何处理。
请记住,如前一章所述,光栅化渲染技术的目标是解决可见性或隐藏表面问题,即确定 3D 对象的某些部分是可见的,哪些部分是隐藏的。
投影:我们要解决什么问题?
在光栅化算法的那个阶段,我们试图在这里解决什么?如上一章所述,光栅化的原理是查找图像中的像素是否与三角形重叠。为此,我们首先需要将三角形投影到画布上,然后将它们的坐标从屏幕空间转换为光栅空间。然后在同一空间中定义像素和三角形,这意味着可以比较它们各自的坐标(我们可以检查给定像素的坐标与三角形顶点的光栅空间坐标)。
因此,此阶段的目标是将构成三角形的顶点从相机空间转换为光栅空间。
投影顶点:注意 Z 坐标!
在前两节课中,我们提到当我们计算 3D 点的光栅坐标时,最终我们真正需要的是它的 x 和 y 坐标(图像中 3D 点的位置)。快速提醒一下,这些 2D 坐标是通过将相机空间中 3D 点的 x 和 y 坐标除以该点各自的 z 坐标(我们称为透视除法),然后重新映射生成的 2D 坐标获得的从屏幕空间到 NDC 空间,然后从 NDC 空间到光栅空间。请记住,由于图像*面位于*裁剪*面,我们还需要将 x 和 y 坐标乘以*裁剪*面。同样,我们在前两节课中详细解释了这个过程。
请注意,到目前为止,我们一直将屏幕空间中的点视为本质上的 2D 点(我们不需要在透视分割后使用点的 z 坐标)。不过,从现在开始,我们将在屏幕空间中声明点为 3D 点,并将它们的 z 坐标设置为相机空间点的 z 坐标,如下所示:
此时最好将投影点 z 坐标设置为原始点 z 坐标的倒数,正如您现在所知道的那样,它是负数。处理正 z 坐标将使以后的一切变得更简单(但这不是强制性的)。
需要跟踪相机空间中的顶点 z 坐标来解决可见性问题。看图 1 会更容易理解为什么。想象一下两个顶点 v1 和 v2,当它们投影到画布上时,它们具有相同的光栅坐标(如图 1 所示)。如果我们在 v2 之前投影 v1,那么当它实际上应该是 v1 时,v2 将在图像中可见(v1 显然在 v2 前面)。但是,如果我们将顶点的 z 坐标与它们的 2D 栅格坐标一起存储,我们可以使用这些坐标来定义离相机最*的点,而与投影顶点的顺序无关(如下面的代码片段所示) .
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
Vec3f v2screen;
v2screen.x = 附* * v2camera.x / -v2camera.z;
v2screen.y = 附* * v2camera.y / -v2camera.z;
v2screen.z = -v2cam.z;
Vec3f v1screen;
v1screen.x = 附* * v1camera.x / -v1camera.z;
v1screen.y = 附* * v1camera.y / -v1camera.z;
v1screen.z = -v1camera.z;
// 如果两个顶点在图像中的坐标相同,则比较它们的 z 坐标
if (v1screen.x == v2screen.x && v1screen.y == v2screen.y && v1screen.z < v2screen.z)
//如果 v1.z < v2.z 然后将 v1 存储在帧缓冲区中
....
我们想要渲染的是三角形而不是顶点。那么问题来了,我们刚刚学到的方法是如何应用于三角形的呢?简而言之,我们将使用三角形顶点坐标来查找三角形上像素重叠的点的位置(以及它的 z 坐标)。这个想法如图 2 所示。如果一个像素与两个或多个三角形重叠,我们应该能够计算像素重叠的三角形上的点的位置,并使用这些点的 z 坐标,就像我们对顶点,以了解哪个三角形最靠*相机。该方法将在第 4 章(The Depth Buffer.Finding the Depth Value by Interpolation)中详细介绍。
屏幕空间也是三维的
总而言之,从相机空间到屏幕空间(这是发生透视划分的过程),我们需要:
- 执行透视划分:即将相机空间 x 和 y 坐标中的点除以点 z 坐标。
磷s c r e n 。_ x =n e a r * Pc a m e r a _ X- Pc a m e r a _ z磷s c r e n 。_ 是的=n e a r * Pc a m e r a _ 是的- Pc a m e r a _ z磷sCreen.X=ne一个r*磷C一个米er一个.X-磷C一个米er一个.z磷sCreen.是的=ne一个r*磷C一个米er一个.是的-磷C一个米er一个.z
- 但也将投影点 z 坐标设置为原始点 z 坐标(相机空间中的点)。
磷s c r e n 。_ z= - Pc a m e r a _ z磷sCreen.z=-磷C一个米er一个.z
实际上,这意味着我们的投影点不再是 2D 点,而是实际上是 3D 点。或者换一种说法,屏幕空间不是二维的。Ed-Catmull在他的论文中写道:
您现在应该能够理解这句话了。该过程也如图 3 所示。首先,几何顶点在相机空间(顶部图像)中定义。然后,每个顶点经历一个透视划分。也就是顶点的 x 和 y 坐标除以其 z 坐标,但如前所述,我们还将生成的投影点 z 坐标设置为原始顶点 z 坐标的倒数。顺便说一下,这可以推断出屏幕空间坐标系的 z 轴方向的变化。如您所见,z 轴现在指向内而不是外(图 3 中的图像)。但最需要注意的是,生成的对象是原始对象的变形版本,但仍然是三维对象。此外,Ed-Catmull 在写“一个 透视 投影 , 然后 一个 正交 投影. 如果您不清楚透视和正交投影之间的区别,请不要担心。这是下一课的主题。但是,请尽量记住这个观察结果,因为它稍后会派上用场。
将屏幕空间坐标重新映射到 NDC 空间
在前两节课中,我们解释了一旦进入屏幕空间,投影点的 x 和 y 坐标需要重新映射到 NDC 空间。在之前的课程中,我们还解释了在 NDC 空间中,画布上的点的 x 和 y 坐标包含在 [0,1] 范围内。但在 GPU 世界中,NDC 空间中的坐标包含在 [-1,1] 范围内。可悲的是,这又是我们需要处理的这些约定之一。我们本可以保留约定 [0,1],但由于 GPU 是光栅化的参考,因此最好坚持使用 GPU 世界中定义该术语的方式。
因此,一旦点已经从相机空间转换到屏幕空间,下一步就是将它们分别从 x 和 y 坐标的范围 [l,r] 和 [b,t] 重新映射到范围 [- 1,1]。这里的术语 l, r, b, t 表示画布的左、右、下和上坐标。通过重新排列这些项,我们可以很容易地找到一个执行我们想要的重新映射的方程:
这里的 x 是屏幕空间中 3D 点的 x 坐标(请记住,从现在开始,我们将假设屏幕空间中的点是三维的,如上所述)。如果我们从方程中去掉 l 项,我们得到:
通过将所有项除以 (rl) 我们得到:
我们现在可以在等式中间展开项:
我们现在可以将所有项乘以 2:
我们现在从所有术语中删除 1:
如果我们开发这些术语并重新组合它们,我们最终会得到:
这是一个非常重要的方程,因为公式中间的红色和绿色项将成为透视投影矩阵的系数。我们将在下一课中学习这个矩阵。但是现在,我们将只应用这个等式将屏幕空间中一个点的 x 坐标重新映射到 NDC 空间(当在 NDC 空间中定义时,位于画布上的任何点的坐标都包含在 [-1.1] 范围内) . 如果我们对 y 坐标应用相同的推理,我们会得到:
把事情放在一起
在本课结束时,我们现在可以执行光栅化算法的第一阶段,您可以将其分解为两个步骤:
- 将相机空间中的一个点转换为屏幕空间。它本质上将一个点投影到画布上,但请记住,我们还需要存储原始点的 z 坐标。屏幕空间中的点是树维的,z 坐标将有助于稍后解决可见性问题。
磷s c r e n 。_ x =n e a r * Pc a m e r a _ X- Pc a m e r a _ z磷s c r e n 。_ 是的=n e a r * Pc a m e r a _ 是的- Pc a m e r a _ z磷s c r e n 。_ z= - Pc a m e r a _ z磷sCreen.X=ne一个r*磷C一个米er一个.X-磷C一个米er一个.z磷sCreen.是的=ne一个r*磷C一个米er一个.是的-磷C一个米er一个.z磷sCreen.z=-磷C一个米er一个.z
- 然后,我们使用以下公式将屏幕空间中这些点的 x 和 y 坐标转换为 NDC 空间:
− 1 <2 ×( r - l )-r + l( r - l )< 1− 1 <2年( t - b )-t + b( t - b )< 1-1<2X(r-l)-r+l(r-l)<1-1<2是的(吨-b)-吨+b(吨-b)<1其中 l, r, b, t 表示画布的左、右、下和上坐标。
从那里,将坐标转换为栅格空间非常简单。我们只需要将 NDC 空间中的 x 和 y 坐标重新映射到范围 [0,1] 并分别将结果数乘以图像的宽度和高度(不要忘记在光栅空间中 y 轴向下而在 NDC 空间中它会上升。因此我们需要在重新映射过程中改变 y 的方向)。在代码中我们得到:
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
// 指向相机空间
Vec3f pCamera;
worldToCamera.multVecMatrix(pWorld, pCamera);
// 转换为屏幕空间
Vec2f pScreen;
pScreen.x = nearClippingPlane * pCamera.x / -pCamera.z;
pScreen.y = nearClippingPlane * pCamera.y / -pCamera.z;
// 现在将点从屏幕空间转换为 NDC 空间(范围 [-1,1])
Vec2f pNDC;
pNDC.x = 2 * pScreen.x / (r - l) - (r + l) / (r - l);
pNDC.y = 2 * pScreen.y / (t - b) - (t + b) / (t - b);
// 转换为光栅空间并将 z 坐标设置为 -pCamera.z
Vec3f pRaster;
pRaster.x = (pScreen.x + 1) / 2 * imageWidth;
// 在栅格空间中,y 向下,因此反转方向
pRaster.y = (1 - pScreen.y) / 2 * imageHeight;
// 存储点相机空间 z 坐标(作为正值)
pRaster.z = -pCamera.z;
请注意,光栅空间中的点或顶点的坐标在这里仍然定义为浮点数,而不是整数(像素坐标就是这种情况)。
下一步是什么?
我们现在已经将三角形投影到画布上,并将这些投影顶点转换为光栅空间。三角形的顶点和像素都生活在同一个坐标系中。我们现在准备遍历图像中的所有像素,并使用一种技术来确定它们是否与三角形重叠。这是下一章的主题。
参考连接:
ios怎么下植物大战僵尸1:http://www.pvzbaike.com/ios-zen-me-xia-zhi-wu-da-zhan-jiang-shi-1.html
Rasterization: a Practical Implementation:https://www.scratchapixel.com/lessons/3d-basic-rendering/rasterization-practical-implementation/projection-stage
以上是关于光栅化:一种实际的实现的主要内容,如果未能解决你的问题,请参考以下文章