Rendering a Triangle using OpenGL(using Shaders)

02 Jul, 2021
In this article we’ll see how to render a triangle using OpenGL. A triangle is probably the simplest shapes you can draw in OpenGL after points and lines and any complicated geometry that you make will me made up of number of triangles joined together.We’ll be using the programmable pipeline, so we’ll be writing simple shader programs as well and compiling them and using them later for rendering. Now this will make our program a bit lengthy but the cool thing is we have do this only once and then reuse the code we’ve already written. This is actually true for most of the OpenGL code and hence we tend to write most of OpenGL codes in different functions and reuse them multiple times later.The libraries that we’ll be using for this is Glew and Glut and Ç++ as our programming language.Setting up the Environment  
Download the latest glew and glut header, library and dll files.In Visual Studio create a Win 32 console application project.Go to your project properties.Import and include header and library for all configuration.Put the dll files where your source files are.
Download the latest glew and glut header, library and dll files.
In Visual Studio create a Win 32 console application project.
Go to your project properties.
Import and include header and library for all configuration.
Put the dll files where your source files are.
The complete Program(Explanation follows after the program)  
CPP
// CPP program to render a triangle using Shaders#include <GL\freeglut.h>#include <GL\glew.h>#include <iostream>#include <string> std::string vertexShader = "#version 430\n"                           "in vec3 pos;"                           "void main() {"                           "gl_Position = vec4(pos, 1);"                           "}"; std::string fragmentShader = "#version 430\n"                             "void main() {"                             "gl_FragColor = vec4(1, 0, 0, 1);"                             "}"; // Compile and create shader object and returns its idGLuint compileShaders(std::string shader, GLenum type){     const char* shaderCode = shader.c_str();    GLuint shaderId = glCreateShader(type);     if (shaderId == 0) { // Error: Cannot create shader object        std::cout << "Error creating shaders";        return 0;    }     // Attach source code to this object    glShaderSource(shaderId, 1, &shaderCode, NULL);    glCompileShader(shaderId); // compile the shader object     GLint compileStatus;     // check for compilation status    glGetShaderiv(shaderId, GL_COMPILE_STATUS, &compileStatus);     if (!compileStatus) { // If compilation was not successful        int length;        glGetShaderiv(shaderId, GL_INFO_LOG_LENGTH, &length);        char* cMessage = new char[length];         // Get additional information        glGetShaderInfoLog(shaderId, length, &length, cMessage);        std::cout << "Cannot Compile Shader: " << cMessage;        delete[] cMessage;        glDeleteShader(shaderId);        return 0;    }     return shaderId;} // Creates a program containing vertex and fragment shader// links it and returns its IDGLuint linkProgram(GLuint vertexShaderId, GLuint fragmentShaderId){    GLuint programId = glCreateProgram(); // create a program     if (programId == 0) {        std::cout << "Error Creating Shader Program";        return 0;    }     // Attach both the shaders to it    glAttachShader(programId, vertexShaderId);    glAttachShader(programId, fragmentShaderId);     // Create executable of this program    glLinkProgram(programId);     GLint linkStatus;     // Get the link status for this program    glGetProgramiv(programId, GL_LINK_STATUS, &linkStatus);     if (!linkStatus) { // If the linking failed        std::cout << "Error Linking program";        glDetachShader(programId, vertexShaderId);        glDetachShader(programId, fragmentShaderId);        glDeleteProgram(programId);         return 0;    }     return programId;} // Load data in VBO and return the vbo's idGLuint loadDataInBuffers(){    GLfloat vertices[] = { // vertex coordinates                           -0.7, -0.7, 0,                           0.7, -0.7, 0,                           0, 0.7, 0    };     GLuint vboId;     // allocate buffer space and pass data to it    glGenBuffers(1, &vboId);    glBindBuffer(GL_ARRAY_BUFFER, vboId);    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);     // unbind the active buffer    glBindBuffer(GL_ARRAY_BUFFER, 0);     return vboId;} // Initialize and put everything togethervoid init(){    // clear the framebuffer each frame with black color    glClearColor(0, 0, 0, 0);     GLuint vboId = loadDataInBuffers();     GLuint vShaderId = compileShaders(vertexShader, GL_VERTEX_SHADER);    GLuint fShaderId = compileShaders(fragmentShader, GL_FRAGMENT_SHADER);     GLuint programId = linkProgram(vShaderId, fShaderId);     // Get the 'pos' variable location inside this program    GLuint posAttributePosition = glGetAttribLocation(programId, "pos");     GLuint vaoId;    glGenVertexArrays(1, &vaoId); // Generate VAO     // Bind it so that rest of vao operations affect this vao    glBindVertexArray(vaoId);     // buffer from which 'pos' will receive its data and the format of that data    glBindBuffer(GL_ARRAY_BUFFER, vboId);    glVertexAttribPointer(posAttributePosition, 3, GL_FLOAT, false, 0, 0);     // Enable this attribute array linked to 'pos'    glEnableVertexAttribArray(posAttributePosition);     // Use this program for rendering.    glUseProgram(programId);} // Function that does the drawing// glut calls this function whenever it needs to redrawvoid display(){    // clear the color buffer before each drawing    glClear(GL_COLOR_BUFFER_BIT);     // draw triangles starting from index 0 and    // using 3 indices    glDrawArrays(GL_TRIANGLES, 0, 3);     // swap the buffers and hence show the buffers    // content to the screen    glutSwapBuffers();} // main function// sets up window to which we'll drawint main(int argc, char** argv){    glutInit(&argc, argv);    glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE);    glutInitWindowSize(500, 500);    glutInitWindowPosition(100, 50);    glutCreateWindow("Triangle Using OpenGL");    glewInit();    init();    glutDisplayFunc(display);    glutMainLoop();    return 0;}
Output Now when you run the program you should have a red triangle on the screen that should look something like the image below. 
main() Function Main function sets up our window. We initialize glut, glew, specify windows size, its position on the screen, specify display mode(which just tells what color space will be used by the window and will it use single or double buffer), set windows title, set the callback method that glut will use to draw stuff on this window and finally make the window visible.loadDataInBuffers() Function A triangle is made up of three coordinate points. These points(stored as floating point array) needs to be sent to graphics processor memory. The way we refer to a location in system memory is relatively easy, just get a pointer to the memory location but using your GPU’s memory is not that straightforward. We do this by something known as buffer objects. Buffer objects are the way you upload your data to the V-RAM and refer to it later. There are usually three steps that you need to follow to get your data into the video memory.  
Generate a unsigned integer identifier for your buffer object.Bind that buffer object to a target.Specify the size of the buffer and optionally load data into it.
Generate a unsigned integer identifier for your buffer object.
Bind that buffer object to a target.
Specify the size of the buffer and optionally load data into it.
Remember that OpenGL expects vertex coordinates in the range of [-1, 1]. Anything outside this range is clipped. However, we are free to choose our own coordinate system in-fact, it is a very common practice to define our own coordinate system and define our objects according to this coordinate system and later change it to the OpengGL coordinate system.But in this post we’ll travel down the easier road and specify our triangle’s coordinates in [-1, 1] range. FYI coordinates in these range are known as normalized device coordinate(NDC).Shaders Throughout the rendering pipeline the input data(here vertex coordinates) go through various stages. We control these stages using something called Shaders Shaders are programs that execute on GPU. Shaders in OpenGL are written in a special language commonly known as GLSL(OpenGL shading language) which you will come to notice is very similar to C and C++. Shaders give you direct control over the graphics pipeline. GLSL was formally included into the OpenGL 2.0 core in 2004 by the OpenGL ARB.The two shaders that you will need all the time are 
Vertex Shader: These shaders are executed once for every vertex(in case of a triangle it will execute 3 times) that the GPU processes. So if your scene consist of one million vertices, the vertex shader will execute one million times once for each vertex. The main job of a vertex shader is to calculate the final positions of the vertices in the scene. It does so by writing a vector(this vector has 4 component) to a special output variable known as gl_Position(only available in vertex shader). The input to the vertex shader is specified using an in type qualifier and output with an out qualifier before the variable. The input and output variable must have global scope.Fragment Shader: After your geometry has passes through other intermediate stages if finally reaches to the rasterizer. Rasterizer divides your geometry into fragments. Then the fragment shader runs for each fragment in your geometry(or triangle). The job of the fragment shader is to determine the final color for each fragment. It writes the final color to a special output variable gl_FragColor(only available to fragment shader). The value passed to the gl_FragColor is a vector containing rgba values for that fragment.
Vertex Shader: These shaders are executed once for every vertex(in case of a triangle it will execute 3 times) that the GPU processes. So if your scene consist of one million vertices, the vertex shader will execute one million times once for each vertex. The main job of a vertex shader is to calculate the final positions of the vertices in the scene. It does so by writing a vector(this vector has 4 component) to a special output variable known as gl_Position(only available in vertex shader). The input to the vertex shader is specified using an in type qualifier and output with an out qualifier before the variable. The input and output variable must have global scope.
Fragment Shader: After your geometry has passes through other intermediate stages if finally reaches to the rasterizer. Rasterizer divides your geometry into fragments. Then the fragment shader runs for each fragment in your geometry(or triangle). The job of the fragment shader is to determine the final color for each fragment. It writes the final color to a special output variable gl_FragColor(only available to fragment shader). The value passed to the gl_FragColor is a vector containing rgba values for that fragment.
The shader code is stored as strings global variable. We’ll link this variable to the data in our openGL program later. The vertex position are passed to gl_Position without modification.Note: It is a common practice to put your shader codes in different files and later store that files contents in string variables.After you done writing your shader you need to create shader objects, attach their corresponding source code, compile them and check for errors if there are.compileShaders() FunctionThis function compiles the shader code, checks for error and creates shader object and returns its id. We’ll call this function once for vertex and fragment shaderlinkProgram() Function Shaders that you write for rendering needs to clubbed together into a shader program. This function clubs these shader objects into a shader program, links them(that sort of creates an executable file for that program) and check for errors that may occur in this process.init() FunctionIt uses all the above functions and puts everything together. We use what is known as VAO(Vertex Array Objects). VAO: A Vertex Array Object (VAO) is an OpenGL Object that stores all of the state needed to supply vertex data. It stores the format of the vertex data as well as the Buffer Objects providing the vertex data arrays. Note that VAOs do not copy, freeze or store the contents of the referenced buffers, if you change any of the data in the buffers referenced by an existing VAO, those changes will be seen by users of the VAO.A shader program assigns to each of the input variable of vertex shader(aka vertex attributes) and uniform variables(variables whose values do not change for the primitive we are rendering) a position that we need to know if we want to link them to their data source.display() Function Remember we haven’t yet told GPU to start rendering. display Function is what gives this command to the GPU. Glut calls this callback function whenever it needs to draw stuff to the screen. 
nidhi_biet
Akanksha_Rai
sweetyty
arorakashish0911
surinderdawra388
computer-graphics
OpenGL
C++ Programs
Writing code in comment?
Please use ide.geeksforgeeks.org,
generate link and share the link here.
Shallow Copy and Deep Copy in C++
C++ Program to check if a given String is Palindrome or not
How to find the minimum and maximum element of a Vector using STL in C++?
C++ Program for QuickSort
C Program to Swap two Numbers
delete keyword in C++
cin in C++
Passing a function as a parameter in C++
Const keyword in C++
Enumerated Types or Enums in C++