OpenGL 3.x tutorials

Hi,

I’ve written a series of tutorials to help people learn OpenGL 3.x. My focus is on the programmable pipeline so there is no usage of deprecated features or fixed function APIs.

The tutorials are available at:

http://ogldev.atspace.org/

Feedback will be greatly appreciated!

Thanks,

Etay Meiri

Feedback will be greatly appreciated!

OK.

You use a lot of very long paragraphs. This was particularly evident in Tutorial 2. Coupled with a larger than standard font, it’s somewhat difficult to read. You should also consider using a monospace font for actual code.

You also do something that will easily confuse readers: make references to features that you’re not immediately going to talk about. For example, in Tutorial 2:

This draw call is very simple and it it used for ordered draws (draw calls where there is only a vertex buffer without an index buffer) and no instances (a feature that enable you to internally redraw the same geometry with some parameters changed per instance).

Someone who isn’t familiar with OpenGL is asking questions like “what is an index buffer?” and “what ‘parameters’ is he talking about?” It’s better to raise these issues exactly and only when you’re prepared to discuss them.

Also, where is your shader? You say, “My focus is on the programmable pipeline so there is no usage of deprecated features or fixed function APIs,” yet you don’t use shaders until Tutorial 4, well after you have drawn things. That means you have been drawing things with the fixed-function pipeline. If this were core GL 3.3, you wouldn’t be able to draw anything at all.

In Tutorial 3, you introduce the concept of winding order. Specifically you state:

The reason I started with a dot is to overcome a nasty attribute of triangles - the danger of specifying the vertices in the incorrect order and having the triangle being rendered backfaced (i.e. not rendered at all).

This is not true. Backface culling is not enabled by default. You did not have to talk about winding order yet if you did not want to.

Also, it is strange to discuss winding order without bothering to detail the OpenGL functions that control winding order and backface culling.

In Tutorial 4, you have a large paragraph that skims complex topics (perspective projection, homogeneous coordinates, etc). But there’s one crucial bit of information that you ignore: what space the coordinates you are writing are in. Defining the extent of clip-space and normalized device coordinate space is very important to understanding what the rules for vertex shader output are.

Also, I would advise not bringing up geometry shaders unless you’re actually going to write one. It just confuses the pipeline.

In Tutorial 5, you have things pretty backwards. If you’re introducing uniforms, you should start with the shader, then show how the uniform from the shader gets filled in by the code.

In Tutorial 6, your math equations are low resolution and scaled. If you want them to be bigger, you should make them bigger in the source data. Consider using MathML as a source and convert it to SVG with something like SVGMath. That’s what I do anyway.

Also, one of the reasons why I introduce the perspective projection first, before other transformations, is that it introduces matrix-based transforms in a way that seems less frivolous. Using a matrix for mere translation seems like overkill until you’re dealing with multiple transformations and transformation composition.

You say:

The point is that C/C++ are row-major languages by default.

This is not true. C/C++ doesn’t know what a “row” is. If you want the first index in a multidimensional array to mean “column” instead of “row”, you can. It’s all a question of user conventions. C/C++ doesn’t impose anything on you. Your matrices are row-major because all of your code assumes them to be row-major by convention.

In Tutorial 7, you quickly introduce trigonometry. This assumes that your audience is familiar with trigonometry, sin, and cosine. If that’s your assumption, so be it, but you might want to make sure that you have an introduction somewhere that tells the reader that they need to know what these functions do before reading the tutorial. Or at least link to the Wikipedia article(s) on the subject.

To me, it seems wrong to go through the effort of introducing the three basic transforms, and then wait to discuss transform composition for 2 tutorials. It just seems more natural to have Tutorial 11 be Tutorial 9.

Tutorial 9 leads with what has to be the biggest paragraph ever. I know I mentioned this before, but it’s a wall of text that’s basically illegible.

In Tutorial 10, you introduce the concept of indexed rendering, which is good. One problem I have is that you don’t really stress the fact that you only get one index for all of the attributes. A lot of people, particularly people new to graphics, are going to assume that you can index each attribute individually. Having a paragraph explaining that you can’t, just to make it abundantly clear that this is not possible would be a good idea.

In Tutorial 11, you have this:

void Rotate(float RotateX, float RotateY, float RotateZ)

No discussion of this could ever possibly be considered complete without a thorough discussion of Gimbal Lock. Otherwise, you give the impression that this function is a good idea (it’s not) and that they can successively apply axial rotations like this to easily orient an object (they can’t).

Also, I think this “Pipeline” class of yours is very poor coding style. It forces 3 specific transforms, and forces them to act in a particular order. While this is the most common order of transforms, it is far from the only one they will ever use. Giving a reason why one might want the scale to be applied after rotation would at least point users who are looking to solve a transformation problem in the right direction.

