Why is origin usually top left corner in painting APIs when logically and native GL is at bottom left corner?

情到浓时终转凉″ 提交于 2021-02-18 17:10:44

问题


I notice that a lot of drawing APIs have their 0,0 origin at the top left corner, so y actually goes down as it increases. I wonder why is that? Any particular advantage to not working in what I personally consider to be more logical bottom left corner (the origin of a regular x/y grid), which also happens to be the native representation of coordinates in hardware rendering APIs?

Or maybe it has something to do with the way scanline rendering or even display refresh goes?


回答1:


2D raster drawing having a top-left origin, with rightward x-axis and downward y-axis, is most likely mirroring the CRT scanning pattern. A frame buffer organized in this way -- with row-major pixels from top to bottom -- can be fed in linear order to the RAMDAC producing the signal for the CRT. Other organizations would require the graphics system to perform more arithmetic, for no practical benefit.




回答2:


This question is a little vague because of the way rendering 3D graphics works.

You need a frame of reference to describe any position and this is where things get interesting. OpenGL has a half-dozen different coordinate spaces that it deals with for most operations and each one has its own unique characteristics.

The behavior you are discussing only applies to one very specific coordinate space. Even then, by altering the matrices you used when you drew your scene, you can effectively match the coordinate behavior of any other API you want.


Let us consider the traditional sequence of transforms in a fixed-function OpenGL draw.

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

Conventionally Right-handed (+X points right, +Y points up, +Z points backward):

 * Eye-space               [Result of ModelView * VertexPos]

Conventionally Left-handed (+X points right, +Y points up, +Z points forward):

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

As you can see, GL starts out with a right-handed coordinate system by convention and then a traditional projection matrix flips the Z-axis to produce a left-handed coordinate space after projection. Assuming you do not flip any other axes, then the only thing this change in handedness really does is place everything on the proper side of the camera (things like winding direction are actually unaffected). The projection matrix is a very powerful tool.

Of these coordinate spaces, NDC is the most important for our discussion. It has the Z-axis such that +Z points into the screen. It also defines (-1,-1,...) to be a point at the bottom-left and (1,1,...) to be a point at the top-right of the viewport. All of these things together are what make NDC space left-handed and this requires the projection matrix to do some extra work in GL. In this coordinate space, you can absolutely say that the positive Y-axis points up. However, as you have just seen it is possible to alter the way your image is represented in NDC space by fiddling with either the Projection or ModelView matrices and it is not necessarily the case that points located in coordinate spaces prior to NDC agree on the the orientation of the Y-axis.

Finally, since you are discussing a situation where the window's bottom-left corner is described using the coordinate (0,0), we know that you are not discussing NDC space (the bottom-left is (-1,-1) in NDC and what is more that point is relative to the viewport rather than the window). Given correct transformation matrices, you could be describing any of the other 5. Nevertheless, the only time that the point (0,0) is guaranteed to be the bottom-left corner of your window with the positive Y-axis pointing up in OpenGL is when you are using window-space coordinates.


Why did I bother walking you through OpenGL's transformations?

Because most of the time when you give OpenGL a position (be it vertex or raster) it will be defined in object-space and will have to go through all of those transformations. You have many opportunities prior to the NDC->window mapping to re-define your projected image's origin.

Window-space and NDC-space always have the positive Y-axis pointing up, but since the positions you use to actually draw things are defined in object-space, you can get around this by simply projecting your scene upside down. Then the window-space coordinate (0,0) will actually correspond to the top-left of your rendered image rather than bottom-left.

Of course, the image will also appear upside down if you try to display it that way. To overcome that problem, you can draw into an FBO and then blit the FBO's attached color buffer into your window upside down too. The Wine project has code to do this in order to compensate for D3D's origin convention, where (0,0) = top-left. This is more trouble than it is worth most of the time, but if you have to be compatible with an otherwise incompatible API it comes in handy.

You can do this for all of OpenGL's coordinates, including texture coordinates. The convention for images is also such that (0,0) represents the bottom-left, but there is no reason you cannot transform your input coordinates using a matrix first. Fixed-function GL already does this using a texture matrix (identity by default) that is stored per-texture unit.


TL;DR: An interesting point of note to using (0,0) to represent the bottom-left corner in window-space is that the viewport can trivially be mapped from NDC to window-space without flipping the Y-axis. D3D's viewport transform has to do this because OpenGL and D3D actually both agree (shocking, right) that NDC is left-handed (they don't agree on the range of the Z coordinate in NDC or much else though). In D3D, (-1,-1,...) is still the bottom-left corner of the viewport in NDC, but it becomes (ViewportX,ViewportY + ViewportHeight) in window-space.

I personally like having everything after clipping agree on the general orientation of coordinate axes, nevermind whether it offered any historical performance advantage.




回答3:


I think it is like that (from top left) due to historical reasons. On old, text screens, the first position on the screen would be 0,0 since you start writing from the top left corner. Then when graphics where implemented, they kept it like that.




回答4:


CRT scanning predates computers. Why those first TV engineers chose to start at the top left I don't know for sure. Probably a combination of not thinking about mathematical uses and being English readers/writers who start at the top left.

So when computers started being connected to CRT displays, the easiest way to make it work was to put the start of the frame buffer memory at the top left, where the frame started.

Putting the coordinate origin (0, 0) at top left simplifies the the coordinate to memory address calculation. In C, with 1 byte per pixel, it would be

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

where rowBytes is the number of pixels horizontally (including any pad bytes to get a better alignment).

If rowBytes is a power of 2, or say the sum of two POT (eg y * 320 = (y * 256) + (y * 64)) then you can do the calculation with just integer shifts.

If the origin is at the bottom left, then that would need an extra subtract on Y. Which nobody cares about today, but was a significant performance hit back when CPU speed was measured in Mhz.



来源:https://stackoverflow.com/questions/23041032/why-is-origin-usually-top-left-corner-in-painting-apis-when-logically-and-native

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!