Click here to Skip to main content
12,761,826 members (43,565 online)
Click here to Skip to main content
Add your own
alternative version

Stats

2.1K views
2 bookmarked
Posted 4 Feb 2017

OpenGL 4 with OpenTK in C# Part 8: Drawing Multiple Objects

, 5 Feb 2017 CPOL
Rate this:
Please Sign up or sign in to vote.
How to reuse the Vertex Array and Buffers to draw the same model multiple times

In this post, we will look at how to reuse the Vertex Array and Buffers to draw the same model multiple times.

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

For other posts in this series:

As stated in the previous post, I am in no way an expert in OpenGL. I write these posts as a way to learn and if someone else finds these posts useful, then all the better. :)

If you think that the progress is slow, then know that I am a slow learner. :P

This part will build upon the game window and shaders from part 6.

Projection Matrix

As a starter, in the previous post, the cube looked a little odd. It had no real perspective and when the window was rescaled, it didn't behave as it should have, i.e., it got out of shape.

To solve this, we shall add a Projection Matrix to our solution.

In our vertex shader, we will add another uniform matrix in addition to the modelView matrix we already have as follows:

#version 450 core

layout (location = 0) in vec4 position;
layout(location = 1) in vec4 color;

out vec4 vs_color;

layout(location = 20) uniform  mat4 projection;
layout (location = 21) uniform  mat4 modelView;

void main(void)
{
 gl_Position = projection * modelView * position;
 vs_color = color;
}

Be sure to get the order of multiplications correct or else things will look very very strange.

In our GameWindow, let's add the following method to create this projection matrix.

private void CreateProjection()
{
            
    var aspectRatio = (float)Width/Height;
    _projectionMatrix = Matrix4.CreatePerspectiveFieldOfView(
        60*((float) Math.PI/180f), // field of view angle, in radians
        aspectRatio,                // current window aspect ratio
        0.1f,                       // near plane
        4000f);                     // far plane
}

Basically, we tell OpenTKs Matrix4 to create this matrix for us. And we want to supply the angle of the field of view together with the window aspect ratio and the near and far clipping planes.

Changing the angle is a cheap man's zoom of sorts, the aspect ratio helps keep things in shape even if we drag the window to strange sizes and the near and far clipping determine how close or far away an object can be and still be drawn on screen.

We want to call the CreateProjection from both the OnLoad and OnResize event handlers.

Then, in our OnRenderFrame, add a call to:

GL.UniformMatrix4(20, false, ref _projectionMatrix);

And we should be set.

Drawing Multiple Objects

So, now that we have gone through the projection matrix, that should probably have been covered in an earlier post, but I did not know it at the time of writing. We can finally start with the topic of this post: Drawing multiple objects on screen.

Let's initialize a few different RenderObjects in our OnLoad method. As the only model we have at the moment is a cube, let's create some different colored ones so that we know the difference.

In reality, these would probably be different models that look differently all together. But for now, let's take what we have.

protected override void OnLoad(EventArgs e)
{
    VSync = VSyncMode.Off;
    CreateProjection();
    _renderObjects.Add(new RenderObject(ObjectFactory.CreateSolidCube(0.2f, Color4.HotPink)));
    _renderObjects.Add(new RenderObject(ObjectFactory.CreateSolidCube(0.2f, Color4.BlueViolet)));
    _renderObjects.Add(new RenderObject(ObjectFactory.CreateSolidCube(0.2f, Color4.Red)));
    _renderObjects.Add(new RenderObject(ObjectFactory.CreateSolidCube(0.2f, Color4.LimeGreen)));

    CursorVisible = true;

    _program = CreateProgram();
    GL.PolygonMode(MaterialFace.FrontAndBack, PolygonMode.Line);
    GL.PatchParameter(PatchParameterInt.PatchVertices, 3);
    GL.Enable(EnableCap.DepthTest);
    Closed += OnClosed;
}

So, now, we have 4 different RenderObjects created, each containing Vertex Arrays and Buffers. The next step is to draw them on the screen. But we don't want to just draw one of each, let's draw a bunch.

protected override void OnRenderFrame(FrameEventArgs e)
{
    _time += e.Time;
    Title = $"{_title}: (Vsync: {VSync}) FPS: {1f / e.Time:0}, z:{_z}";
    GL.ClearColor(_backColor);
    GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);

    GL.UseProgram(_program);
    GL.UniformMatrix4(20, false, ref _projectionMatrix);
    float c = 0f;
    foreach (var renderObject in _renderObjects)
    {
        renderObject.Bind();
        for (int i = 0; i < 5; i++)
        {
            var k = i + (float)(_time * (0.05f + (0.1 * c)));
            var t2 = Matrix4.CreateTranslation(
                (float)(Math.Sin(k * 5f) * (c + 0.5f)),
                (float)(Math.Cos(k * 5f) * (c + 0.5f)),
                _z);
            var r1 = Matrix4.CreateRotationX(k * 13.0f + i);
            var r2 = Matrix4.CreateRotationY(k * 13.0f + i);
            var r3 = Matrix4.CreateRotationZ(k * 3.0f + i);
            var modelView = r1 * r2 * r3 * t2;
            GL.UniformMatrix4(21, false, ref modelView);
            renderObject.Render();
        }
        c += 0.3f;
    }
    GL.PointSize(10);
    SwapBuffers();
}

The projection matrix will stay the same during the whole frame, so we need only to set it once before the render loop.

Then for each model that we loaded (different colored cube in our case), let's apply some transforms and rotations, each object gets a unique model view matrix that we set.

Note that the same RenderObject is bound once but rendered multiple times, just with different translations.

In the RenderObject, we need to split the old Render method into a Render and Bind method for this to work.

public void Bind()
{
    GL.BindVertexArray(_vertexArray);
}
public void Render()
{
    GL.DrawArrays(PrimitiveType.Triangles, 0, _verticeCount);
}

The end results should be like this:

Full video: https://youtu.be/4st0qiSAGtU

Hope this helps someone out there. :)

No cat video today either, it seems to be an every other post thing currently.

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)

Share

About the Author

Eowind
Architect
Sweden Sweden
http://dreamstatecoding.blogspot.com

You may also be interested in...

Comments and Discussions

 
-- There are no messages in this forum --
Permalink | Advertise | Privacy | Terms of Use | Mobile
Web02 | 2.8.170217.1 | Last Updated 5 Feb 2017
Article Copyright 2017 by Eowind
Everything else Copyright © CodeProject, 1999-2017
Layout: fixed | fluid