OpenGL Programming/Modern OpenGL Tutorial 2D

Even if you do not plan to make a 3D game, and stick to 2D, OpenGL will still bring invaluable tools.

Hardware acceleration
In the past, 2D graphic card provided hardware acceleration by allowing the programmer to store bitmaps and sprites directly in the card, along with a few primitives to perform basic copies (blits), with or without alpha blending.

Nowadays, these features are being replaced by OpenGL and its (more generic) textures. Programming in 2D in OpenGL is basically displaying textures facing the screen, with z coordinates always set to 0. This also introduces a greatly needed standardization for 2D acceleration (for instance, there is essentially no way to get 2D acceleration under GNU/Linux + X11).

This technique is used by several graphics libraries, including SFML, ClanLib or Gnash.

Setting up the 2D space
We will use an orthographic projection matrix, where there is no perspective (objects far away look as big as near objects - you may have already seen this in technical drawing, or by typing Numpad 5 in Blender).

GLM provides  to compute such a projection. Since we'll be manipulating pixels directly, let's use the size of the physical screen in pixels, rather than [-1, 1] as we previously did.

In addition, we've seen that OpenGL's vertical axis is bottom-to-top, while traditional 2D screens are top-to-bottom, so let's reverse the Y axis:

But, since we're programming modern, shouldn't we forget about old-style 2D coordinates? Well, it happens that most graphic formats are top-to-bottom, too. TGA and BMP are bottom-to-top, but almost all other formats, and above all formats libraries (JPG, PNG, etc.), will give you top-to-bottom pictures. And we saw in the texture tutorial that OpenGL uses bottom-to-top for textures, so it would display them upside-down!

Reversing the OpenGL screen means that our pictures can be uploaded to the OpenGL graphic card as-is, and will be displayed in the right direction. The alternative would be to reverse texture coordinates.

The only point of attention is that the rotation angle on the Z axis need to be reversed, too.

But maybe the main reason behind reversing coordinates is that most users expect Y coordinates at top-to-bottom: check Gimp and Dia for instance; also check other 2D game frameworks and libraries. One notable exception is Inkscape (vector drawing) where coordinates start at the bottom-left like OpenGL.

Uploading textures
Graphic cards used to have odd limits, such as only allowing power-of-two dimensions.

In OpenGL ES 2, non-power-of-two textures are allowed only if: otherwise the texture will always return black.
 * doesn't use mipmaps
 * and  are both set to

Let's do that for our textures.

Displaying a sprite
To "blit" the texture to the OpenGL buffer the simplest way is to draw a pair of triangles with a texture:

You could however perform incremental display updates, by not calling  and using techniques such as dirty rectangles - although nowdays the GPU is usually fast enough to avoid implementing this kind of optimization.

Blitting on a texture
framebuffer / renderbuffer / reuse-that-as-textures / ... ?

WIP

Optimizing for 2D
Since we work in 2D, we can remove:
 * back-face culling is not necessary
 * depth-test is not necessary

Remove the z coordinate
We can remove the z coordinates in the vertices to save space. Keep the w coordinate to 1, so we can work with transformation matrices.

Speaking of which, GLM only provides 4x4 transformation matrices. To shrink transformation matrices from 4x4 to 3x3 we can:
 * program a new set of functions to work with 3x3 matrices.
 * drop the 3rd line and the 3rd row:

This is a 3D affine matrix: xx yx  zx  tx xy  yy  zy  ty xz  yz  zz  tz 0   0   0   1

This is a 2D affine matrix: xx yx  tx xy  yy  ty 0   0   1

We can code the 3D->2D conversion as:

gl_Position is still a vec4, so: It doesn't really matter what  is, since the ortho view always displays the texture the same, whatever z we use.

Zooming the whole screen
You can perform a zoom effect by adjusting the projection. Here's a progressive zoom out, for instance:

Exact / perfect pixelization
OpenGL has a special rule to draw fragments at the center of pixel screens, called "diamond exit rule".

Consequently, it is recommended to add a small translation in X,Y before drawing 2D lines, so that the last pixel isn't missed:

TODO: provide an example

Note: this seems limited to drawing primitives, I couldn't reproduce any issue when manipulating textures.

Links

 * "2d" from the OpenGL wikibook examples: https://gitlab.com/wikibooks-opengl/modern-tutorials/tree/master/2d
 * Simple implementation of 2D blit in the GLtron project:
 * http://sourceforge.net/p/gltron/code/HEAD/tree/trunk/nebu/video/2d.c
 * http://sourceforge.net/p/gltron/code/HEAD/tree/trunk/nebu/video/video_utility.c
 * AGPL'd implementation of 2D blits with optional palette emulation in the GNU FreeDink project:
 * http://git.savannah.gnu.org/cgit/freedink.git/tree/src/IOGfxDisplayGL2.cpp
 * http://git.savannah.gnu.org/cgit/freedink.git/tree/src/IOGfxSurfaceGL2.cpp
 * SDL2 and SFML both provide an OpenGL-accelerated 2D API.