Mathyn's picture

OpenGL Framework using OpenTK

During the development of my game I decided to wrap some functionality of OpenGL inside c# classes. I wanted to simplify the creation of vertex buffers, vertex array objects, texture objects and frame buffer objects.
As I started to expand the OpenGL wrapper I also included functionality for skeletal animations and particle systems (on the gpu ofcource :P). Besides that I also included some convenience functionality to ease my burdens as a programmer.

I am going to use this blog to show you what I got so far. It is far from finished, however if anyone is interested maybe I could release the source for everyone to use. Before that happens I do need to iron out some things tough.

Below you will find example code to create and show a cube on the screen using my OpenGL wrapper. I will try to explain it as best as possible, however if you have any questions: please ask!

First off: I am using a default GameWindow. I create the buffers in the OnLoad, rotate the cube in the OnUpdateFrame and render the cube in the OnRenderFrame.

I first declare the following variables

        /// <summary>
        /// The glManager takes care of opengl state changes. It is also used to bind opengl object
        /// </summary>
        private GLManager glManager;
        /// <summary>
        /// Contains some render options
        /// </summary>
        private RenderOptions renderOptions;
 
        /// <summary>
        /// The world transform of the cube
        /// </summary>
        private Matrix4 cubeTransform;
        /// <summary>
        /// The world transform of the camera
        /// </summary>
        private Matrix4 cameraTransform;
 
        /// <summary>
        /// The vertex array object which contains the settings for the cube
        /// </summary>
        private VertexArray vertexArray;
        /// <summary>
        /// The texture to use with the cube
        /// </summary>
        private Texture2D texture;
        /// <summary>
        /// The render technique. Contains the shader which renders the cube.
        /// </summary>
        private Technique technique;

The VertexArray object is a wrapper around the OpenGL VAO. The VertexBuffer is a wrapper around the OpenGL VBO.
The Technique class is an object which encapsulates the creation of a shader, the definition of vertex attributes and the retrieval of uniform locations. I will show the Technique class implementation later up.

The code for the OnLoad looks like this

protected override void OnLoad(EventArgs e)
        {
            // Initialise the GLFramework managers
            this.glManager = new GLManager();
            this.renderOptions = new RenderOptions(800, 600, base.WindowState, base.VSync);
 
            // Set the background color
            this.glManager.ClearColor = Color4.LightBlue;
 
            // Create three vertex buffers. One for each data type (vertex, texture coordinate and normal)
            VertexBuffer verticesBuffer = new VertexBuffer(BufferUsageHint.StaticDraw, BufferAttribute.Vertex, this.vertices);
            VertexBuffer texCoordBuffer = new VertexBuffer(BufferUsageHint.StaticDraw, BufferAttribute.TexCoord, this.texCoords);
            VertexBuffer normalBuffer = new VertexBuffer(BufferUsageHint.StaticDraw, BufferAttribute.Normal, this.normals);
 
            // Create the vertex array which encapsulates the state changes needed to enable the vertex buffers
            // We just pass the created vertex buffers as parameters to the vertex array
           // next time we bind the vertex array the vertexbuffers will also be bound
            this.vertexArray = new VertexArray(verticesBuffer, texCoordBuffer, normalBuffer);
 
            // Load the texture. We can also create the texture by calling the constructor of Texture2D
            // but by using the TextureConstructor the texture will automatically be cached.
            this.texture = TextureConstructor.ConstructTexture2D("crate.jpg");
 
            // Create the render technique and initialise it
            this.technique = new RenderTechnique();
            if (!this.technique.Initialise())
                MessageBox.Show(this.technique.ErrorMessage);
 
            // Create the cube transform
            this.cubeTransform = Matrix4.Identity;
            // Create the camera transform
            this.cameraTransform = Matrix4.CreateRotationX(MathHelper.DegreesToRadians(35.0f)) * Matrix4.CreateTranslation(0.0f, 0.0f, -10.0f);
        }

The BufferAttribute enum used in the above code is one of these things I consider ugly because it forces the programmer to use my enum.

The OnUpdateFrame is really simple:

protected override void OnUpdateFrame(FrameEventArgs e)
        {
            // Rotate the cube
            this.cubeTransform *= Matrix4.CreateRotationY(MathHelper.DegreesToRadians(2.0f));
 
            // If enter is pressed: turn wireframe mode on
            this.glManager.PolygonMode = base.Keyboard[Key.Enter] ? PolygonMode.Line : PolygonMode.Fill;
 
            // Set the title to show the fps
            base.Title = ((int)base.RenderFrequency).ToString();
        }

We just rotate the cube, check if a key was pressed and display the fps in the title bar of the window.

Finally to render the cube: the OnRenderFrame

