diff options
| author | Boris Verkhovskiy <boris.verk@gmail.com> | 2024-04-04 04:26:14 -0700 | 
|---|---|---|
| committer | GitHub <noreply@github.com> | 2024-04-04 04:26:14 -0700 | 
| commit | 4d59048f0df8441e5ad2c2c440e8d54b0e9c11b6 (patch) | |
| tree | fa2dbdd40da35b3c27f928f1112ea43193a7482e /opengl.html.markdown | |
| parent | b38d4437120e700646a45dff68b7c4ff3f7109c0 (diff) | |
| parent | 327001f58739489b41f6b1f7bbc8be900847b381 (diff) | |
Merge branch 'master' into patch-2
Diffstat (limited to 'opengl.html.markdown')
| -rw-r--r-- | opengl.html.markdown | 1530 | 
1 files changed, 765 insertions, 765 deletions
| diff --git a/opengl.html.markdown b/opengl.html.markdown index 83ace3e8..f6a9085f 100644 --- a/opengl.html.markdown +++ b/opengl.html.markdown @@ -1,765 +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)
 +--- +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 occurred. +    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 possible 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 output 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 attribute 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_UNSIGNED_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_UNSIGNED_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 + +Geometry 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) | 
