From 12476ec7496db1817a25da21335010428c13e44c Mon Sep 17 00:00:00 2001 From: SimonDei Date: Sat, 3 Aug 2019 14:49:45 +0200 Subject: [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 --- opengl.html.markdown | 765 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 765 insertions(+) create mode 100644 opengl.html.markdown 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.[1] 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 +#include +#include +#include + +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 glCompileShader() and glAttachShader(). + +```cpp +GLint logSize = 0; +std::vector 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 glLinkProgram(), just replace glGetShaderiv() with glGetProgramiv() +and glGetShaderInfoLog() with glGetProgramInfoLog(). + +```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 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 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.
+**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 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() 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 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)
+[glTexImage2D - OpenGL Refpage](https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glTexImage2D.xhtml)
+[glTexParameter - OpenGL Refpage](https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glTexParameter.xhtml)
+ +```cpp +// With the texture created, we now have to specify the UV, +// or in OpenGL terms ST coordinates. +std::vector 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.
+ +**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 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 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 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.
+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 +[1][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) -- cgit v1.2.3