My first triangle

In this tutorial I will show you how to display a triangle on the screen. It will use the concepts introduced in previous tutorials while also introducing you to some of the manager/helper classes of the Koeky 3D Framework.

To display a window on the screen I will use the GameWindow class provided by the OpenTK library. The basic layout of the ExampleWindow looks like this:
class ExampleWindow : GameWindow
    {
        protected override void OnLoad(EventArgs e)
        {
             // Initialise the data we need to draw a triangle
        }

        protected override void OnResize(EventArgs e)
        {
            // Handle resolution changes
        }

        protected override void OnRenderFrame(FrameEventArgs e)
        {
            // Display the triangle on the screen
        }
    }


I first declare the following variables:
private VertexArray vertexArray;
private DefaultTechnique technique;

private GLManager glManager;
private RenderOptions renderOptions;


The VertexArray object is explained in the tutorial Koeky 3D Framework Basics and the DefaultTechnique object is explained in the tutorial Koeky 3D Framework: Shaders.

The GLManager and RenderOptions classes are new tough. I will explain them now.
The GLManager class is used to make state changes and state tracking in OpenGL cleaner and easier. Not all OpenGL states are supported tough, I add new ones once the need to use it arises.
The GLManager class also allows to bind objects (vertex arrays, textures etc.) and it will filter out any redundant object binds.
Lastly it also has some functions which help you as a programmer. For example image the following piece of code:
public void Draw(GLManager glManager)
{
    // A lot of state changes
    this.glManager.DepthTestEnabled = false;
    this.glManager.CullFaceEnabled = false;
    this.glManager.BlendingEnabled = true;
    this.glManager.BlendingDestination = BlendingFactorDest.OneMinusSrcAlpha;
    this.glManager.BlendingSource = BlendingFactorSrc.SrcAlpha;

    // draw something here

    // We now need to reset every setting again. Otherwise we would 'leak' state changes

    this.glManager.DepthTestEnabled = true;
    this.glManager.CullFaceEnabled = true;
    this.glManager.BlendingEnabled = false;
}

In my opinion this is error prone and not nice to look at. That is why with the GLManager class you can do this:
public void Draw(GLManager glManager)
{
    glManager.PushRenderState();

    // A lot of state changes
    glManager.DepthTestEnabled = false;
    glManager.CullFaceEnabled = false;
    glManager.BlendingEnabled = true;
    glManager.BlendingDestination = BlendingFactorDest.OneMinusSrcAlpha;
    glManager.BlendingSource = BlendingFactorSrc.SrcAlpha;

    // draw something here

    // Reset the state changes we just made
    glManager.PopRenderState();
}

Much easier and nicer to look at!

My advice: use the GLManager wherever you can. It makes state changes and object binding a lot easier.

The RenderOptions class works hand in hand with the GLManager class (if you use a GLManager object you must use a RenderOptions object). It tracks window specific things like:
  • screen resolution
  • window state (fullscreen or windowed)
  • view frustum variables (fov, z near, z far etc.)

Because of this is can be used to easily create a ViewFrustum object (usable for visibility testing) and set the projection matrix.

Now that we have these two classes covered, let's see how to use them in this tutorial.

The OnLoad methods looks like this:
protected override void OnLoad(EventArgs e)
{
    // Create the render options class. This makes it easy to extract a view frustum
       or projection matrix.
    this.renderOptions = new RenderOptions(base.Width, base.Height, 
                                           base.WindowState, base.VSync);

    // Create the GLManager class. This makes state changes easier and cleaner.
    this.glManager = new GLManager(this.renderOptions);

    // Set the background color to red
    this.glManager.ClearColor = Color4.Red;

    // Create the render technique, we use the DefaultTechnique class right now.
    // However more often than not you will find that it is easier to implement    
        this class yourself.
    this.technique = new DefaultTechnique();
    if (!this.technique.Initialise())
        MessageBox.Show(this.technique.ErrorMessage);

    // Create a vertex buffer which will contain the vertex position
    Vector3[] vertices = new Vector3[3]
    {
        new Vector3(-1.0f, 0.0f, 0.0f),
        new Vector3(1.0f, 0.0f, 0.0f),
        new Vector3(0.0f, 1.0f, 0.0f)
    };
    VertexBuffer vertexBuffer = new VertexBuffer(BufferUsageHint.StaticDraw, (int)
                                                        BufferAttribute.Vertex, vertices);

    // Create a vertex array
    this.vertexArray = new VertexArray(vertexBuffer);
}


So we create the RenderOptions and GLManager objects, we create and initialise the DefaultTechnique and lastly we create and store the data needed to render the triangle.

The OnRenderFrame looks like this:
protected override void OnRenderFrame(FrameEventArgs e)
{
    // clear the screen using the GLManager
    this.glManager.ClearScreen(ClearBufferMask.ColorBufferBit | 
                                ClearBufferMask.DepthBufferBit);

    // Set the projection, view and world matrix
    // The GLManager object makes sure they end up in the DefaultTechnique
    this.glManager.Projection = this.renderOptions.Projection;
    this.glManager.World = Matrix4.Identity;
    this.glManager.View = Matrix4.CreateTranslation(0.0f, 0.0f, -5.0f);

    // Bind the vertex array and render technique
    this.glManager.BindVertexArray(this.vertexArray);
    this.glManager.BindTechnique(this.technique);

    // Set some render settings in the DefaulTechnique
    this.technique.UseTexture = false;
    this.technique.DrawColor = Color4.Blue;

    // Draw the triangle, again using the GLManager class
    this.glManager.DrawElements(BeginMode.Triangles, 0, 3);

    // display the image to the user
    base.SwapBuffers();
}


Lastly the OnResize:
protected override void OnResize(EventArgs e)
{
    // Notify the RenderOptions class of the change in resolution
    this.renderOptions.Resolution = base.Size;
}


This is all we need to do to display the triangle. Once you display the window you will see a blue triangle on a red background.

Last edited Jun 10, 2012 at 3:47 PM by Mathyn, version 2

Comments

No comments yet.