Shader are compulsory on Modern OpenGL and most important. Shader are a little program they run in GPU i.e for OpenGL, shader run in rendering hardware. There are many shader language available but we use GLSL (OpenGL Shader Language) which is very similar to C but not C. For beginner, shader can be new things so just think it is a little program written in different language that run in your graphics card. Without shader nothing can be displayed on the screen. Shader takes vertex data as an input, process it and return output value to display on the screen it is vertex processing shader which is called vertex shader. In this program we also use another shader called fragment shader which is used to output the color pixel of fragment. How shader takes input and vertex are processed you will understand later.
Lets start digging the code and I will explain related theory side by side in short so that you can learn quickly and start to write you own program.You can also download the complete source code of this tutorials.
In this whole tutorials series we are using four library:
All the code in this series of articles is available from github: https://github.com/smokindinesh/Modern-OpenGL-Series You can download a zip of all the files from that page, or you can clone the repository if you are familiar with git.
Lets start explaining code from the funciton MainAppFunc() our program starts form here.Code for initializing GLFW and GLEW is given below.
Lets start digging the code and I will explain related theory side by side in short so that you can learn quickly and start to write you own program.You can also download the complete source code of this tutorials.
In this whole tutorials series we are using four library:
- GLFW for creating window,
- GLEW to acess OpenGL API function at run time ,
- GLM for doing some math for us like rotation, translation etc and
- GLSL as shader language.
All the code in this series of articles is available from github: https://github.com/smokindinesh/Modern-OpenGL-Series You can download a zip of all the files from that page, or you can clone the repository if you are familiar with git.
Lets start explaining code from the funciton MainAppFunc() our program starts form here.Code for initializing GLFW and GLEW is given below.
// initialise GLFW if(!glfwInit()) throw std::runtime_error("glfwInit failed"); // open a window with GLFW glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4); glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2); glfwWindowHint(GLFW_RESIZABLE, GL_TRUE); MainWindow = glfwCreateWindow((int)SCREEN_SIZE.x, (int)SCREEN_SIZE.y,"Intro OpenGL with Shader",NULL,NULL); if(!window) throw std::runtime_error("glfwOpenWindow failed. Can your hardware handle OpenGL 4.2?"); // GLFW settings glfwMakeContextCurrent(MainWindow); // initialise GLEW glewExperimental = GL_TRUE; //stops glew crashing on OSX :-/ if(glewInit() != GLEW_OK) throw std::runtime_error("glewInit failed"); // print out some info about the graphics drivers std::cout << "OpenGL version: " << glGetString(GL_VERSION) << std::endl; std::cout << "GLSL version: " << glGetString(GL_SHADING_LANGUAGE_VERSION) << std::endl; std::cout << "Vendor: " << glGetString(GL_VENDOR) << std::endl; std::cout << "Renderer: " << glGetString(GL_RENDERER) << std::endl; // make sure OpenGL version 4.2 API is available if(!GLEW_VERSION_4_2) throw std::runtime_error("OpenGL 4.2 API is not available.");
I am using OpenGL version 4.2 and GLSL version 4.2 so your hardware must support it. In this tutorials we are going to draw a simple triangle. Vertex data to draw traingle is given below.
// Put the three triangle verticies into the VBO GLfloat vertexData[] = { // X Y Z 0.0f, 0.8f, 0.0f, -0.8f,-0.8f, 0.0f, 0.8f,-0.8f, 0.0f, };
Vertex Buffer Object (VBO):
It helps to upload the vertex, color data to the GPU memory. Since memory reside on GPU rather than system memory (CPU), it can be used directly to render the scene which increase the rendering performance. This is the most important feature of VBO. VBO are "buffer" of video memory they just upload the bunch of bytes containing any binary data , it doesn't know what kind of data it has. In this tutorial we are just uploading three 3D point of triangle.
Vertex Array Object (VAO):
VBO doesn't know what type of data it has. VAO describe what type of data does VBO contain because VAO is connection between VBO and shader.
Coder for creating VBO and VAO
// make and bind the VAO glGenVertexArrays(1, &gVAO); glBindVertexArray(gVAO); // make and bind the VBO glGenBuffers(1, &gVBO); glBindBuffer(GL_ARRAY_BUFFER, gVBO); glBufferData(GL_ARRAY_BUFFER, sizeof(vertexData), vertexData, GL_STATIC_DRAW); glEnableVertexAttribArray(0); glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, NULL); // unbind the VBO and VAO glBindBuffer(GL_ARRAY_BUFFER, 0); glBindVertexArray(0);First four line of code just make and bind the VAO and VBO. Fifth line glBufferData upload the vertexData on the newly created VBO. Next line of code setup the VAO. glEnableVertexAttribArray(0) enable the variable in the shader program having index zero. Next glVertexAttribPointer describes what type of data VBO has. First argument "0" index to the shader variable, second argument "3" says each vertices has 3 number, third agrument GL_FLOAT says point are floats type, fourth argument GL_FLSE says points doesn't has to normalized.The firth argument,0, would be used if there was a gap in between each point. Setting this to zero means there are no gaps in our data , sixth argument , NULL, means that our data starts right at the first byte of the VBO.
Now its time to look our shader. We use two type of shader vertex shader for vertex processing and fragment shader for processing pixel color of fragment.
Vertex Shader:
#version 420 core in vec3 vPosition; void main() { gl_Position = vec4(vPosition,1.0); }At first we have to specify the version of the shader. I am using 4.2 version. The second line "in vec3 vPosition" is the input shader variable vec3 means have there vector x,y,z. This is the variable where the vertex data we upload using VBO is assign. Here is only one input variable so it has index 0. Now you may became clear that we upload the data from C++ program using VBO and that data is input for the shader. That input vertex data is process here return as outpur for rendering on screeen.The forth line "gl_Position" = vec4(vPosition,1.0)" means sending the output vertex to the variable gl_Position. Here we input the vertex in variable vPosition and without doing anything we assign the output straight to gl_Position.The variable gl_Position is a global defined by OpenGL, and is used to store the output of the vertex shader. All vertex shaders must set teh gl_Position variable.Finally we have to understand that vertex shader is used to transform the point x,y and z into different point.
Fragment Shader:
#version 420 core out vec4 color; void main() { color = vec4(1.0,1.0,1.0,1.0); }
Main purpose of the fragment shader is to calculate the color of the pixel that is drawn.
Compiling and linking the shader:
Code of the Function to load the shader is given below
GLuint loadShader(char *shaderFile, GLenum type) { std::ifstream in(shaderFile); std::string src= ""; std::string line=""; while(std::getline(in,line)) src += line + "\n"; std::cout << src; GLuint shader; GLint compiled; shader = glCreateShader(type); const char* source = src.c_str(); glShaderSource(shader,1,&source,NULL); glCompileShader(shader); if(!shader) { std::cerr << "Could not compile the shader"; return 0; } return shader; }This function just load the GLSL shader code . We have two shader program vertex and fragment saved in file vertex.glsl and fragment.glsl.
Now code to creating program, linking shader and compile shader is given below
GLuint createShaderProgram() { GLuint vertexShader,fragmentShader; GLint linked; vertexShader = loadShader("vertex.glsl",GL_VERTEX_SHADER); fragmentShader = loadShader("fragment.glsl",GL_FRAGMENT_SHADER); if(!vertexShader || !fragmentShader) return 0; programId=glCreateProgram(); if(!programId) { std::cerr << "could not create the shader program"; return 0; } glAttachShader(programId,vertexShader); glAttachShader(programId,fragmentShader); glBindAttribLocation(programId,0,"vPosition"); glLinkProgram(programId); glGetProgramiv(programId,GL_LINK_STATUS,&linked); if(!linked) { std::cerr << "could not link the shader"; return 0; } glUseProgram(programId); return programId; }
Now code for rendering function is given below.
// draws a single frame static void Render(GLFWwindow* MainWindow) { // clear everything glClearColor(0, 0, 0, 1); // black glClear(GL_COLOR_BUFFER_BIT); // bind the VAO (the triangle) glBindVertexArray(gVAO); // draw the VAO glDrawArrays(GL_TRIANGLES, 0, 3); // unbind the VAO glBindVertexArray(0); // swap the display buffers (displays what was just drawn) glfwSwapBuffers(MainWindow); }
Next Chapter: Drawing Basic Shapes
No comments:
Post a Comment