Examples » Your First Triangle

Basic rendering with builtin shaders.


The Hello World of 3D graphics, rendering a single colored triangle using OpenGL.

Continuing from the Getting Started Guide, where we used basically just the Platform::Sdl2Application class and clearing the GL::DefaultFramebuffer, we'll now additionally need GL::Mesh for encapsulating our triangle, GL::Buffer to store vertex data, and the Shaders::VertexColor2D shader which will take care of rendering.

#include <Magnum/GL/Buffer.h>
#include <Magnum/GL/DefaultFramebuffer.h>
#include <Magnum/GL/Mesh.h>
#include <Magnum/Math/Color.h>
#include <Magnum/Platform/Sdl2Application.h>
#include <Magnum/Shaders/VertexColor.h>

The subclassed application looks very similar, only additionally containing the shader and a mesh instance. You can read more about platform support and various portability tricks in Platform support.

class TriangleExample: public Platform::Application {
        explicit TriangleExample(const Arguments& arguments);

        void drawEvent() override;

        GL::Mesh _mesh;
        Shaders::VertexColor2D _shader;

In the constructor we pass arguments to the application class. The Configuration configuration is optional and allows us to set a window title and other things.

TriangleExample::TriangleExample(const Arguments& arguments):
    Platform::Application{arguments, Configuration{}.setTitle("Magnum Triangle Example")}

Now we specify vertex attributes, consisting of positions and colors. For performance reasons it is common to interleave them, so data for each vertex are in one continuous place in memory. In this case, because we need just three vertices, we will interleave them manually in-place. In the next tutorial we will learn how to interleave them programatically. See Math type system for more information about scalar and vector types used in Magnum — one of the notable convenience features is an ability to use a custom literal to specify hexadecimal colors, just like you are used to from CSS and various graphics editors.

    using namespace Math::Literals;

    struct TriangleVertex {
        Vector2 position;
        Color3 color;
    const TriangleVertex data[]{
        {{-0.5f, -0.5f}, 0xff0000_rgbf},    /* Left vertex, red color */
        {{ 0.5f, -0.5f}, 0x00ff00_rgbf},    /* Right vertex, green color */
        {{ 0.0f,  0.5f}, 0x0000ff_rgbf}     /* Top vertex, blue color */

We then create vertex buffer and fill it with data.

    GL::Buffer buffer;

Now we configure the mesh. The mesh is GL::MeshPrimitive::Triangles by default, so we need to specify just vertex count and add the vertex buffer with specified attribute locations for use with the shader. We need to describe physical location of vertex attributes in the buffer — zero offset from the beginning, first the position, then the color; with no gaps in between. Because we use a 2D shader, position is implicitly a two-component float vector and we use three-component RGB colors (we could also say Shaders::VertexColor::Color4 for RGBA).

Here we use std::move() instead of just passing it by reference to make the mesh an owner of the buffer. In this case it simplifies things for us, otherwise we would need to ensure buffer exists for the whole mesh lifetime. On the other hand, we lose the ability to modify that buffer afterwards.

         .addVertexBuffer(std::move(buffer), 0,

The drawEvent() function will take care of rendering the scene. We will clear color buffer of the default framebuffer (which is also the default rendering target) and then we draw the mesh using our shader. The context is double-buffered, so we need to swap the buffers after drawing.

void TriangleExample::drawEvent() {



Lastly, we need a main() function. The MAGNUM_APPLICATION_MAIN() macro will handle that conveniently for us, silently covering platform differences in the background:


That's all, now we can compile the whole example using CMake. Continuing from the Getting Started Guide, the only addition is finding & linking the Magnum::Shaders library that's now being used as well:

find_package(Corrade REQUIRED Main)
find_package(Magnum REQUIRED GL Shaders Sdl2Application)


add_executable(magnum-triangle WIN32 TriangleExample.cpp)
target_link_libraries(magnum-triangle PRIVATE

You can now try changing vertex count, positions or colors to see how the shader behaves. The full file content is linked below. Full source code is also available in the magnum-examples GitHub repository.

The ports branch contains additional patches for iOS, Android and Emscripten support that aren't present in master in order to keep the example code as simple as possible.