diff options
Diffstat (limited to 'opengl.html.markdown')
| -rw-r--r-- | opengl.html.markdown | 765 | 
1 files changed, 765 insertions, 0 deletions
| diff --git a/opengl.html.markdown b/opengl.html.markdown new file mode 100644 index 00000000..83ace3e8 --- /dev/null +++ b/opengl.html.markdown @@ -0,0 +1,765 @@ +---
 +category: tool
 +tool: OpenGL
 +filename: learnopengl.cpp
 +contributors:
 +    - ["Simon Deitermann", "s.f.deitermann@t-online.de"]
 +---
 +
 +**Open Graphics Library** (**OpenGL**) is a cross-language cross-platform application programming interface
 +(API) for rendering 2D computer graphics and 3D vector graphics.<sup>[1]</sup> In this tutorial we will be
 +focusing on modern OpenGL from 3.3 and above, ignoring "immediate-mode", Displaylists and
 +VBO's without use of Shaders.
 +I will be using C++ with SFML for window, image and context creation aswell as GLEW
 +for modern OpenGL extensions, though there are many other librarys available.
 +
 +```cpp
 +// Creating an SFML window and OpenGL basic setup.
 +#include <GL/glew.h>
 +#include <GL/gl.h>
 +#include <SFML/Graphics.h>
 +#include <iostream>
 +
 +int main() {
 +    // First we tell SFML how to setup our OpenGL context.
 +    sf::ContextSettings context{ 24,   // depth buffer bits
 +                                  8,   // stencil buffer bits
 +                                  4,   // MSAA samples
 +                                  3,   // major opengl version
 +                                  3 }; // minor opengl version
 +    // Now we create the window, enable VSync
 +    // and set the window active for OpenGL.
 +    sf::Window window{ sf::VideoMode{ 1024, 768 },
 +                       "opengl window",
 +                       sf::Style::Default,
 +		       context };
 +    window.setVerticalSyncEnabled(true);
 +    window.setActive(true);
 +    // After that we initialise GLEW and check if an error occured.
 +    GLenum error;
 +    glewExperimental = GL_TRUE;
 +    if ((err = glewInit()) != GLEW_OK)
 +        std::cout << glewGetErrorString(err) << std::endl;
 +    // Here we set the color glClear will clear the buffers with.
 +    glClearColor(0.0f,    // red
 +                 0.0f,    // green
 +                 0.0f,    // blue
 +                 1.0f);   // alpha
 +    // Now we can start the event loop, poll for events and draw objects.
 +    sf::Event event{ };
 +    while (window.isOpen()) {
 +        while (window.pollEvent(event)) {
 +            if (event.type == sf::Event::Closed)
 +                window.close;
 +        }
 +        // Tell OpenGL to clear the color buffer
 +        // and the depth buffer, this will clear our window.
 +        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
 +        // Flip front- and backbuffer.
 +        window.display();
 +    }
 +    return 0;
 +}
 +```
 +
 +## Loading Shaders
 +
 +After creating a window and our event loop we should create a function,
 +that sets up our shader program.
 +
 +```cpp
 +GLuint createShaderProgram(const std::string& vertexShaderPath,
 +                           const std::string& fragmentShaderPath) {
 +    // Load the vertex shader source.
 +    std::stringstream ss{ };
 +    std::string vertexShaderSource{ };
 +    std::string fragmentShaderSource{ };
 +    std::ifstream file{ vertexShaderPath };
 +    if (file.is_open()) {
 +        ss << file.rdbuf();
 +        vertexShaderSource = ss.str();
 +        file.close();
 +    }
 +    // Clear the stringstream and load the fragment shader source.
 +    ss.str(std::string{ });
 +    file.open(fragmentShaderPath);
 +    if (file.is_open()) {
 +        ss << file.rdbuf();
 +        fragmentShaderSource = ss.str();
 +        file.close();
 +    }
 +    // Create the program.
 +    GLuint program = glCreateProgram();
 +    // Create the shaders.
 +    GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER);
 +    GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
 +    // Now we can load the shader source into the shader objects and compile them.
 +    // Because glShaderSource() wants a const char* const*,
 +    // we must first create a const char* and then pass the reference.
 +    const char* cVertexSource = vertexShaderSource.c_str();
 +    glShaderSource(vertexShader,     // shader
 +                   1,                // number of strings
 +                   &cVertexSource,   // strings
 +                   nullptr);         // length of strings (nullptr for 1)
 +    glCompileShader(vertexShader);
 +    // Now we have to do the same for the fragment shader.
 +    const char* cFragmentSource = fragmentShaderSource.c_str();
 +    glShaderSource(fragmentShader, 1, &cFragmentSource, nullptr);
 +    glCompileShader(fragmentShader);
 +    // After attaching the source and compiling the shaders,
 +    // we attach them to the program;
 +    glAttachShader(program, vertexShader);
 +    glAttachShader(program, fragmentShader);
 +    glLinkProgram(program);
 +    // After linking the shaders we should detach and delete
 +    // them to prevent memory leak.
 +    glDetachShader(program, vertexShader);
 +    glDetachShader(program, fragmentShader);
 +    glDeleteShader(vertexShader);
 +    glDeleteShader(fragmentShader);
 +    // With everything done we can return the completed program.
 +    return program;
 +}
 +```
 +
 +If you want to check the compilation log you can add the following between <code>glCompileShader()</code> and <code>glAttachShader()</code>.
 +
 +```cpp
 +GLint logSize = 0;
 +std::vector<GLchar> logText{ };
 +glGetShaderiv(vertexShader,         // shader
 +              GL_INFO_LOG_LENGTH,   // requested parameter
 +              &logSize);            // return object
 +if (logSize > 0) {
 +    logText.resize(logSize);
 +    glGetShaderInfoLog(vertexShader,      // shader
 +                       logSize,           // buffer length
 +                       &logSize,          // returned length
 +                       logText.data());   // buffer
 +    std::cout << logText.data() << std::endl;
 +}
 +```
 +
 +The same is possibile after <code>glLinkProgram()</code>, just replace <code>glGetShaderiv()</code> with <code>glGetProgramiv()</code>
 +and <code>glGetShaderInfoLog()</code> with <code>glGetProgramInfoLog()</code>.
 +
 +```cpp
 +// Now we can create a shader program with a vertex and a fragment shader.
 +// ...
 +glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
 +
 +GLuint program = createShaderProgram("vertex.glsl", "fragment.glsl");
 +
 +sf::Event event{ };
 +// ...
 +// We also have to delete the program at the end of the application.
 +// ...
 +    }
 +    glDeleteProgram(program);	
 +    return 0;
 +}
 +// ...
 +```
 +
 +Ofcourse we have to create the vertex and fragment shader before we can load them,
 +so lets create two basic shaders.
 +
 +**Vertex Shader**
 +
 +```glsl
 +// Declare which version of GLSL we use.
 +// Here we declare, that we want to use the OpenGL 3.3 version of GLSL.
 +#version 330 core
 +// At attribute location 0 we want an input variable of type vec3,
 +// that contains the position of the vertex.
 +// Setting the location is optional, if you don't set it you can ask for the
 +// location with glGetAttribLocation().
 +layout(location = 0) in vec3 position;
 +// Every shader starts in it's main function.
 +void main() {
 +    // gl_Position is a predefined variable that holds
 +    // the final vertex position.
 +    // It consists of a x, y, z and w coordinate.
 +    gl_Position = vec4(position, 1.0);
 +}
 +```
 +
 +**Fragment Shader**
 +
 +```glsl
 +#version 330 core
 +// The fragment shader does not have a predefined variable for
 +// the vertex color, so we have to define a output vec4,
 +// that holds the final vertex color.
 +out vec4 outColor;
 +
 +void main() {
 +    // We simply set the ouput color to red.
 +    // The parameters are red, green, blue and alpha.
 +    outColor = vec4(1.0, 0.0, 0.0, 1.0);
 +}
 +```
 +
 +## VAO and VBO
 +Now we need to define some vertex position we can pass to our shaders. Lets define a simple 2D quad.
 +
 +```cpp
 +// The vertex data is defined in a counter-clockwise way,
 +// as this is the default front face.
 +std::vector<float> vertexData {
 +    -0.5f,  0.5f, 0.0f,
 +    -0.5f, -0.5f, 0.0f,
 +     0.5f, -0.5f, 0.0f,
 +     0.5f,  0.5f, 0.0f
 +};
 +// If you want to use a clockwise definition, you can simply call
 +glFrontFace(GL_CW);
 +// Next we need to define a Vertex Array Object (VAO).
 +// The VAO stores the current state while its active.
 +GLuint vao = 0;
 +glGenVertexArrays(1, &vao);
 +glBindVertexArray(vao);
 +// With the VAO active we can now create a Vertex Buffer Object (VBO).
 +// The VBO stores our vertex data.
 +GLuint vbo = 0;
 +glGenBuffers(1, &vbo);
 +glBindBuffer(GL_ARRAY_BUFFER, vbo);
 +// For reading and copying there are also GL_*_READ and GL_*_COPY,
 +// if your data changes more often use GL_DYNAMIC_* or GL_STREAM_*.
 +glBufferData(GL_ARRAY_BUFFER,     // target buffer
 +             sizeof(vertexData[0]) * vertexData.size(),   // size
 +             vertexData.data(),   // data
 +             GL_STATIC_DRAW);     // usage
 +// After filling the VBO link it to the location 0 in our vertex shader,
 +// which holds the vertex position.
 +// ...
 +// To ask for the attibute location, if you haven't set it:
 +GLint posLocation = glGetAttribLocation(program, "position");
 +// ..
 +glEnableVertexAttribArray(0);
 +glVertexAttribPointer(0, 3,       // location and size
 +                      GL_FLOAT,   // type of data
 +                      GL_FALSE,   // normalized (always false for floats)
 +                      0,          // stride (interleaved arrays)
 +                      nullptr);   // offset (interleaved arrays)
 +// Everything should now be saved in our VAO and we can unbind it and the VBO.
 +glBindVertexArray(0);
 +glBindBuffer(GL_ARRAY_BUFFER, 0);
 +// Now we can draw the vertex data in our render loop.
 +// ...
 +glClear(GL_COLOR_BUFFER_BIT);
 +// Tell OpenGL we want to use our shader program.
 +glUseProgram(program);
 +// Binding the VAO loads the data we need.
 +glBindVertexArray(vao);
 +// We want to draw a quad starting at index 0 of the VBO using 4 indices.
 +glDrawArrays(GL_QUADS, 0, 4);
 +glBindVertexArray(0);
 +window.display();
 +// ...
 +// Ofcource we have to delete the allocated memory for the VAO and VBO at
 +// the end of our application.
 +// ...
 +glDeleteBuffers(1, &vbo);
 +glDeleteVertexArrays(1, &vao);
 +glDeleteProgram(program);
 +return 0;
 +// ...
 +```
 +
 +You can find the current code here: [OpenGL - 1](https://pastebin.com/W8jdmVHD).
 +
 +## More VBO's and Color
 +Let's create another VBO for some colors.
 +
 +```cpp
 +std::vector<float> colorData {
 +    1.0f, 0.0f, 0.0f,
 +    0.0f, 1.0f, 0.0f,
 +    0.0f, 0.0f, 1.0f,
 +    1.0f, 1.0f, 0.0f
 +};
 +```
 +
 +Next we can simply change some previous parameters to create a second VBO for our colors.
 +
 +```cpp
 +// ...
 +GLuint vbo[2];
 +glGenBuffers(2, vbo);
 +glBindBuffer(GL_ARRAY_BUFFER, vbo[0]);
 +// ...
 +glDeleteBuffers(2, vbo);
 +/ ...
 +// With these changes made we now have to load our color data into the new VBO
 +// ...
 +glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, nullptr);
 +
 +glBindBuffer(GL_ARRAY_BUFFER, vbo[1]);
 +glBufferData(GL_ARRAY_BUFFER, sizeof(colorData[0]) * colorData.size(),
 +             colorData.data(), GL_STATIC_DRAW);
 +glEnableVertexAttribArray(1);
 +glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, nullptr);
 +
 +glBindVertexArray(0);  
 +// ...
 +```
 +
 +Next we have to change our vertex shader to pass the color data to the fragment shader.<br>
 +**Vertex Shader**
 +
 +```glsl
 +#version 330 core
 +
 +layout(location = 0) in vec3 position;
 +// The new location has to differ from any other input variable.
 +// It is the same index we need to pass to
 +// glEnableVertexAttribArray() and glVertexAttribPointer().
 +layout(location = 1) in vec3 color;
 +
 +out vec3 fColor;
 +
 +void main() {
 +    fColor = color;
 +    gl_Position = vec4(position, 1.0);
 +}
 +```
 +
 +**Fragment Shader**
 +
 +```glsl
 +#version 330 core
 +
 +in vec3 fColor;
 +
 +out vec4 outColor;
 +
 +void main() {
 +    outColor = vec4(fColor, 1.0);
 +}
 +```
 +
 +We define a new input variable ```color``` which represents our color data, this data
 +is passed on to ```fColor```, which is an output variable of our vertex shader and
 +becomes an input variable for our fragment shader.
 +It is imporatant that variables passed between shaders have the exact same name
 +and type.
 +
 +## Handling VBO's
 +
 +```cpp
 +// If you want to completely clear and refill a VBO use glBufferData(),
 +// just like we did before.
 +// ...
 +// There are two mains ways to update a subset of a VBO's data.
 +// To update a VBO with existing data
 +std::vector<float> newSubData {
 +	-0.25f, 0.5f, 0.0f
 +};
 +glBindBuffer(GL_ARRAY_BUFFER, vbo[0]);
 +glBufferSubData(GL_ARRAY_BUFFER,      // target buffer
 +                0,                    // offset
 +                sizeof(newSubData[0]) * newSubData.size(),   // size
 +                newSubData.data());   // data
 +// This would update the first three values in our vbo[0] buffer.
 +// If you want to update starting at a specific location just set the second
 +// parameter to that value and multiply by the types size.
 +// ...
 +// If you are streaming data, for example from a file,
 +// it is faster to directly pass the data to the buffer.
 +// Other access values are GL_READ_ONLY and GL_READ_WRITE.
 +glBindBuffer(GL_ARRAY_BUFFER, vbo[0]);
 +// You can static_cast<float*>() the void* to be more safe.
 +void* Ptr = glMapBuffer(GL_ARRAY_BUFFER,   // buffer to map
 +                        GL_WRITE_ONLY);    // access to buffer
 +memcpy(Ptr, newSubData.data(), sizeof(newSubData[0]) * newSubData.size());
 +// To copy to a specific location add a destination offset to memcpy().
 +glUnmapBuffer(GL_ARRAY_BUFFER);
 +// ...
 +// There is also a way to copy data from one buffer to another,
 +// If we have two VBO's vbo[0] and vbo[1], we can copy like so
 +// You can also read from GL_ARRAY_BUFFER.
 +glBindBuffer(GL_COPY_READ_BUFFER, vbo[0]);
 +// GL_COPY_READ_BUFFER and GL_COPY_WRITE_BUFFER are specifically for
 +// copying buffer data.
 +glBindBuffer(GL_COPY_WRITE_BUFFER, vbo[1]);
 +glCopyBufferSubData(GL_COPY_READ_BUFFER,    // read buffer
 +                    GL_COPY_WRITE_BUFFER,   // write buffer
 +                    0, 0,                   // read and write offset
 +                    sizeof(vbo[0]) * 3);    // copy size
 +// This will copy the first three elements from vbo[0] to vbo[1].
 +```
 +
 +## Uniforms
 +
 +**Fragment Shader**
 +
 +```glsl
 +// Uniforms are variables like in and out, however,
 +// we can change them easily by passing new values with glUniform().
 +// Lets define a time variable in our fragment shader.
 +#version 330 core
 +// Unlike a in/out variable we can use a uniform in every shader,
 +// without the need to pass it to the next one, they are global.
 +// Don't use locations already used for attributes!
 +// Uniform layout locations require OpenGL 4.3!
 +layout(location = 10) uniform float time;
 +
 +in vec3 fColor;
 +
 +out vec4 outColor;
 +
 +void main() {
 +    // Create a sine wave from 0 to 1 based on the time passed to the shader.
 +    float factor = (sin(time * 2) + 1) / 2;
 +    outColor = vec4(fColor.r * factor, fColor.g * factor, fColor.b * factor, 1.0);
 +}
 +```
 +
 +Back to our source code.
 +
 +```cpp
 +// If we haven't set the layout location, we can ask for it.
 +GLint timeLocation = glGetUniformLocation(program, "time");
 +// ...
 +// Also we should define a Timer counting the current time.
 +sf::Clock clock{ };
 +// In out render loop we can now update the uniform every frame.
 +    // ...
 +    window.display();
 +    glUniform1f(10,   // location
 +                clock.getElapsedTime().asSeconds());   // data
 +}
 +// ...
 +```
 +
 +With the time getting updated every frame the quad should now be changing from
 +fully colored to pitch black.
 +There are different types of glUniform() you can find simple documentation here:
 +[glUniform - OpenGL Refpage](https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glUniform.xhtml)
 +
 +## Indexing and IBO's
 +
 +Element Array Buffers or more commonly Index Buffer Objects (IBO) allow us to use the
 +same vertex data again which makes drawing a lot easier and faster. here's an example:
 +
 +```cpp
 +// Lets create a quad from two rectangles.
 +// We can simply use the old vertex data from before.
 +// First, we have to create the IBO.
 +// The index is referring to the first declaration in the VBO.
 +std::vector<unsigned int> iboData {
 +    0, 1, 2,
 +    0, 2, 3
 +};
 +// That's it, as you can see we could reuse 0 - the top left
 +// and 2 - the bottom right.
 +// Now that we have our data, we have to fill it into a buffer.
 +// Note that this has to happen between the two glBindVertexArray() calls,
 +// so it gets saved into the VAO.
 +GLuint ibo = 0;
 +glGenBufferrs(1, &ibo);
 +glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);
 +glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(iboData[0]) * iboData.size(),
 +             iboData.data(), GL_STATIC_DRAW);
 +// Next in our render loop, we replace glDrawArrays() with:
 +glDrawElements(GL_TRIANGLES, iboData.size(), GL_UNSINGED_INT, nullptr);
 +// Remember to delete the allocated memory for the IBO.
 +```
 +
 +You can find the current code here: [OpenGL - 2](https://pastebin.com/R3Z9ACDE).
 +
 +## Textures
 +
 +To load out texture we first need a library that loads the data, for simplicity I will be
 +using SFML, however there are a lot of librarys for loading image data.
 +
 +```cpp
 +// Lets save we have a texture called "my_tex.tga", we can load it with:
 +sf::Image image;
 +image.loadFromFile("my_tex.tga");
 +// We have to flip the texture around the y-Axis, because OpenGL's texture
 +// origin is the bottom left corner, not the top left.
 +image.flipVertically();
 +// After loading it we have to create a OpenGL texture.
 +GLuint texture = 0;
 +glGenTextures(1, &texture);
 +glBindTexture(GL_TEXTURE_2D, texture);
 +// Specify what happens when the coordinates are out of range.
 +glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
 +glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
 +// Specify the filtering if the object is very large.
 +glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
 +glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
 +// Load the image data to the texture.
 +glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image.getSize().x, image.getSize().y,
 +             0, GL_RGBA, GL_UNSIGNED_BYTE, image.getPixelsPtr());
 +// Unbind the texture to prevent modifications.
 +glBindTexture(GL_TEXTURE_2D, 0);
 +// Delete the texture at the end of the application.
 +// ...
 +glDeleteTextures(1, &texture);
 +```
 +
 +Ofcourse there are more texture formats than only 2D textures,
 +You can find further information on parameters here:
 +[glBindTexture - OpenGL Refpage](https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glBindTexture.xhtml)<br>
 +[glTexImage2D - OpenGL Refpage](https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glTexImage2D.xhtml)<br>
 +[glTexParameter - OpenGL Refpage](https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glTexParameter.xhtml)<br>
 +
 +```cpp
 +// With the texture created, we now have to specify the UV,
 +// or in OpenGL terms ST coordinates.
 +std::vector<float> texCoords {
 +    // The texture coordinates have to match the triangles/quad
 +    // definition.
 +    0.0f, 1.0f,	   // start at top-left
 +    0.0f, 0.0f,	   // go round counter-clockwise
 +    1.0f, 0.0f,
 +    1.0f, 1.0f     // end at top-right
 +};
 +// Now we increase the VBO's size again just like we did for the colors.
 +// ...
 +GLuint vbo[3];
 +glGenBuffers(3, vbo);
 +// ...
 +glDeleteBuffers(3, vbo);
 +// ...
 +// Load the texture coordinates into the new buffer.
 +glBindBuffer(GL_ARRAY_BUFFER, vbo[2]);
 +glBufferData(GL_ARRAY_BUFFER, sizeof(texCoords[0]) * texCoords.size(),
 +             texCoords.data(), GL_STATIC_DRAW);
 +glEnableVertexAttribArray(2);
 +glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 0, nullptr);
 +// Because the VAO does not store the texture we have to bind it before drawing.
 +// ...
 +glBindVertexArray(vao);
 +glBindTexture(GL_TEXTURE_2D, texture);
 +glDrawElements(GL_TRIANGLES, iboData.size(), GL_UNSINGED_INT, nullptr);
 +// ...
 +```
 +
 +Change the shaders to pass the data to the fragment shader.<br>
 +
 +**Vertex Shader**
 +
 +```glsl
 +#version 330 core
 +
 +layout(location = 0) in vec3 position;
 +layout(location = 1) in vec3 color;
 +layout(location = 2) in vec2 texCoords;
 +
 +out vec3 fColor;
 +out vec2 fTexCoords;
 +
 +void main() {
 +    fColor = color;
 +    fTexCoords = texCoords;
 +    gl_Position = vec4(position, 1.0);
 +}
 +```
 +
 +**Fragment Shader**
 +
 +```glsl
 +#version 330 core
 +// sampler2D represents our 2D texture.
 +uniform sampler2D tex;
 +uniform float time;
 +
 +in vec3 fColor;
 +in vec2 fTexCoords;
 +
 +out vec4 outColor;
 +
 +void main() {
 +    // texture() loads the current texure data at the specified texture coords,
 +    // then we can simply multiply them by our color.
 +    outColor = texture(tex, fTexCoords) * vec4(fColor, 1.0);
 +}
 +```
 +
 +You can find the current code here: [OpenGL - 3](https://pastebin.com/u3bcwM6q)
 +
 +## Matrix Transformation
 +
 +**Vertex Shader**
 +
 +```glsl
 +#version 330 core
 +
 +layout(location = 0) in vec3 position;
 +layout(location = 1) in vec3 color;
 +layout(location = 2) in vec2 texCoords;
 +// Create 2 4x4 matricies, 1 for the projection matrix
 +// and 1 for the model matrix.
 +// Because we draw in a static scene, we don't need a view matrix.
 +uniform mat4 projection;
 +uniform mat4 model;
 +
 +out vec3 fColor;
 +out vec2 fTexCoords;
 +
 +void main() {
 +    fColor = color;
 +    fTexCoords = texCoords;
 +    // Multiplay the position by the model matrix and then by the
 +    // projection matrix.
 +    // Beware order of multiplication for matricies!
 +    gl_Position = projection * model * vec4(position, 1.0);
 +}
 +```
 +
 +In our source we now need to change the vertex data, create a model- and a projection matrix.
 +
 +```cpp
 +// The new vertex data, counter-clockwise declaration.
 +std::vector<float> vertexData {  
 +    0.0f, 1.0f, 0.0f,   // top left
 +    0.0f, 0.0f, 0.0f,   // bottom left
 +    1.0f, 0.0f, 0.0f,   // bottom right
 +    1.0f, 1.0f, 0.0f    // top right
 +};
 +// Request the location of our matricies.
 +GLint projectionLocation = glGetUniformLocation(program, "projection");
 +GLint modelLocation = glGetUniformLocation(program, "model");
 +// Declaring the matricies.
 +// Orthogonal matrix for a 1024x768 window.
 +std::vector<float> projection {  
 +    0.001953f,       0.0f,  0.0f, 0.0f,
 +         0.0f, -0.002604f,  0.0f, 0.0f,
 +         0.0f,       0.0f, -1.0f, 0.0f,
 +        -1.0f,       1.0f,  0.0f, 1.0f
 +};
 +// Model matrix translating to x 50, y 50
 +// and scaling to x 200, y 200.
 +std::vector<float> model {  
 +    200.0f,   0.0f, 0.0f, 0.0f,
 +      0.0f, 200.0f, 0.0f, 0.0f,
 +      0.0f,   0.0f, 1.0f, 0.0f,
 +     50.0f,  50.0f, 0.0f, 1.0f
 +};
 +// Now we can send our calculated matricies to the program.
 +glUseProgram(program);
 +glUniformMatrix4fv(projectionLocation,   // location
 +                   1,                    // count
 +                   GL_FALSE,             // transpose the matrix
 +                   projection.data());   // data
 +glUniformMatrix4fv(modelLocation, 1, GL_FALSE, model.data());
 +glUseProgram(0);
 +// The glUniform*() calls have to be done, while the program is bound.
 +```
 +
 +The application should now display the texture at the defined position and size.<br>
 +You can find the current code here: [OpenGL - 4](https://pastebin.com/9ahpFLkY)
 +
 +```cpp
 +// There are many math librarys for OpenGL, which create
 +// matricies and vectors, the most used in C++ is glm (OpenGL Mathematics).
 +// Its a header only library.
 +// The same code using glm would look like:
 +glm::mat4 projection{ glm::ortho(0.0f, 1024.0f, 768.0f, 0.0f) };
 +glUniformMatrix4fv(projectionLocation, 1, GL_FALSE,
 +                   glm::value_ptr(projection));
 +// Initialise the model matrix to the identity matrix, otherwise every
 +// multiplication would be 0.
 +glm::mat4 model{ 1.0f };
 +model = glm::translate(model, glm::vec3{ 50.0f, 50.0f, 0.0f });
 +model = glm::scale(model, glm::vec3{ 200.0f, 200.0f, 0.0f });
 +glUniformMatrix4fv(modelLocation, 1, GL_FALSE,
 +                   glm::value_ptr(model));
 +```
 +
 +## Geometry Shader
 +
 +Gemoetry shaders were introduced in OpenGL 3.2, they can produce vertices
 +that are send to the rasterizer. They can also change the primitive type e.g.
 +they can take a point as an input and output other primitives.
 +Geometry shaders are inbetween the vertex and the fragment shader.
 +
 +**Vertex Shader**
 +
 +```glsl
 +#version 330 core
 +
 +layout(location = 0) in vec3 position;
 +layout(location = 1) in vec3 color;
 +// Create an output interface block passed to the next shadaer stage.
 +// Interface blocks can be used to structure data passed between shaders.
 +out VS_OUT {
 +    vec3 color;
 +} vs_out;
 +
 +void main() {
 +    vs_out.color = color
 +    gl_Position = vec4(position, 1.0);
 +}
 +```
 +
 +**Geometry Shader**
 +
 +```glsl
 +#version 330 core
 +// The geometry shader takes in points.
 +layout(points) in;
 +// It outputs a triangle every 3 vertices emitted.
 +layout(triangle_strip, max_vertices = 3) out;
 +// VS_OUT becomes an input variable in the geometry shader.
 +// Every input to the geometry shader in treated as an array.
 +in VS_OUT {
 +    vec3 color;
 +} gs_in[];
 +// Output color for the fragment shader.
 +// You can also simply define color as 'out vec3 color',
 +// If you don't want to use interface blocks.
 +out GS_OUT {
 +    vec3 color;
 +} gs_out;
 +
 +void main() {
 +    // Each emit calls the fragment shader, so we set a color for each vertex.
 +    gs_out.color = mix(gs_in[0].color, vec3(1.0, 0.0, 0.0), 0.5);
 +    // Move 0.5 units to the left and emit the new vertex.
 +    // gl_in[] is the current vertex from the vertex shader, here we only
 +    // use 0, because we are receiving points.
 +    gl_Position = gl_in[0].gl_Position + vec4(-0.5, 0.0, 0.0, 0.0);
 +    EmitVertex();
 +    gs_out.color = mix(gs_in[0].color, vec3(0.0, 1.0, 0.0), 0.5);
 +    // Move 0.5 units to the right and emit the new vertex.
 +    gl_Position = gl_in[0].gl_Position + vec4(0.5, 0.0, 0.0, 0.0);
 +    EmitVertex();
 +    gs_out.color = mix(gs_in[0].color, vec3(0.0, 0.0, 1.0), 0.5);
 +    // Move 0.5 units up and emit the new vertex.
 +    gl_Position = gl_in[0].gl_Position + vec4(0.0, 0.75, 0.0, 0.0);
 +    EmitVertex();
 +    EndPrimitive();
 +}
 +```
 +
 +**Fragment Shader**
 +
 +```glsl
 +in GS_OUT {
 +    vec3 color;
 +} fs_in;
 +
 +out vec4 outColor;
 +
 +void main() {
 +    outColor = vec4(fs_in.color, 1.0);
 +}
 +```
 +
 +If you now store a single point with a single color in a VBO and draw them,
 +you should see a triangle, with your color mixed half way between
 +red, green and blue on each vertex.
 +
 +
 +## Quotes
 +<sup>[1]</sup>[OpenGL - Wikipedia](https://en.wikipedia.org/wiki/OpenGL)
 +
 +## Books
 +
 +- OpenGL Superbible - Fifth Edition (covering OpenGL 3.3)
 +- OpenGL Programming Guide - Eighth Edition (covering OpenGL 4.3)
 | 
