44
\$\begingroup\$

http://www.sdltutorials.com/sdl-opengl-tutorial-basics

http://www.opengl-tutorial.org/beginners-tutorials/tutorial-2-the-first-triangle/

These two tutorials use completely different approaches to get nearly the same result. The first uses things like glBegin(GL_QUADS). The second one uses stuff like vertexBufferObjects, shaders based on GLEW. But the result is the same: you get basic shapes.

Why do these differences exist?

The first approach seems much easier to understand. What's the advantage of the complicated second approach?

\$\endgroup\$
4
  • 4
    \$\begingroup\$ There is never just one way to skin a cat. \$\endgroup\$
    – Philipp
    Jan 25, 2015 at 20:45
  • 4
    \$\begingroup\$ @Philipp Yes, but there are right ways and wrong ways, old ways and new ways (and as the answers below demonstrate, the old and new ways may not be compatible in all situations) \$\endgroup\$ Jan 26, 2015 at 9:33
  • 3
    \$\begingroup\$ There are no right ways and wrong ways, only worse ways and better ways (in several different dimensions). \$\endgroup\$
    – user253751
    Jan 27, 2015 at 7:50
  • \$\begingroup\$ glBegin and glEnd have been deprecated because they're extremely inefficient for the current graphics architectures \$\endgroup\$
    – Alex
    Jan 28, 2015 at 6:21

3 Answers 3

78
\$\begingroup\$

OpenGL has four different major versions, not counting the versions for mobile devices and embedded systems (OpenGL|ES) and the Web via JavaScript (WebGL). Just like Direct3D 11 has a different way of doing things than Direct3D 8, so does OpenGL 3 have a different way of doing things than OpenGL 1. The big difference is that OpenGL versions are mostly just add-ons to the older versions (but not entirely).

On top of the different editions and versions of OpenGL, the main OpenGL also added the concept of profiles. Namely the Compatibility Profile (which enables support for APIs from older versions) and the Core Profile (which disables those old APIs). Things like glBegin simply don't work when you use the Core Profile but will when you use the Compatibility Profile (which is the default).

As one further major complication, some implementations of OpenGL (like Apple's, among others) will only enable newer OpenGL features when you're using the Core Profile. This means that you must stop using older APIs in order to use newer APIs.

You then end up with several very confusing scenarios for tutorials:

  1. The tutorial is old and only uses deprecated APIs.
  2. The tutorial is new and well-written and only uses Core-compatible APIs.
  3. The tutorial is new but makes the mistake of assuming that you're working with a driver that enables all APIs in Compatibility mode, and freely mixes both new and old APIs.
  4. The tutorial is for a different edition of OpenGL like OpenGL|ES that doesn't support any of the old APIs at all, in any version.

Things like glBegin are part of what's sometimes called the immediate mode API. This is also super confusing because there's no such thing as a retained mode in OpenGL and "immediate mode" already had a different definition in graphics. It's much better to just refer to those as OpenGL 1.x APIs as they've been obsolete since OpenGL 2.1.

OpenGL's 1.x API would immediately submit vertices to the graphics pipeline back in the old days. This worked well when the speed of the hardware that rendered vertices was roughly on par with the speed of the CPU generating the vertex data. OpenGL back then just offloaded the triangle rasterization and not much else.

These days, the GPU can chew through massive numbers of vertices at very high speeds while performing advanced vertex and pixel transformation and the CPU simply cannot even remotely keep up. On top of that, the interface between the CPU and the GPU has been designed around this speed difference meaning that it's not even possible to submit vertices to the GPU one at a time anymore.

All GL drivers must emulate glBegin by internally allocating a vertex buffer, putting the vertices submitted with glVertex into this buffer, and then submitting that whole buffer in a single draw call when glEnd is called. The overhead of these functions is far greater than if you just updated the vertex buffer yourself, which is why some documentation will (very mistakenly!) refer to vertex buffers as "an optimization" (it's not an optimization; it's the only way to actually talk to the GPU).

There are various other APIs that have been deprecated or obsoleted in OpenGL over the years. The so-called fixed-function pipeline is another such piece. Some documentation might still use this pipeline or mix with the programmable pipeline. The fixed-function pipeline comes from the olden days when graphics cards hard-coded all of the math used to render 3D scenes and the OpenGL API was limited to setting some configuration values for that math. These days the hardware has very little hard-coded math and (just like your CPU) runs user-supplied programs (often called shaders) instead.

Once again the drivers must emulate the old API as the fixed-function features simply aren't present on hardware anymore. This means that the driver has a bunch of compatibility shaders embedded in it that execute the old math from the fixed-function days which is used when you don't supply your own shaders. The old OpenGL functions that modify that old fixed-function state (like the old OpenGL lighting API) are actually using modern OpenGL features like uniform buffers to feed these values to the driver's compatibility shaders.

Drivers that support compatibility have to do a lot of behind-the-scenes work just to figure out when you're using these obsolete features and making sure that you can combine them with modern features smoothly, which adds overhead and greatly complicates the driver. This is one of the reasons that some drivers force you to enable the Core Profile to get at newer features; it simplifies their driver internals greatly by not having to support both the old and new APIs being used simultaneously.

A lot of documentation may recommend that you start with the old APIs simply on account of them being easier to get started with. Direct3D solved this problem for beginners by offering a companion library (DirectX Tool Kit) that provides simpler drawing APIs and pre-written shaders which can be freely mixed with raw Direct3D 11 usage as your expertise grows. The wider OpenGL community has mostly stuck with the Compatibility Profile for beginners, unfortunately, which is problematic as again there are systems that do not let you mix old OpenGL APIs with the newer ones. There are unofficial libraries and tools for simpler rendering on the new OpenGL with varying levels of features and target use cases and languages (MonoGame for .NET users for instance), but nothing officially endorsed or widely agreed upon.

Documentation you're finding may not even be for OpenGL but may be for one of the other similar APIs. OpenGL|ES 1.x had fixed-function rendering but did not have the OpenGL 1.x APIs for vertex submission. OpenGL|ES 2.x+ and WebGL 1+ do not have any fixed-function features at all and there is no backwards compatibility modes for those APIs.

These APIs look very very similar to main OpenGL; they're not quite compatible, but there are official extensions to OpenGL that some (not all) drivers support to become compatible with OpenGL|ES (which WebGL is based on). Because things weren't confusing enough before.

\$\endgroup\$
5
  • 4
    \$\begingroup\$ +1 Fantastic answer! If you could mention a couple of those unofficial libraries and tools for simple rendering on the new OpenGL that would be great :) \$\endgroup\$
    – user541686
    Jan 26, 2015 at 9:23
  • 2
    \$\begingroup\$ Brilliant answer. I've had the same trouble with DirectX back in the day - much simpler than with OpenGL, but the leap from retained/immediate mode to shaders was huge. Fortunately, the documentation helped a lot (unlike OpenGL's, at least for me), but the beginning of "how do I even light" was crazy :D \$\endgroup\$
    – Luaan
    Jan 26, 2015 at 12:20
  • \$\begingroup\$ I'm the author of opengl-tutorial.org, and I agree with Sean. The API evolved this way primarily for performance reasons. \$\endgroup\$
    – Calvin1602
    Jan 26, 2015 at 12:34
  • \$\begingroup\$ Very good information about the topic.. \$\endgroup\$
    – reynmar
    Jan 27, 2015 at 13:25
  • 1
    \$\begingroup\$ @Mehrdad: I don't recall any off the top of my head; there are libraries like SDL2 or SFML that add simplified 2D rendering, various scene graph libraries, MonoGame for C#, etc., but I'm not actually aware of anything directly equivalent to Direct TK now that I think about it. Will edit post since saying "many" may be a big fat lie. :) \$\endgroup\$ Jan 28, 2015 at 17:55