Additionally, this discussion of transformations ensues without any discussion of coordinate systems or spaces. Nothing is said about model space, world space, clip space, or any other kind of space. Admittedly, some of that is hard considering that we haven’t introduced camera space and the perspective matrix yet.

Overall, it’s a good start, but somewhat slim on content and marred by poor information presentation.

Cool. Thanks for sharing.

Some of Alfonse’s comments are good, but some are just picking nits or flat out wrong, like this one. C/C++ features row-major arrays, period. FORTRAN is column-major. Yes we can redefine north to be south and south to be north and pretend anything, but then we’re just being silly. Don’t let him get you down.

To pick a nit with the nit-picking:

Nothing is said about model space, world space, clip space

There is no such thing as “model space” in OpenGL. There is OBJECT space, WORLD space, CLIP space, NDC space, etc., and there is a MODELING transform which takes you from OBJECT space to WORLD space (and a MODELVIEW which takes you from OBJECT space to EYE space). But no “model” space.

Also I disagree with his point that you have to explain everything before you use it. For tuturials, you often should get a test program out there and running quick, and then you can go back and annotate comments on what you presented and it’ll make more sense. With GL3 there’s just way too much stuff you have to know before you can really get down to doing something useful if you try to explain “everything” before you actually use it (because they ripped out all the convenience rtns, leaving everyone to reinvent the wheel). Leads to bored readers with lots of info in their head not attached to anything concrete, and who will likely bail before it gets good.

So just take all comments with a grain of salt, and go with your gut. It’s your tutorial.

Some of Alfonse’s comments are good, but some are just picking nits or flat out wrong, like this one. C/C++ features row-major arrays, period. FORTRAN is column-major. Yes we can redefine north to be south and south to be north and pretend anything, but then we’re just being silly.

No, we’re being practical.

The operations you apply to arrays define whether they’re row-major or column-major.

If defining north to be south and south to be north makes everything easier, then do it. Definitions are useful only in so far as they do not become a hindrance; once they do, they should be shoved aside. Otherwise, we’d only use textures and images as textures and images, not Fresnel lookup tables or shadow maps or any of the other many possible uses of a texture. Texture coordinates would have to come from vertex attributes, rather than from arbitrary means. And so forth.

The undeniable fact is this: you can use multidimensional arrays in C/C++ as row major or column major as you see fit. If you write code that uses them as row-major, then they are row-major. If you write code that uses them as column-major, then they are column-major. That’s why OpenGL’s glUniformMatrix functions have a parameter that tells whether it should interpret the pointer as row or column major.

There is no such thing as “model space” in OpenGL.

Whatever you want to call them, my point is that there is no discussion of spaces at all in the tutorials. Spaces are important; whatever names you want to give them, there should be some kind of discussion of the various common spaces that positions can be in.

The operations you apply to arrays define whether they’re row-major or column-major.

The way an array is stored in memory defines whether it is row-major or column-major. The operations you apply to arrays merely define how you abstracted the native storage scheme. The native storage scheme for arrays in C++ is row major.

The undeniable fact is this: you can use multidimensional arrays in C/C++ as row major or column major as you see fit

The user convention can be whatever he wishes, but first he has to understand how C++ will store an array in memory and the terminology (row major) used to describe it before he can write code that uses his own convention.

You use a lot of very long paragraphs. This was particularly evident in Tutorial 2. Coupled with a larger than standard font, it’s somewhat difficult to read. You should also consider using a monospace font for actual code.

You also do something that will easily confuse readers: make references to features that you’re not immediately going to talk about. For example, in Tutorial 2:

Someone who isn’t familiar with OpenGL is asking questions like “what is an index buffer?” and “what ‘parameters’ is he talking about?” It’s better to raise these issues exactly and only when you’re prepared to discuss them.

These are good comments. I’ll revise the sections you mentioned. Also, a full description of ordered and indexed draws is a good idea.

Also, where is your shader? You say, “My focus is on the programmable pipeline so there is no usage of deprecated features or fixed function APIs,” yet you don’t use shaders until Tutorial 4, well after you have drawn things. That means you have been drawing things with the fixed-function pipeline. If this were core GL 3.3, you wouldn’t be able to draw anything at all.

I wanted to break up stuff into small and manageable steps and since shaders add a lot of area where new developers can make mistake and you need some data in your vertex buffer in order to drive the shaders I postponed shaders until tutorial 4. So on the first tutorial you learn how to open a window and the second and third you learn about the vertex buffers without shaders. From tutorial 4 and on everything is shaderized. I think this is acceptable for a teaching text.

This is not true. Backface culling is not enabled by default. You did not have to talk about winding order yet if you did not want to.

I just checked the spec and found out you are correct. I’ll revise this.

Also, it is strange to discuss winding order without bothering to detail the OpenGL functions that control winding order and backface culling.

