summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorSimonDei <s.f.deitermann@gmail.com>2019-08-03 14:49:45 +0200
committerDivay Prakash <divayprakash@users.noreply.github.com>2019-08-03 18:19:45 +0530
commit12476ec7496db1817a25da21335010428c13e44c (patch)
treede6e7272066588aa86603a35aa85719b9305a7bf
parent808031c20f2c23b0b8abad5e79b0b7cc8e8bc9e3 (diff)
[opengl/en] Add Basic OpenGL 3.3+ (#3577)
* Add files via upload * Rename learnopengl.html.markdown to opengl.html.markdown * Fix broken links and change category * Update opengl.html.markdown * Fixed some typos * Replace tab by spaces * Update opengl.html.markdown * Update opengl.html.markdown - More uniforms info - Handling VBO's - Better description of Functions * Update opengl.html.markdown * Added Matrix Transformation section * Update opengl.html.markdown * Update opengl.html.markdown * Added geometry shader section * Update opengl.html.markdown Fix newline issues * Fix typo
-rw-r--r--opengl.html.markdown765
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)