GPU:将浮点顶点坐标转换为定点。如何?
GPU: converting floating-point vertex coordinates to fixed-point. How To?
我主要是想了解 GPU 在光栅化过程中将浮动顶点坐标转换为定点数时的工作原理。
我读了这个excellent article,它已经解释了很多东西,但它也让我感到困惑。所以文章解释说,因为我们使用了32位整数和如下形式(a - b)*(c - d) - (e - f)*(g - h)
的边缘函数,我们被限制在[-16384,16383]范围内。我明白我们是如何得到这个数字的。这是我的问题:
- 首先,这表明顶点坐标可以为负数。但是我不明白的是,在那个阶段技术上顶点坐标是光栅 space,并且之前应该已经剪裁了所有三角形。因此,从技术上讲,x 坐标的顶点坐标应该只在 [0,图像宽度] 范围内,而 y 坐标的顶点坐标应该在 [0,图像高度] 范围内?那为什么坐标是负数呢?
- 所以作者解释范围太有限了[-16384,16383]。实际上,如果您的宽度为 2048 像素并使用 256 子像素,则 x 中的点坐标需要为 4194048。因此您会溢出。作者继续解释他们如何在 GPU 上解决这个问题,但我就是不明白。如果有人也可以解释它是如何在 GPU 上实际完成的,那就太好了。
- First, this suggests that vertex coordinates can be negative. However what I don't understand is that technically at that stage vertex coordinates are in raster space, and all triangles should have been clipped before. Thus technically there should only be vertex coordinates in the range [0, image width] for the x-coordinate and [0, image height] for the y-coordinate? So why are coordinates negative?
简短的回答是,虽然三角形已被裁剪,但它们尚未裁剪到视口(0,0 - 图像宽度、图像高度)。相反,它们被裁剪到 guard-band 裁剪区域,这是一个围绕视口的较大矩形。视口外但在保护带裁剪区域内的顶点坐标可以具有负坐标。
有(至少)三种类型的三角形裁剪。第一个是 "analytic clipping",这是当你计算三角形边缘与保护带剪辑区域边缘的交点(如果它们重叠),然后在这些点处切掉三角形并将其剩余部分细分为更小的三角形三角形,每个三角形现在都在剪辑区域内。第二种类型是当三角形边界框被裁剪到视口以找到像素范围以在光栅化时迭代(注意这不会改变三角形顶点坐标)。第三种类型是文章中描述的逐像素测试,您在屏幕上迭代并测试每个像素以查看它是否在三角形内。
除此之外,根据实现方式,屏幕中心可能会在内部定义为 (0,0) 以用于裁剪计算,这意味着屏幕左侧的任何内容都将具有负 x 坐标。
- So the author explains the range is too limited [-16384,16383]. Indeed if you have a 2048 pixels in width and use 256 sub-pixels then the coordinate of the point in x would need to be 4194048. Thus you would get overflow. The author keeps going and explains how they do it on the GPU to work around this problem, but I simply don't get it. If someone could also explain how it's practically done on the GPU then it would be great.
注意:我不是 GPU 工程师,所以这只是一个高级概念性答案:
文中给出的解释中的关键词是增量评估。看看 orient2d
等式:
int orient2d(const Point2D& a, const Point2D& b, const Point2D& c)
{
return (b.x-a.x)*(c.y-a.y) - (b.y-a.y)*(c.x-a.x);
}
点a
和b
是三角形顶点,而点c
是屏幕坐标。对于给定的三角形,当您遍历屏幕坐标范围时,三角形顶点将保持不变,只有 c
点发生变化。增量评估意味着您只需计算与上次评估方程式相比发生的变化。
假设我们计算方程一次并得到结果 w0
:
w0 = (b.x-a.x)*(c.y-a.y) - (b.y-a.y)*(c.x-a.x);
然后 c.x
递增 s
(每像素步长)。 w0
的新值将是:
w0_new = (b.x-a.x)*(c.y-a.y) - (b.y-a.y)*(c.x+s-a.x);
用第二个方程减去第一个方程,我们得到:
w0_new - w0 = -(b.y-a.y)*s;
-(b.y-a.y)*s
是给定三角形的常数值,因为 s
每次(一个像素)都是相同的量,而 a
和 b
已经提到的也是不变的。我们可以计算一次并将其存储在一个变量中(称之为w0_step
)然后计算减少到:
w0_new = w0 + w0step;
您可以对 w1
和 w2
执行此操作,也可以对 c.y
步骤执行类似的操作。这允许更精确的原因是每像素方程不再包含定点乘法,这是导致溢出的原因。 GPU 可以对每个三角形(例如 64 位)进行一次高精度计算,然后对每个像素(例如 32 位)进行一次较低精度的计算。
我主要是想了解 GPU 在光栅化过程中将浮动顶点坐标转换为定点数时的工作原理。
我读了这个excellent article,它已经解释了很多东西,但它也让我感到困惑。所以文章解释说,因为我们使用了32位整数和如下形式(a - b)*(c - d) - (e - f)*(g - h)
的边缘函数,我们被限制在[-16384,16383]范围内。我明白我们是如何得到这个数字的。这是我的问题:
- 首先,这表明顶点坐标可以为负数。但是我不明白的是,在那个阶段技术上顶点坐标是光栅 space,并且之前应该已经剪裁了所有三角形。因此,从技术上讲,x 坐标的顶点坐标应该只在 [0,图像宽度] 范围内,而 y 坐标的顶点坐标应该在 [0,图像高度] 范围内?那为什么坐标是负数呢?
- 所以作者解释范围太有限了[-16384,16383]。实际上,如果您的宽度为 2048 像素并使用 256 子像素,则 x 中的点坐标需要为 4194048。因此您会溢出。作者继续解释他们如何在 GPU 上解决这个问题,但我就是不明白。如果有人也可以解释它是如何在 GPU 上实际完成的,那就太好了。
- First, this suggests that vertex coordinates can be negative. However what I don't understand is that technically at that stage vertex coordinates are in raster space, and all triangles should have been clipped before. Thus technically there should only be vertex coordinates in the range [0, image width] for the x-coordinate and [0, image height] for the y-coordinate? So why are coordinates negative?
简短的回答是,虽然三角形已被裁剪,但它们尚未裁剪到视口(0,0 - 图像宽度、图像高度)。相反,它们被裁剪到 guard-band 裁剪区域,这是一个围绕视口的较大矩形。视口外但在保护带裁剪区域内的顶点坐标可以具有负坐标。
有(至少)三种类型的三角形裁剪。第一个是 "analytic clipping",这是当你计算三角形边缘与保护带剪辑区域边缘的交点(如果它们重叠),然后在这些点处切掉三角形并将其剩余部分细分为更小的三角形三角形,每个三角形现在都在剪辑区域内。第二种类型是当三角形边界框被裁剪到视口以找到像素范围以在光栅化时迭代(注意这不会改变三角形顶点坐标)。第三种类型是文章中描述的逐像素测试,您在屏幕上迭代并测试每个像素以查看它是否在三角形内。
除此之外,根据实现方式,屏幕中心可能会在内部定义为 (0,0) 以用于裁剪计算,这意味着屏幕左侧的任何内容都将具有负 x 坐标。
- So the author explains the range is too limited [-16384,16383]. Indeed if you have a 2048 pixels in width and use 256 sub-pixels then the coordinate of the point in x would need to be 4194048. Thus you would get overflow. The author keeps going and explains how they do it on the GPU to work around this problem, but I simply don't get it. If someone could also explain how it's practically done on the GPU then it would be great.
注意:我不是 GPU 工程师,所以这只是一个高级概念性答案:
文中给出的解释中的关键词是增量评估。看看 orient2d
等式:
int orient2d(const Point2D& a, const Point2D& b, const Point2D& c)
{
return (b.x-a.x)*(c.y-a.y) - (b.y-a.y)*(c.x-a.x);
}
点a
和b
是三角形顶点,而点c
是屏幕坐标。对于给定的三角形,当您遍历屏幕坐标范围时,三角形顶点将保持不变,只有 c
点发生变化。增量评估意味着您只需计算与上次评估方程式相比发生的变化。
假设我们计算方程一次并得到结果 w0
:
w0 = (b.x-a.x)*(c.y-a.y) - (b.y-a.y)*(c.x-a.x);
然后 c.x
递增 s
(每像素步长)。 w0
的新值将是:
w0_new = (b.x-a.x)*(c.y-a.y) - (b.y-a.y)*(c.x+s-a.x);
用第二个方程减去第一个方程,我们得到:
w0_new - w0 = -(b.y-a.y)*s;
-(b.y-a.y)*s
是给定三角形的常数值,因为 s
每次(一个像素)都是相同的量,而 a
和 b
已经提到的也是不变的。我们可以计算一次并将其存储在一个变量中(称之为w0_step
)然后计算减少到:
w0_new = w0 + w0step;
您可以对 w1
和 w2
执行此操作,也可以对 c.y
步骤执行类似的操作。这允许更精确的原因是每像素方程不再包含定点乘法,这是导致溢出的原因。 GPU 可以对每个三角形(例如 64 位)进行一次高精度计算,然后对每个像素(例如 32 位)进行一次较低精度的计算。