Your first triangle
Basic rendering with builtin shaders.
The Hello World of 3D graphics, rendering a single colored triangle using OpenGL.
For this example we will use SDL2, which is the most widely used multiplatform windowing toolkit. Support for it is in the Platform::
#include <Magnum/GL/Buffer.h> #include <Magnum/GL/DefaultFramebuffer.h> #include <Magnum/GL/Mesh.h> #include <Magnum/Platform/Sdl2Application.h> #include <Magnum/Shaders/VertexColor.h>
We subclass the application and next to the constructor we implement the one single required function which is needed for rendering into the window. You can read more about platform support and various portability tricks in Platform support.
class TriangleExample: public Platform::Application { public: explicit TriangleExample(const Arguments& arguments); private: void drawEvent() override; GL::Buffer _buffer; GL::Mesh _mesh; Shaders::VertexColor2D _shader; };
In the constructor we pass all necessary arguments to the application class.
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 Type system for more information about scalar and vector types used in Magnum — one of the notable convenience features is 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. The data won't be changed or read back into main memory during application lifetime, so we mark them with appropriate GL::
_buffer.setData(data, GL::BufferUsage::StaticDraw);
Now we configure the mesh — we specify GL::
_mesh.setPrimitive(GL::MeshPrimitive::Triangles) .setCount(3) .addVertexBuffer(_buffer, 0, Shaders::VertexColor2D::Position{}, Shaders::VertexColor2D::Color{Shaders::VertexColor2D::Color::Components::Three}); }
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() { GL::defaultFramebuffer.clear(GL::FramebufferClear::Color); _mesh.draw(_shader); swapBuffers(); }
Lastly, we need a main()
function. The MAGNUM_
MAGNUM_APPLICATION_MAIN(TriangleExample)
That's all, now we can compile the whole example using CMake. First we require the Magnum
package with GL
, Shaders
and Sdl2Application
components. It's recommended to use also Corrade's set of compiler flags to enable additional warnings. Then we create our executable and link to all Magnum libraries we requested. See Usage with CMake for more information.
find_package(Magnum REQUIRED GL Shaders Sdl2Application) set_directory_properties(PROPERTIES CORRADE_USE_PEDANTIC_FLAGS ON) add_executable(magnum-triangle TriangleExample.cpp) target_link_libraries(magnum-triangle PRIVATE Magnum::Application Magnum::GL Magnum::Magnum Magnum::Shaders)
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.