OpenGL from scratch

It gets harder

So far there hasn't really been much new if you've programmed in C++/C before. In this part however many new concepts will be introduced. In OpenGL versions before 3.0 drawing a triangle would have been quite trivial by just calling glVertex 3 times. In modern OpenGL however you'll have to do a lot more setup before you can get anything on the screen. Mixing old immediate mode OpenGL with the modern way isn't a good idea in the long run.
Here's an overview of what we'll need to do.

Doesn't seem that hard does it?

Let's start

First we should create the shaders. To draw anything in OpenGL you must have a vertex shader and a fragment shader. Normally you would put them into a text file and then read the file at runtime. In OpenGL all shader types need to be separate strings, but you might want to have a way of having them in a same file and then parse the fragment shader and vertex shader part out separately to make OpenGL happy. Knowing how to do that is really more about general programming knowledge, so I'll just create a global variable for both of them to keep this relevant.

char* vertex_shader_str = "#version 400 core \n\ void main() {\n\ gl_Position = gl_Vertex;\n\ }";

Even if you've never seen a shader before this shader should be quite straightforward. gl_Position is a built-in variable that should be set to the final position of a vertex. We just take whatever position is passed as gl_Vertex and set the gl_Position to that. gl_Vertex is also a built-in variable and to be fair you probably shouldn't use it in modern OpenGL. We should use the following instead:

char* vertex_shader_str = "#version 400 core\n\ layout(location = 0) in vec4 position; \n\ void main() { \n\ gl_Position = position; \n\ }";

You can leave the (location = 0) part out and get the location with glGetAttribLocation, but for these simple tutorials I prefer to define the location in shader. We'll cover the meaning of the location soon.
Now let's declare the fragment shader.

char* frag_shader_str = "#version 400 core\n\ layout(location = 0) out vec4 outColor;\n\ void main() {\n\ outColor = vec4(1, 1, 1, 1); \n\ }";

There used to be a built-in variable called gl_FragColor, but it has been deprecated. Let's go with the flow and define our own color output variable called 'outColor'. We set it to RGBA 1,1,1,1 which is solid white. The #version let's us specify the shader version we're targeting, just to make sure OpenGL will scream at us when we use a too recent feature.
Now we need to compile these and link them into a program. I should repeat that normally you want to read them from a file.
Here are the 2 functions for our simplified case.

bool CheckShaderCompileStatus(GLint shader) { GLint status; glGetShaderiv(shader, GL_COMPILE_STATUS, &status); if (status == GL_FALSE) { char errorBuf[1024]; glGetShaderInfoLog(shader, 1024, NULL, errorBuf); OutputDebugStringA(errorBuf); } return status == GL_TRUE; } bool CompileShaders(GLint *compiledProgram, char *vertexShader, char *fragmentShader) { bool success = true; GLuint vert_shader = glCreateShader(GL_VERTEX_SHADER); GLuint frag_shader = glCreateShader(GL_FRAGMENT_SHADER); int vert_shader_len = strlen(vertexShader); int frag_shader_len = strlen(fragmentShader); glShaderSource(vert_shader, 1, &vertexShader, &vert_shader_len); glShaderSource(frag_shader, 1, &fragmentShader, &frag_shader_len); glCompileShader(vert_shader); glCompileShader(frag_shader); if (!CheckShaderCompileStatus(vert_shader) || !CheckShaderCompileStatus(frag_shader)) { glDeleteShader(vert_shader); glDeleteShader(frag_shader); return false; } GLuint program = glCreateProgram(); glAttachShader(program, vert_shader); glAttachShader(program, frag_shader); glLinkProgram(program); GLint status; glGetProgramiv(program, GL_LINK_STATUS, &status); if (status == GL_FALSE) { char errorBuf[1024]; glGetProgramInfoLog(program, 1024, NULL, errorBuf); OutputDebugStringA(errorBuf); success = false; } else { *compiledProgram = program; } glDetachShader(program, vert_shader); glDetachShader(program, frag_shader); glDeleteShader(vert_shader); glDeleteShader(frag_shader); return success; }

CompileShaders takes a pointer to GLint that will be set to the program handle and 2 strings for the shaders. On success it returns true and on failure it prints the error to Visual Studio debug output.
First we get 2 shader handles with glCreateShader. This is a very common pattern in OpenGL so you'll see that a lot. Next we set the shader source with glShaderSource and then compile them with glCompileShader. CheckShaderCompileStatus will make sure the compilation was successful and in case there was an error it will read the error and print it to output.
Assuming the shaders compiled we link them to a program. If the linking is successful we're done, if not then we print the error to output. We should also detach and delete the shaders to free the memory we no longer need.
With that out of the way let's call our new function and do the rest of initialization (after InitOpenGLExt())

GLint defaultProgram; if (!CompileShaders(&defaultProgram, vertex_shader_str, frag_shader_str)) return -1; float vertices[6] = { -1.0f, -1.0f, // bottom left 1.0f, -1.0f, // bottom right 0.0f, 1.0f, // top center }; GLuint vbo; glGenBuffers(1, &vbo); glBindBuffer(GL_ARRAY_BUFFER, vbo); glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 6, vertices, GL_STATIC_DRAW); glEnableVertexAttribArray(0); glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, 0);

First we declare the vertices. We only want to draw a triangle so we can declare the coordinates directly in screen space. In OpenGL screen space coordinates are from -1 to 1, (-1,-1) being the bottom left corner.
Now we need a buffer for the vertices. First we generate a buffer with glGenBuffers, where 1 means that we only want 1 buffer. Same concept as glCreateShader. Then we bind the buffer and set the buffer data. GL_STATIC_DRAW tells OpenGL that the buffer data will not change, it is mostly an optimization hint for the driver.
Only thing that's left to do is explain the data layout to OpenGL. glEnableVertexAttribArray(0) will enable the position attribute. Remember the (location 0) we had in vertex shader? That's where the 0 comes from. Note that gl_Position was vec4 but the buffer contains vec2. That works because in OpenGL the default value for vec4 is (0, 0, 0, 1), the missing values will be set to the defaults. glVertexAttribPointer will tell OpenGL that attribute 0 is 2 floats that should not be normalized, have no stride and start at 0th byte. Stride basically means how many bytes should be skipped until you find the next value, if it is 0 then OpenGL assumes that the values are one after another. Using 8 for stride is also valid in this case.
We're almost there, now we just have to draw it in the main loop. (between clear and swap)

glUseProgram(defaultProgram); glBindBuffer(GL_ARRAY_BUFFER, vbo); glEnableVertexAttribArray(0); glDrawArrays(GL_TRIANGLES, 0, 3);

First we bind the shader program and VAO, then we enable the position attribute (not really needed because we never disable it) and then we draw 3 vertices with the GL_TRIANGLES primitive type.
If you compile and run you should see a white triangle!
Note that you don't really want to draw things directly from VAO, instead you should create Vertex Buffer Object (VBO) that can store the buffer related state (including enabled vertex attributes). We'll do that in the next part.

Final code

Rotating textured cube