Creating Shader Program

Shader is program that works in some stages from GPU. For example, if we want to draw triangle with OpenGL, we need to create a shader program. This program will be run on the rendering pipeline stages. Some of them stages are Vertex Shader and Fragment Shader which are important for us as beginner. Both of stages can be programmed by programmer.

Vertex Shader - It handles and transforms vertices into clip space to get output according to the attributes which is defined how to use data from buffer by programmer. This shader works once for each vertex. Detailed information: Vertex Shader - OpenGL Wiki (khronos.org)

Fragment Shader - After defining positions by Vertex Shader, Fragment Shader will paint each pixels that are inside area that created by vertex positions. Fragment Shader works for each pixel at once. We can say fragment instead of pixel.

How can be programmed these shaders? - Using GLSL which stands for GL Shading Language. It looks like C/C++ programming language. So, We need to type glsl code to programmed shaders.

Creating Shader Program Step by Step:

  1. Create three integer typed variables called programId, vertexShader, fragmentShader
  2. Vertex Shader:
    1. Create the vertexShader using CreateShader(VERTEX_SHADER_AS_TYPE)
    2. Link to Source Code(GLSL code for vertex shader as external file or string) to vertexShader.
    3. Compile the vertexShader using CompileShader(vertexShader)
    4. Check the status of the vertexShader if compiling operation is fault.
  3. Fragment Shader:
    1. Create the fragmentShader using CreateShader(FRAGMENT_SHADER_AS_TYPE)
    2. Link to Source Code(GLSL code for vertex shader as external file or string) to the fragmentShader.
    3. Compile the fragmentShader using CompileShader(fragmentShader)
    4. Check the status of the fragmentShader if compiling operation is fault
  4. Create program:
    1. Create the programId using CreateProgram()
    2. Attach the vertex shader to programId using AttachShader(programId, vertexShader)
    3. Attach the fragment shader to programId  using AttachShader(programId, vertexShader)
    4. Link program using LinkProgram(shaderProgram)
    5. Check the status of the programId if linking operation is fault.
It seems tricky, but it's eacy part of OpenGL progress. I'm not going to type code according to this way to create each shaders. I will just create class called Shader, and then I will make easier usage of Shader. I created this class like in the below:
using SFML.Window;
using SFML.Graphics;
using SFML.System;
using OpenTK.Graphics.OpenGL;
using OpenTK.Windowing.Desktop;
using System.IO;
using System;

namespace OpenTKTutorial
{
    public class Shader
    {
        int shaderProgram;
        int vertexShader, fragmentShader;
        public int ShaderProgram { get {return shaderProgram; } }

        public Shader(string vertexPath, string fragmentPath)
        {
            vertexShader = loadShader(ShaderType.VertexShader, vertexPath);
            fragmentShader = loadShader(ShaderType.FragmentShader, fragmentPath);
            createProgram();
        }

        private int loadShader(ShaderType type, string shaderSourcePath)
        {   
            var shader = GL.CreateShader(type);
            GL.ShaderSource(shader, GetSource(shaderSourcePath));
            GL.CompileShader(shader);
            
            int status;
            GL.GetShader(shader, ShaderParameter.CompileStatus, out status);

            if(status == 0)
            {
                Console.WriteLine(type + " ::shader compile error::" + GL.GetShaderInfoLog(shader));
                return -1;
            }
            return shader;
        }

        private string GetSource(string shaderSourcePath)
        {
            return File.ReadAllText(shaderSourcePath);
        }

        private void createProgram()
        {
            shaderProgram = GL.CreateProgram();
            GL.AttachShader(shaderProgram, vertexShader);
            GL.AttachShader(shaderProgram, fragmentShader);
            GL.LinkProgram(shaderProgram);

            GL.DeleteShader(vertexShader);
            GL.DeleteShader(fragmentShader);

            int status;
            GL.GetProgram(shaderProgram, GetProgramParameterName.LinkStatus, out status);

            if(status == 0)
            {
                Console.WriteLine("::shader program linking error::" + GL.GetProgramInfoLog(shaderProgram));
                return;
            }
        }
    
        ~Shader()
        {
            GL.DetachShader(shaderProgram, vertexShader);
            GL.DetachShader(shaderProgram, fragmentShader);
            GL.DeleteProgram(shaderProgram);
        }
    }
}
In the next posts, We need to add new methods to the Shader class. We are going to draw a triangle in the next post, and we are going to use Shader class for it.

No comments:

Post a Comment