protected override void OnRenderFrame(FrameEventArgs e)
        {
            this.glManager.ClearScreen(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
 
            // Set the camera transform, cube transform and projection
            this.glManager.View = this.cameraTransform;
            this.glManager.World = this.cubeTransform;
            this.glManager.Projection = this.renderOptions.Projection;
 
            // Bind the required objects.
            this.glManager.BindTechnique(this.technique);
            this.glManager.BindTexture(this.texture, TextureUnit.Texture0);
            this.glManager.BindVertexArray(this.vertexArray);
 
            // Draw the data
            this.glManager.DrawElements(BeginMode.Quads, 0, 6 * 4);
 
            // Display the image to the user
            base.SwapBuffers();
        }

The GLManager class makes sure variables like the projection, world and view matrices are uploaded to the current shader.
Binding every objects is done using the GLManager. However you are not forced to use it. I consider it a convenience in my current project.

Lastly I want to show the render technique. As I said earlier: a Technique class is an object which encapsulates the creation of a shader, the definition of vertex attributes and the retrieval of uniform locations.

This is the code for the technique

/// <summary>
    /// Provides a simple render technique to display a model with a texture
    /// </summary>
    class RenderTechnique : Technique
    {
        private int textureLocation;
 
        public override bool Initialise()
        {
            // Load the shaders from a file
            if (!base.CreateShaderFromFile("vertexShader.txt", "fragmentShader.txt", ""))
                return false;
 
            // Set shader attributes, this is where the data will go in the shader
            base.SetShaderAttribute(BufferAttribute.Vertex, "in_Vertex");
            base.SetShaderAttribute(BufferAttribute.TexCoord, "in_TexCoord");
            base.SetShaderAttribute(BufferAttribute.Normal, "in_Normal");
 
            // Finalize creation of the shader program
            if (!base.Finalize())
                return false;
 
            // Retrieve the location of the uniform 'texture'
            this.textureLocation = base.GetUniformLocation("texture");
 
            // Initialisation was succesful
            return true;
        }
 
        public override void Enable()
        {
            // Set the texture variable to 0
            // By default it should be zero, but just to be sure :P
            base.shader.SetVariable(this.textureLocation, 0);
        }
    }

So we load a shader, define the shader attributes, finalize the creation of the shader and retrieve the uniform location of the texture. In my current game I got a Technique to draw models and render the terrain. But you could implement this anyway you like.

This is just a simple example. However I did not need a single direct call to OpenGL. Because it is just a wrapper around OpenGL you are however not restricted to this wrapper. If you want you could use this wrapper in combination with your own direct OpenGL calls.

Now I am really interested in any feedback. Ofcourse this isn't the first wrapper of its kind, so how do you think this compares? Or do you think I should post a more complex example?
I am also wondering if I can upload the visual studio solution to this blog, so people can experiment with this example if they like.

Really looking forward to your feedback :)


Comments

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
flopoloco's picture

I like this architecture because it has a centralized API, I mean everything that has to do with rendering is contained in a "sandbox (in comparison to game specific code), I don't know though about customizations... But for any detailed aspect I would not mind have a look at your project.

I have tried to create a skeleton of a framework here: http://www.opentk.com/node/2993
(This is a first version W.I.P. for testing purposes and is very brutal-structured)
I had in mind to be able to break the stack -at any time- and start from ground-zero, for the purposes of visualization, but I don't know about game development. I try to find the balance between raw opengl and framework-reusable-code. e.g. Have freedom to create custom vertex buffer implementations, use a custom shader pipeline and return to the previous one, use camera or not, that sort of things...

Your project will not be uploaded here as an archive, as far as I know. You can use "Google Drive" to share files, or create an account on MediaFire (or other file hosting services).

Mathyn's picture

Thank you for the reaction. I realize now the title of this blog was poorly chosen, I named it a framework while I should just have named it a wrapper.

My main goal is to make the creation of common OpenGL object simple. So you can still use normal OpenGL commands (just like your framework) but my goal is that you don't need to.

It is possible to customize lots of things. For example the layout of a buffer can be set to anything you want. And you are also free to implement shaders in any way you want. The Technique class is simply my preferred way.

I will try to upload my project to one of the possibilities you mentioned. I'll let you know when that is done, so anyone can take a look at my code. Alternatively: can't I also create a project on this website or are these used for other purposes?

As for your framework: good to see other people are also experimenting and building these things. I think you are chosing a much more tight integration with OpenGL. Where I have a VertexBuffer class you have a integer to refer to the buffer. I think both approaches have there benefits. Yours could be easier to extent and mine might be easier to use for quick deployment.

Mathyn's picture

I uploaded the source code for the example. You can find it at the following url: http://dl.dropbox.com/u/4467680/GLFrameworkExample.rar

I will release the source for the framework at a later time.

flopoloco's picture

I had a closer look into your project and it seems superb! It has a clever design which blends smoothly hardcore OpenGL and high level concepts. I will start using it as soon as it's ready.

About project hosting: You can create a project here but not count on it. I have seen that not enough people use it. It's more practical (you get all features at once, users, exposure, bug tracking, announcements) to use e.g. codeplex.

Keep up the good work.

Mathyn's picture

I just uploaded the source to codeplex (thanks for pointing me in that direction). You can find it at the following url: https://koekygl.codeplex.com/

I decided to call it KoekyGL Wrapper because it makes it sound like it is easy and delicious as a koeky ;-)

Now I need to work on some documentation, this is the first time I release a project for other people. I hope it works out :)

flopoloco's picture

Wow, you 're really made an awesome framework, this is close to a complete rendering solution.
Especially skeletal animations, shaders, particles...

One note: I recommend (if you find it a good idea first of all) to change the title of the project to something like "KOEKY 3D FRAMEWORK" to be more more specific regarding all of the offered features (OpenGL Wrapper + 3D Framework) in a single packet. :)

I will get my hands on it to test the waters.

Mathyn's picture

Thank you. I still need to perfect and expand some functionality to consider it complete, but I am getting there.

I like your suggestion to change the name. Since it has indeed got a mix of low level features (vertex buffers, frame buffers etc.) and high level concepts (particle systems, skeletal animations). I will change the name to what you suggested, since it would make more sense.

I changed the url to better reflect the new name. You can find it now at https://koeky3d.codeplex.com/