如何在半整数像素中心光栅化OpenGL三角形

如何在半整数像素中心光栅化OpenGL三角形,opengl,pixel,rasterizing,rasterize,Opengl,Pixel,Rasterizing,Rasterize,OpenGL像素/片段在概念上是以半整数像素为中心的1x1正方形。OpenGL 4.5规范规定: 片段位于其左下角,位于整数上 网格坐标。光栅化操作也指片段的 中心,从其左下角(和)偏移(1/2,1/2 所以位于半整数坐标上) 光栅化器通常假定像素中心位于整数网格上。由于我正在尝试实现一个正确的OpenGL三角形填充,我想知道下面的过程是否合理 让我们以一个简单的三角形为例,其剪辑坐标为(-1,-1),(+1,-1),(0,+1),如下图左侧所示(假设正交投影且z=0)。假设我们有一个(小5x5

OpenGL像素/片段在概念上是以半整数像素为中心的1x1正方形。OpenGL 4.5规范规定:

片段位于其左下角,位于整数上 网格坐标。光栅化操作也指片段的 中心,从其左下角(和)偏移(1/2,1/2 所以位于半整数坐标上)

光栅化器通常假定像素中心位于整数网格上。由于我正在尝试实现一个正确的OpenGL三角形填充,我想知道下面的过程是否合理

让我们以一个简单的三角形为例,其剪辑坐标为(-1,-1),(+1,-1),(0,+1),如下图左侧所示(假设正交投影且z=0)。假设我们有一个(小5x5帧缓冲区),我们通过
glViewport(0,0,5,5)
将三角形映射到该缓冲区,如右图所示,在设备坐标中生成三角形,顶点为(0,0)、(5,0)、(2.5,5)

正如您所看到的,中心位于三角形内的13个片段(图像中的着色像素)应由光栅化器生成。请注意,碎片中心位于半整数坐标上。要实现OpenGL规范,这就是结果所需要的

扫描线多边形填充将确定扫描线与三角形相交的x跨度,但扫描线的y值为半整数,如下图所示:

硬件/固件光栅化器将假定像素中心位于整数网格上,因为这是执行填充的最有效方式。在下图中,我将三角形的设备坐标移动了(-0.5,-0.5),以将中心放置在整数网格上:

请注意,像素中心现在确实位于整数网格上。该光栅化器只需将(0.5,0.5)添加回每个片段中心,然后再传递给片段着色器。至少这是我的计划

处理纹理坐标似乎是直截了当的。想象我指定的纹理坐标(0,0)、(1,0)、(0.5,1),如下所示。左边的图像使用半整数像素中心(OpenGL方式),右边的图像使用整数像素中心(硬件方式)。纹理坐标(该问题的任何附加片段属性)最终都具有相同的值——即,无需执行任何特殊操作

那么我的方法似乎正确吗

  • 将(-0.5,-0.5)添加到每个碎片坐标
  • 使用硬件高效填充
  • 生成碎片中心时,将(0.5,0.5)添加回,然后
  • 不要担心其他片段属性(它们只是起作用)

是什么让您认为坐标移动了1/2在硬件中实现效率低下?将2的幂相加(1/2就是这样的幂)很容易通过移位一些位并加1来处理。这是在硬件上实现的。硅设计的一个鲜为人知的事实(对软件人员来说):添加更多的晶体管很容易;困难的是在模具上的区域之间布线。拥有一些硬连线逻辑来进行必要的转换是微不足道的。@datenwolf我曾考虑过直接使用半整数像素进行扫描转换,但有一些细节似乎有点棘手。例如,使用整数中心,您总是可以通过计算
ceil(y)
找到分数y值后面的下一个扫描行,这可以在浮点或定点中轻松完成,如
truncate(y+0.9999)
——在具有16位分数的定点中,这只是
(y+0x0FFFF)>>16
。对于半整数扫描行,您可以减去0.5,执行ceil运算,然后再加上1/2。先做一次减法似乎比较容易。我看你已经完全理解了数学。但你仍然有心理障碍。您仍然认为1整数增量==1像素。你必须认为整数双像素=1像素。使用固定点坐标,始终移动1位。CPU不太关心写i+=1还是i+=2。是的,这些机器通常有一条递增指令,但通常只是解码为add1的微码。当涉及到OpenGL时,目标是GPU实现,所以根本不涉及任何软件。是的,有OpenGL光栅化器的软件实现,但这些通常是回退。对于专用硬件来说,没有更简单或更难的算术运算。@datenwolf是的,无论是哪种方式,添加都是微不足道的。对于整数像素中心,我可以在span
x
as
ceil(x)
(例如,在扫描线上
y=4
,span
[1.75,2.75]
的第一个整数是
ceil(1.75)=2
。如果我使用半整数像素,那么我会将前半整数>=x作为
ceil(x+0.5)-0.5
(例如,在扫描线上
y=4.5
span
[2.25,2.75]
上的前半个整数是
ceil(2.25+0.5)-0.5=2.5
)。定点
((x+0x17FFF)>>16)