为什么原点通常在绘制API的左上角,逻辑上和原生GL在左下角?

时间:2014-04-13 09:27:59

标签: opengl graphics coordinates coordinate-systems representation

我注意到很多绘图API的左上角有0,0的原点,所以y实际上随着增加而下降。我想知道为什么会这样?没有在我个人认为更符合逻辑的左下角(常规x / y网格的原点)工作的任何特殊优势,这也恰好是硬件渲染API中坐标的原生表示?

或者它可能与扫描线渲染甚至显示刷新的方式有关?

4 个答案:

答案 0 :(得分:4)

具有左上原点的2D光栅绘制,具有向右的x轴和向下的y轴,很可能反映CRT扫描模式。以这种方式组织的帧缓冲器 - 具有从上到下的行主像素 - 可以以线性顺序馈送到RAMDAC,从而产生CRT的信号。其他组织要求图形系统执行更多算术,没有实际好处。

答案 1 :(得分:3)

由于渲染3D图形的工作方式,这个问题有点模糊。

你需要一个参考框架来描述任何位置,这就是事情变得有趣的地方。 OpenGL有六个不同的坐标空间,它可以处理大多数操作,每个坐标空间都有自己独特的特性。

您正在讨论的行为仅适用于一个非常特定的坐标空间。即便如此,通过更改绘制场景时使用的矩阵,您可以有效地匹配所需的任何其他API的坐标行为。


让我们在固定函数OpenGL绘图中考虑传统变换序列。

 * Object-space            {VertexPos (also RasterPos)}
 * World-space             (Skipped in GL by operation below)

惯用右手 (+ X点,+ Y点向上,+ Z点<强>向后):

 * Eye-space               [Result of ModelView * VertexPos]

传统左撇子 (+ X点,+ Y点向上,+ Z点<强>向前):

 * Clip-space              [Result of Projection * EyePos]
 * Normalized Device-space [Result of ClipPos / ClipPos.w]
 * Window-space            [Result of Viewport and Depth Range mapping]

正如您所看到的,GL按惯例从右手坐标系开始,然后传统的投影矩阵翻转Z轴以在投影后产生左手坐标空间。假设你没有翻转任何其他轴,那么这种手动改变的唯一方法就是将所有东西放在相机的正确位置(像缠绕方向这样的东西实际上不受影响)。投影矩阵是一个非常强大的工具。

在这些坐标空间中,NDC对我们的讨论来说是最重要的。它具有Z轴,使得+ Z将 指向 屏幕。它还将(-1,-1,...)定义为左下角的点和(1,1,...)作为视口右上角的点。所有这些事情共同使得NDC空间成为左撇子,这需要投影矩阵在GL中做一些额外的工作。在这个坐标空间中,您可以绝对说正Y轴指向上方。但是,正如您刚刚看到的那样,可以通过摆弄Projection或ModelView矩阵来改变图像在NDC空间中的表示方式,并且不一定是在NDC之前位于坐标空间中的点同意Y轴的方向。

最后,由于您正在讨论使用坐标(0,0)描述窗口左下角的情况,我们知道您 不是 < / strong>讨论NDC空间(NDC中左下角为(-1,-1),该点相对于视口而不是窗口更多)。给定正确的变换矩阵,你可以描述其他任何一个。然而,唯一一次点(0,0)保证是你的窗口左下角的正面当您使用窗口空间坐标时,在OpenGL中指向的Y轴。


为什么我要打扰你完成OpenGL的转换?

因为大多数情况下,当你给OpenGL一个位置(无论是顶点还是光栅)时,它将在 object-space 中定义,并且必须经过所有那些转变。在NDC-&gt;窗口映射之前,您有很多机会重新定义投影图像的来源。

窗口空间和NDC空间始终具有正Y轴朝上,但由于用于实际绘制事物的位置在 对象空间 中定义,你可以通过简单地将场景颠倒投影来解决这个问题。那么窗口空间坐标(0,0)实际上对应于渲染图像的左上角而不是左下角。

当然,如果您尝试以这种方式显示图像,图像也会颠倒显示。为了克服这个问题,你可以画成一个FBO,然后将FBO附加的颜色缓冲区也颠倒到你的窗口中。 Wine项目有代码执行此操作以补偿D3D的原始约定,其中(0,0)=左上角。这比大多数时候的价值更麻烦,但如果你必须与其他不兼容的API兼容,它就派上用场了。

您可以对所有OpenGL的坐标执行此操作,包括纹理坐标。图像的约定也是(0,0)表示左下角,但没有理由不能首先使用矩阵转换 输入 坐标。固定功能GL已经使用纹理矩阵(默认情况下为标识符)来执行此操作,该纹理矩阵按纹理单元存储。


TL; DR :使用(0,0)表示窗口空间左下角的一个有趣的注意事项是视口可以简单地从NDC映射到窗口 - 没有翻转Y轴的空间。 D3D的视口转换必须这样做,因为OpenGL和D3D实际上都同意(令人震惊,正确)NDC是左撇子(他们不同意Z的范围在NDC或其他许多方面协调)。在D3D中,(-1,-1,...)仍然是NDC中视口的左下角,但它变为(视口 X ,视口 Y 在窗口空间中+视​​口高度

我个人喜欢在裁剪对坐标轴的一般方向达成一致之后,不管它是否提供任何历史性能优势。

答案 2 :(得分:1)

由于历史原因,我认为就是这样(从左上角开始)。在旧的文本屏幕上,自从您从左上角开始书写后,屏幕上的第一个位置将为0,0。然后,当图形实现时,他们保持这样。

答案 3 :(得分:1)

CRT扫描早于计算机。为什么那些第一批电视工程师选择从左上角开始,我不确定。可能是不考虑数学用途和从左上角开始的英语读者/作者的组合。

因此,当计算机开始连接到CRT显示器时,使其工作的最简单方法是将帧缓冲存储器的开头放在框架开始的左上角。

将坐标原点(0,0)置于左上方简化了坐标到内存地址的计算。在C中,每像素1个字节,它将是

char * pixel  = (y * rowBytes) + x;

其中rowBytes是水平像素数(包括任何填充字节以获得更好的对齐)。

如果rowBytes是2的幂,或者说两个POT的总和(例如y * 320 =(y * 256)+(y * 64))那么你可以用整数移位进行计算。

如果原点位于左下方,则需要在Y上进行额外的减法。今天没有人关心,但是当以Mhz测量CPU速度时,这是一个重要的性能回击。