9
\$\begingroup\$

The primary difference is how up-to-date the strategies are. The immediate mode used in the first tutorial:

glBegin(GL_QUADS);
    glColor3f(1, 0, 0); glVertex3f(0, 0, 0);
    glColor3f(1, 1, 0); glVertex3f(100, 0, 0);
    glColor3f(1, 0, 1); glVertex3f(100, 100, 0);
    glColor3f(1, 1, 1); glVertex3f(0, 100, 0);
glEnd();

Is outdated and not supported on newer versions.

Using vertex buffers and shaders is the current method of rendering with OpenGL. It may seem more complicated, but it performs considerably better. Additionally, once you have your supporting code wrapping up the OpenGL stuff, the differences are abstracted away for the most part.

\$\endgroup\$
2
\$\begingroup\$

Just to add some more context to the other excellent answers.

The immediate mode as described in the first link is, as others have said, legacy code from the earliest versions of OpenGL (1.1). It was used back when GPUs were little more than triangle rasterizers and the idea of programmable pipelines didn't exist. If you look at the source code for some of the early hardware-accelerated games like GLQuake and Quake 2 for example, you will see immediate mode in use. In simple terms, the CPU sends instructions for vertices one at a time to the GPU to start drawing triangles on the screen. For the record, GL_QUADS has the same result as GL_TRIANGLES, except the GPU has to turn those quads into triangles itself on the fly.

Modern (3.2+) OpenGL takes a different approach. It buffers the vertex data into the GPU memory for quick access, and then you can send drawing instructions either using glDrawArrays or glDrawElements. You also have the programmable pipeline (glUseProgram) which allows you to customise how the GPU positions and colours the vertices.

There are a few reasons why immediate mode was deprecated, the main reason being performance. As Sean said in his answer, nowadays GPUs can crunch the data faster than the CPU can upload it, so you would be bottlenecking the GPU performance. There is a small overhead involved for every call to OpenGL you make, it is miniscule but when you're making tens of thousands of calls every frame it begins to stack up. Simply put, to draw a textured model using immediate mode, you need at least 2 calls per vertex (glTexCoord2f and glVertex3f) per frame. With modern OpenGL you use a couple of calls at the start to buffer the data, then you can draw the entire model, regardless of how many vertices it contains, using just a few calls to bind the vertex array object, enable some attribute pointers and then a single call to glDrawElements or glDrawArrays. To maximise this performance benefit, many modern engines will batch their vertex data, so rather than having one vertex array object per mesh in the scene, they will batch them all up into one giant vertex array and then crunch it in one go, minimizing the communication overhead.

Which technique is right? Well that depends on what you're trying to do. A simple 2D game which doesn't require any fancy post-processing techniques or shaders will work just fine using immediate mode and it will probably be easier to write the code. However, a more modern 3D game would really struggle, and if you're planning on learning GLSL (shader language), then definitely learn the modern technique.

\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .