Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / multimedia / OpenGL

OpenGL 4 with OpenTK in C# Part 2: Compiling Shaders and Linking Them

5.00/5 (2 votes)
27 Jan 2017CPOL3 min read 20.8K  
OpenGL 4 with OpenTK in C# Part 2: Compiling Shaders and linking them
Image 1

Now that we have a game window, let's look at loading and compiling shaders and getting things on screen.

This is part 2 of my series on OpenGL4 with OpenTK.

For other posts in this series:

OpenGL 4 with OpenTK in C# Part 1: Initialize the GameWindow

OpenGL 4 with OpenTK in C# Part 2: Compiling shaders and linking them

OpenGL 4 with OpenTK in C# Part 3: Passing data to shaders

OpenGL 4 with OpenTK in C# Part 4: Refactoring and adding error handling

OpenGL 4 with OpenTK in C# Part 5: Buffers and Triangle

 

As stated in the previous post, I am in no way an expert in OpenGL. I write these posts as a way to learn, basically by reading up on examples in a book (OpenGL SuperBible, Seventh Edition) and then converting them to OpenTK. I really recommend the book if you want to know how it actually works and why things are done. :)

This part will build upon the game window from the previous post.

Compiling Shaders and Linking Them

I will not go into details about shaders in this post as I do not really know much of them yet, maybe I'll write about them in detail in the future. At this point, I just want to be able to use them.

The following function does just that. It tells OpenGL to create a new shader object with the

GL.CreateShader method. It then populates that shader object with the shader source code from a file with the GL.ShaderSource call. Using System.IO.File.ReadAllText to load the contents of the shader file and then a call to GL.CompileShader to compile it.

The minimum shaders needed to get something to the screen are the VertexShader and the FragmentShader, so the function loads both.

After that, we create a new program by calling GL.CreateProgram, attach the shaders with GL.AttachShader and then link the program with GL.LinkProgram. Pretty straight forward.

After the linking, it is OK to remove the shaders by calling GL.DetachShader and GL.DeleteShader. Always keep a tidy shop. :)

C#
private int CompileShaders()
{
    var vertexShader = GL.CreateShader(ShaderType.VertexShader);
    GL.ShaderSource(vertexShader, 
    File.ReadAllText(@"Components\Shaders\vertexShader.vert"));
    GL.CompileShader(vertexShader);

    var fragmentShader = GL.CreateShader(ShaderType.FragmentShader);
    GL.ShaderSource(fragmentShader, 
    File.ReadAllText(@"Components\Shaders\fragmentShader.frag"));
    GL.CompileShader(fragmentShader);

    var program = GL.CreateProgram();
    GL.AttachShader(program, vertexShader);
    GL.AttachShader(program, fragmentShader);
    GL.LinkProgram(program);

    GL.DetachShader(program, vertexShader);
    GL.DetachShader(program, fragmentShader);
    GL.DeleteShader(vertexShader);
    GL.DeleteShader(fragmentShader);
    return program;
}

Call this from the OnLoad method and store the program id in a member variable in the gamewindow so that we can find it later.

C#
private int _program;
protected override void OnLoad(EventArgs e)
{
 CursorVisible = true;
 _program = CompileShaders();
}

And a little cleanup in the OnExit method:

C#
public override void Exit()
{
 GL.DeleteProgram(_program);
 base.Exit();
}

And when executed, the screen is still dark blue. So nothing.

Drawing Things

So... evidently we need shaders. So I'll just provide two simple ones.

VertexShader

A basic Vertex Shader that sets the position of a vertex.

C#
#version 440 core

void main(void)
{
 gl_Position = vec4( 0.25, -0.25,  0.5,  1.0);
}

FragmentShader

A basic Fragment Shader that sets the color of the fragment.

C#
#version 440 core

out vec4 color;

void main(void)
{
 color = vec4(1.0, 0.0, 0.0, 1.0);
}

Changes To Our Program

In the OnLoad method, let's add a vertex buffer initialization so that we can bind it so that we can use it.

C#
private int _program;
private int _vertexArray;
protected override void OnLoad(EventArgs e)
{
    CursorVisible = true;
    _program = CompileShaders();
    GL.GenVertexArrays(1, out _vertexArray);
    GL.BindVertexArray(_vertexArray);
}

More cleanup in our OnExit method.

C#
public override void Exit()
{
    GL.DeleteVertexArrays(1, ref _vertexArray);
    GL.DeleteProgram(_program);
    base.Exit();
}

And then to actually do some output on the screen in OnRenderFrame method by adding GL.UseProgram, GL.DrawArrays and GL.PointSize. The last so that the point is bigger then just 1 pixel.

C#
protected override void OnRenderFrame(FrameEventArgs e)
{
    Title = $"{_title}: (Vsync: {VSync}) FPS: {1f / e.Time:0}";

    Color4 backColor;
    backColor.A = 1.0f;
    backColor.R = 0.1f;
    backColor.G = 0.1f;
    backColor.B = 0.3f;
    GL.ClearColor(backColor);
    GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);

    GL.UseProgram(_program);

    GL.DrawArrays(PrimitiveType.Points, 0, 1);
    GL.PointSize(10);
    SwapBuffers();
}

This should give an output similar to the header (without the white text, i.e., a red dot on blue background).

If for some reason the shader doesn't work after compiling, check the versions.

Hope this helps someone out there. :)

Thanks for reading. Here's a GIF of 2 of our cats fighting. (Full video at https://youtu.be/p9fTlyCnwgg.)

Image 2

Until next time: Work to Live, Don’t Live to Work

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)