
Hello. A few weeks ago I started a series of translations of articles on studying OpenGL. But on Article 4, one Khabrovsk commander noted that my translations could violate the license for which the training materials provided in the original article are distributed. And indeed, my translations violated the license. To resolve this problem, I turned to the authors of that set of lessons, but I could not get a normal answer. For this reason, I contacted the author of another, no less (and perhaps even more) steep set of lessons on OpenGL:
Joey de Vries . And he gave full permission to translate his set of lessons. His lessons are much more extensive than the last set, so these translations will last for a long time. And I promise it will be interesting. Interested please under the cat.
I also got up at a crossroads: either I will describe all the basics like creating a window and context in one article, so as not to produce articles, but in this case, not everyone will master such a huge article; or I will also, as before, translate based on the hierarchy of the original. I decided to choose the second option.
At the expense of lessons on Vulkan: unfortunately it’s hard for me to write lessons on this API now because of the scarce video card at the moment, which simply does not support the Vulkan API, so the lessons on this API will be only after updating the video card.
Part 1.1 - OpenGL
Introduction
Before we begin our journey, we should understand what OpenGL is. Mostly OpenGL is understood as an API (Application Programming Interface), which provides a large set of functions that we can use to manage graphics and images. But in fact, OpenGL is more of a specification developed and maintained by the
Khronos Group .
The OpenGL specification describes what the result of each specific function will be and what it should do. And already the
implementation of these specifications lies on the shoulders of developers. And since the specification does not describe the details of the implementation, various OpenGL implementations, respectively, have the right to exist, at least as long as they meet the specifications.
People who develop OpenGL libraries are often video card manufacturers. Each video card you buy supports specific versions of OpenGL from a set of libraries developed for this series of video cards. When using the Apple system, OpenGL libraries are supported by Apple; under Linux, there are combinations of versions from suppliers and custom adaptations of these libraries. It also means that if the version of OpenGL you are using shows a strange behavior, it means, with a high probability, this is a mistake made by video card manufacturers.
Since most implementations are developed by video card manufacturers, it is necessary to update the video card drivers to fix bugs. This is one of the reasons why almost all the lessons recommend updating the drivers on a video card.
Khronos publicly posted all the specifications for all versions of OpenGL. An interested reader can find the OpenGL 3.3 specifications (this is the version of OpenGL we will use)
here . Specifications perfectly show the rules of all functions.
Core-profile and Immediate mode (Instant Mode)
Previously, using OpenGL involved developing in Immediate mode (also known as a fixed function pipeline), which was easy to use for drawing graphics. Most of the functionality of OpenGL was hidden in libraries and the developers did not have the freedom to understand the calculations made by OpenGL.
The developers demanded more flexibility in the development and later the specification became more flexible, and the developers got more control over the process of drawing their graphics. Immediate mode was easy to use and understand, but it was extremely inefficient. For this reason, the specification indicated Immediate mode as obsolete, and from version 3.2 began to motivate programmers to use the Core-profile mode, which excluded all outdated functionality.
When using core-profile, OpenGL forces us to use modern practices. When we try to use outdated functions, OpenGL throws an error and stops drawing. The advantages of using modern practices are flexibility and efficiency, but unfortunately there is a greater difficulty in learning. Immediate mode is a great abstraction and it hides a large amount of
real work done by OpenGL and therefore it was easy to learn, but it’s hard to figure out how OpenGL actually works. The modern approach requires the developer to fully understand OpenGL and graphical programming in general, and although it is a bit more complicated, such a scheme allows for greater flexibility and efficiency.
This is the reason why our lessons are based on Core-Profile OpenGL version 3.3.
Although it is a bit more complicated, but worth it.
Much newer versions of OpenGL have now been released (at the time of writing 4.5) and you may ask: why should we learn OpenGL 3.3 when 4.5 is already released? The answer is pretty simple. All older versions of OpenGL, starting from version 3.3, do not add various useful features without changing the basic mechanics. Newer versions simply provide slightly more efficient or more convenient ways to perform the same operations. As a result, all concepts and techniques applicable to OpenGL 3.3 can be applied to new versions of OpenGL.
Using the latest versions of OpenGL has one problem. Only modern video cards will be able to execute the latest APIs.
Extensions
An excellent feature of OpenGL is support for extensions. At a time when video card manufacturers are introducing new technology or a new extensive optimization for rendering, an extension related to this event appears in the drivers. If the hardware on which the application is running supports the extension, then the developer can use the functionality provided by this extension for more advanced, or efficient graphics rendering. Thus, a graphic programmer can use new technologies without waiting for their implementation in new versions of OpenGL, simply by checking the support of the technology with a video card. Often, if an extension is in high demand, it is implemented as part of the next version of OpenGL.
The developer only needs to check the availability of the extension (or use the extension library). This approach allows the programmer to perform actions more efficiently, based on the extensions he has:
if(GL_ARB_extension_name) {
With OpenGL 3.3, we rarely need extensions, but when needed, the necessary instructions will be provided.
State machine
OpenGL is essentially a large finite state machine: a set of variables that defines the behavior of OpenGL. The state of OpenGL basically means the context of OpenGL. In the process of using OpenGL, we often change states, set some options, manage buffers, and then draw using the current context.
When we tell OpenGL that we want to start drawing, for example, lines, instead of triangles, we change the state of OpenGL by changing the option responsible for how OpenGL should draw. After changing the state of OpenGL, to drawing lines, all subsequent drawing functions will draw lines instead of triangles.
While working with OpenGL, we will go through several state-changing functions that will change the context, and several state-changing functions that perform actions depending on the current state of OpenGL. As long as you keep in mind the fact that OpenGL is a large state machine, most of the functionality will be clear to you.
Objects
OpenGL libraries are written in C and have multiple branches, but this is mainly the C library. Since most of the constructs from the C language is not translated to high-level languages, OpenGL was developed using a large number of abstractions. One such abstraction is the system of objects in OpenGL.
An object in OpenGL is a set of options that represent a subset of the states of OpenGL. For example, we can create an object that describes the window drawing configuration; we can set the size, the number of colors and so on. Such an object can be represented by a C-like structure:
struct object_name { GLfloat option1; GLuint option2; GLchar[] name; };
Primitive types
Note that when using OpenGL, it is recommended to use primitives defined by OpenGL. Instead of using float, write it with added GL. Same for int, uint char, bool, and so on. OpenGL defines memory markup for its GL primitives for cross-platform support, since some operating systems may have different markup. Using OpenGL primitives allows you to achieve full cross-platform of your application.
Every time we want to use objects, we basically write it something like this:
// GLuint objectId = 0; glGenObject(1, &objectId); // glBindObject(GL_WINDOW_TARGET, objectId); // , GL_WINDOW_TARGET glSetObjectOption(GL_WINDOW_TARGET, GL_OPTION_WINDOW_WIDTH, 800); glSetObjectOption(GL_WINDOW_TARGET, GL_OPTION_WINDOW_HEIGHT, 600); // glBindObject(GL_WINDOW_TARGET, 0);
This small piece of code is something you'll often see while working with OpenGL. In the beginning, we create an object and save a link to it as an identification number (id). (The real object data is hidden in the implementation). Then we bind the object to the required part of the context (The location of the window target from the example is given as `GL_WINDOW_TARGET`). Then we set the values of the window options and, in the end, untie the object by setting the id to 0. The values set by us continue to be stored in the object that we can access via
objectId and restore them to the
GL_WINDOW_TARGET object
again .
This code only shows an example of how OpenGL works. Later real examples will be presented.
The main feature of these objects is that we can declare a lot of objects in our application, set their options and whenever we run operations using the OpenGL state, we can simply bind the object with our preferred settings. For example, there may be objects with 3D model data or something that we want to draw on this model. Possession of several objects allows you to simply switch between them in the process of drawing.
let's start
Now you know a little about OpenGL both about the specification and about the library. We learned an approximate algorithm of work and several features used by OpenGL. Do not be discouraged if something is misunderstood, then we will go through all the stages step by step and you will see enough examples to sort out all the OpenGL intricacies. If you are ready to start, then we can start creating an OpenGL context and our first window right here.
Additional resources
- opengl.org : the official OpenGL site;
- OpenGL registry : a repository of OpenGL specifications and all its extensions for all versions of OpenGL.