For other projects, I decided to learn OpenGL by going along with this: https://learnopengl.com/ Most of the notes are either taken from the pdf or interpreted from stuff taken from the pdf.
- Update video card drivers
- Newer versions of OpenGL targets most modern graphics cards so most target lower versions
Library in C giving us bare necessities required for rendering goodies to the screen. Allows us to create an OpenGL context, define window parameters, and handle user input.
- Downloaded the GLFW binary
- Installed CMake
- Built the GLFW binary with CMake
- Opened the generated GLFW solution in VS and built the solution
- Extracted the desired include and libs for a specific opengl header content ressource
- Added the references to the inlcude and library to a project + the dependencies to glfw3.lib and opengl32.lib
Library that saves us time and headaches with functions declarations.
Just followed 3.5 similarly to the GLFW section.
Including glad goes before glfw3 as the second requires the first.
OpenGL is about transforming 3D coordinates to 2D pixels that fit on our screen. This is managed by the graphics pipeline of OpenGL.
Small programs run on the GPU by the processing cores for each step of the pipeline.
OpenGL Shading Language used to write shaders.
A collection of data per 3D coordinate. Vertex data is represented using vertex attributes that can contain data. (Or also just 3D position with some color value)
OpenGL requires you to hint what kind of render types you want to form with the data (ex: collection of points, triangales, one long line ...). Those hints are called primitives and are given to OpenGL with any drawing commands. Some hits: GL_POINTS, GL_TRIANGLES and GL_LINE_STRIP.
Takes a single vertex as input. Main purpose is to transform 3D coordinates into different 3D coordinates. Allows for basic processing on the vertex.
Takes all the vertices from the vertex shader that form a primitive and assembles all the points, in this example: a triangle.
Takes the output of the primitive assembly for a collection of vertices as input with the ability to generate other shapes by creating new shapes or primitives. In this example: a second triangle out of the given shape.
Mapping of the resulting primitive(s) to the corresponding pixels on the screen resutling in fragments for the fragment shader to use.
Clipping discards all fragments that are outside your view, increasing performance.
A fragment in OpenGL is all the data required for OpenGL to render a single pixel.
Calculate the final color of a pixel. Contains data about the 3D scene to calculate (light, shadows, colors of lights etc).
Checks the corresponding depth value of the fragment to check if it's in front or behind another object and should be discarded. Also checks alpha values (opacity of an object) and blends the object accordingly.
Requires to define at least a vertex and a fragment shader of our own since there are none by default. This is why it's easier to learn OpenGL with older versions.
The 0,0 is at the center of the graph

Objects that can store a large number of vertices in the GPU's memory. We use it to store our coordinates and send as inputs to the first step (vertex shaders).
...
Now we're going to make a rectangle ... That's twice the amount of triangles!
...
- VBO -> A memory array for the GPU containings the data for vertices
- AVO -> An object that register the configuration of the VBOs (which buffers are used etc) so we don't configure everytime
- EBO -> An array of indices to tell the order to use the vertices of the VBO to draw. Avoids repetitions.
Try to draw 2 triangles next to each other using glDrawArrays by adding more vertices to your data.
So we get rid of the EBO since glDrawArrays needs all of the vertices specified (no indices). Then we simply draw both in our loop.
Now create the same 2 triangles using two different VAOs and VBOs for their data.
I created a VAO_T1 and VAO_T2 (same for the VBOs) for each triangle. We then use them in our loop.
Create two shader programs where the second program uses a different fragment shader that outputs the color yellow. Draw both triangles again where one outputs the color yellow.
I just copy pasted the code for the initial fragment with other values for the color. I then created another program following what we had done before and used it in the render loop.
Little programs that rest on the GPU. Run for each specific version of the graphics pipeline. Programs transforming inputs to outputs. Very isolated programs that don't communicate with eachother.
The C-like language used to write shaders. Begin with a version declaration followed by list of input and output variables, uniforms and the main function.
ex: #version version_number in type in_variable_name; in type in_variable_name; out type out_variable_name; uniform type uniform_name; void main() { // process input(s) and do some weird graphics stuff ... // output processed stuff to output variable out_variable_name = weird_stuff_we_processed; }
An input variable = vertex attribute. For OpenGL, guarantees 16 4-component vertex, but some hardware may allow for more. Query GL_MAX_VERTEX_ATTRIBS: to change that.
int nrAttributes; glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &nrAttributes); std::cout << "Maximum nr of vertex attributes supported: " << nrAttributes << std::endl;
int, float, double, uint bool (like c) with vectors and matrices.
1,2,3 or 4 component container of any basic type.
- vecn: the default vector of n floats.
- bvecn: a vector of n booleans.
- ivecn: a vector of n integers.
- uvecn: a vector of n unsigned integers.
- dvecn: a vector of n double components.
Swizzling is a flexible component selection method. It allows syntaxes as such:
vec2 someVec; vec4 differentVec = someVec.xyxx; vec3 anotherVec = differentVec.zyw; vec4 otherVec = someVec.xxxx + anotherVec.yxzy; ... vec2 vect = vec2(0.5, 0.7); vec4 result = vec4(vect, 0.0, 0.0); vec4 otherResult = vec4(result.xyz, 1.0);
We want inputs and outputs on individual shaders so we can move stuff around.
It is also possible to omit the layout (location = 0) specifier and query for the attribute locations in your OpenGL code via glGetAttribLocation, but the tutorial author prefers to set them in the vertex shader. It is easier to understand and saves us (and OpenGL) some work.
Also, if we fail to set a vec4 color output variable, the color buffer output for those fragments will be undefined (they'll be black or white).
We need to declare an output in the sending shader and a similar to the receiving shader.
Vertex shader
#version 330 core layout (location = 0) in vec3 aPos; // position has attribute position 0 out vec4 vertexColor; // specify a color output to the fragment shader void main() { gl_Position = vec4(aPos, 1.0); // we give a vec3 to vec4’s constructor vertexColor = vec4(0.5, 0.0, 0.0, 1.0); // output variable to dark-red }
Fragment shader
#version 330 core out vec4 FragColor; in vec4 vertexColor; // input variable from vs (same name and type) void main() { FragColor = vertexColor; }
Another way to pass data from our app on the CPU to the shaders on the GPU. Uniforms are different compared to vertex attributes as they are global. A uniform is unique per shader program and can be accessed by any shader at any stage.
If you declare a uniform that isn’t used anywhere in your GLSL code the compiler will silently remove the variable from the compiled version which is the cause for several frustrating errors; keep this in mind!
We managed to make a flashing green triangle. Yay !
Because OpenGL is in its core a C library it does not have native support for function overloading, so wherever a function can be called with different types OpenGL defines new functions for each type required; glUniform is a perfect example of this. The function requires a specific postfix for the type of the uniform you want to set. A few of the possible postfixes are: • f: the function expects a float as its value. • i: the function expects an int as its value. • ui: the function expects an unsigned int as its value. • 3f: the function expects 3 floats as its value. • fv: the function expects a float vector/array as its value. Whenever you want to configure an option of OpenGL simply pick the overloaded function that corresponds with your type. In our case we want to set 4 floats of the uniform individually so we pass our data via glUniform4f (note that we also could’ve used the fv version).