I often disable culling when things strangely disappear. I’ll add that as a debugging tip

In Tutorial 4, you have a large paragraph that skims complex topics (perspective projection, homogeneous coordinates, etc). But there’s one crucial bit of information that you ignore: what space the coordinates you are writing are in. Defining the extent of clip-space and normalized device coordinate space is very important to understanding what the rules for vertex shader output are.

I’ll add a description about coordinate space.

Also, I would advise not bringing up geometry shaders unless you’re actually going to write one. It just confuses the pipeline.

This is just for completeness so I’ll leave it in.

In Tutorial 6, your math equations are low resolution and scaled. If you want them to be bigger, you should make them bigger in the source data. Consider using MathML as a source and convert it to SVG with something like SVGMath. That’s what I do anyway.

Thanks for the tip. The equations took me a long time create as this is the first html and math text that I’ve ever written. I used lyx for the math and then exported it to dvi and then converted to png. I’ll check out your method.

C/C++ doesn’t know what a “row” is. If you want the first index in a multidimensional array to mean “column” instead of “row”, you can. It’s all a question of user conventions. C/C++ doesn’t impose anything on you. Your matrices are row-major because all of your code assumes them to be row-major by convention.

According to “C - A reference manual” by Harbison & Steele (page 125): “Multidimensional array elements are stored in row-major order. That is, those elements that differ only in their last subscript are stored adjacently”.

In Tutorial 7, you quickly introduce trigonometry. This assumes that your audience is familiar with trigonometry, sin, and cosine. If that’s your assumption, so be it, but you might want to make sure that you have an introduction somewhere that tells the reader that they need to know what these functions do before reading the tutorial. Or at least link to the Wikipedia article(s) on the subject.

I agree. I will add about it in the instructions section.

In Tutorial 10, you introduce the concept of indexed rendering, which is good. One problem I have is that you don’t really stress the fact that you only get one index for all of the attributes. A lot of people, particularly people new to graphics, are going to assume that you can index each attribute individually. Having a paragraph explaining that you can’t, just to make it abundantly clear that this is not possible would be a good idea.

Agree. Will clarify that point.

No discussion of this could ever possibly be considered complete without a thorough discussion of Gimbal Lock. Otherwise, you give the impression that this function is a good idea (it’s not) and that they can successively apply axial rotations like this to easily orient an object (they can’t).

I think this function is OK for this stage of the learning path. In many books they don’t go beyond that to describe quaternions as a possible solution. However, I think that a description of the Gimbal Lock as a limitation of that approach is good. Also, describing how the reader can see it in action will be even better.

Also, I think this “Pipeline” class of yours is very poor coding style. It forces 3 specific transforms, and forces them to act in a particular order. While this is the most common order of transforms, it is far from the only one they will ever use. Giving a reason why one might want the scale to be applied after rotation would at least point users who are looking to solve a transformation problem in the right direction.

As you said it yourself, this is the most common order of transforms and will be enough for most people for many of their initial demos. I want to help the readers develop their own framework for future demos and this class will simplify the maintenance of the WVP matrix.

Overall, it’s a good start, but somewhat slim on content and marred by poor information presentation.

I think your feedback was in general helpful. You’ve certainly given me a good amount of work. In some parts we may have different views on how to present stuff. I’m not a graphics guru so I’m trying to create the tutorials that would have been as beneficial to me as possible if I had to study from them.

Thanks!

Etay

First impressions:

  • The text is all big. I’d prefer it to be the normal size that is configured in my web browser. If I were visually impaired, I could adjust it there myself.

  • Nice. I agree that more material is needed on the 3.3 core profile and above.

  • Thank you for assuming I already know how to turn on my computer, compile and link source code with system libraries, and run the resulting executable. It was good to mention GLX and GLUT up front though.

  • It would be great if the full source for each tutorial was listed all out at the bottom of the page. I’d prefer not to have to download a compressed archive, extract it, find the file to open in a source code editor, remember to clean up the directory when I’m done with it, and so on. I’m sure there are plenty of programs that do a basic job of converting C/C++ to HTML.

  • It seems odd to wait until the very last chapter (after indexed drawing and such) before the first 3D perspective example. Probably the best way to keep people interested is to give them a 3D world space to play in by lesson 2 or 3. IMHO, it’s OK to have a little bit of “we haven’t explained this yet” code in the early lessons.

  • Likewise, a tutorial shouldn’t withhold transform matrices until the end, they’re just too useful and fun to play with. But you don’t have to define the matrix multiplication itself at that point. Just show how you can compress arbitrary sequences of the basic T-S-R transform using the ‘*’ operator on a 4x4 matrix class (which most people get from a library anyway).

Thanks for the tutorials. I’ve bookmarked them and hope to take a closer look come January. :slight_smile:

(By then, you’ll likely have incorporated all the feedback, too!)