Skip to main content
This tutorial walks you through creating your first Filament application that renders a spinning colored triangle. This is the simplest example demonstrating the core concepts of Filament’s rendering pipeline.

Overview

In this tutorial, you’ll learn how to:
  • Initialize the Filament engine
  • Create vertex and index buffers
  • Define a simple unlit material
  • Set up a camera and scene
  • Render and animate geometry

Complete Source Code

The complete example is available in samples/hellotriangle.cpp. Here’s the step-by-step breakdown:
1

Define the Application Structure

First, define a structure to hold all the Filament objects we’ll need:
struct App {
    Config config;
    VertexBuffer* vb;
    IndexBuffer* ib;
    Material* mat;
    Camera* cam;
    Entity camera;
    Skybox* skybox;
    Entity renderable;
};
This structure keeps track of our vertex buffer, index buffer, material, camera, and the renderable entity.
2

Define Vertex Data

Create a vertex structure and define the triangle vertices with positions and colors:
struct Vertex {
    filament::math::float2 position;
    uint32_t color;
};

static const Vertex TRIANGLE_VERTICES[3] = {
    {{1, 0}, 0xffff0000u},  // Red vertex
    {{cos(M_PI * 2 / 3), sin(M_PI * 2 / 3)}, 0xff00ff00u},  // Green vertex
    {{cos(M_PI * 4 / 3), sin(M_PI * 4 / 3)}, 0xff0000ffu},  // Blue vertex
};

static constexpr uint16_t TRIANGLE_INDICES[3] = { 0, 1, 2 };
The vertices form an equilateral triangle with different colors at each vertex. Colors are stored as RGBA in packed 32-bit format.
3

Create Vertex Buffer

Build a vertex buffer that describes the layout of vertex data:
app.vb = VertexBuffer::Builder()
        .vertexCount(3)
        .bufferCount(1)
        .attribute(VertexAttribute::POSITION, 0, VertexBuffer::AttributeType::FLOAT2, 0, 12)
        .attribute(VertexAttribute::COLOR, 0, VertexBuffer::AttributeType::UBYTE4, 8, 12)
        .normalized(VertexAttribute::COLOR)
        .build(*engine);
app.vb->setBufferAt(*engine, 0,
        VertexBuffer::BufferDescriptor(TRIANGLE_VERTICES, 36, nullptr));
Key points:
  • vertexCount(3): We have 3 vertices
  • attribute(): Defines position (2 floats at offset 0) and color (4 bytes at offset 8)
  • normalized(VertexAttribute::COLOR): Normalizes color from 0-255 to 0.0-1.0
  • Stride is 12 bytes (8 bytes for position + 4 bytes for color)
4

Create Index Buffer

Create an index buffer for the triangle:
app.ib = IndexBuffer::Builder()
        .indexCount(3)
        .bufferType(IndexBuffer::IndexType::USHORT)
        .build(*engine);
app.ib->setBuffer(*engine,
        IndexBuffer::BufferDescriptor(TRIANGLE_INDICES, 6, nullptr));
The index buffer defines the order in which vertices are connected to form triangles.
5

Create Material

Load the baked color material from precompiled resources:
app.mat = Material::Builder()
        .package(RESOURCES_BAKEDCOLOR_DATA, RESOURCES_BAKEDCOLOR_SIZE)
        .build(*engine);
This material is unlit and uses vertex colors. The material package was compiled using the matc tool.
6

Create Renderable Entity

Build a renderable entity that combines geometry, material, and rendering properties:
app.renderable = EntityManager::get().create();
RenderableManager::Builder(1)
        .boundingBox({{ -1, -1, -1 }, { 1, 1, 1 }})
        .material(0, app.mat->getDefaultInstance())
        .geometry(0, RenderableManager::PrimitiveType::TRIANGLES, app.vb, app.ib, 0, 3)
        .culling(false)
        .receiveShadows(false)
        .castShadows(false)
        .build(*engine, app.renderable);
scene->addEntity(app.renderable);
Important details:
  • Builder(1): We have 1 primitive (one set of geometry)
  • boundingBox(): Defines spatial bounds for culling
  • geometry(): Associates vertex/index buffers with the primitive
  • culling(false): Disables backface culling
7

Set Up Camera

Create and configure an orthographic camera:
app.camera = utils::EntityManager::get().create();
app.cam = engine->createCamera(app.camera);
view->setCamera(app.cam);
In the animation loop, update the camera projection:
constexpr float ZOOM = 1.5f;
const uint32_t w = view->getViewport().width;
const uint32_t h = view->getViewport().height;
const float aspect = (float) w / h;
app.cam->setProjection(Camera::Projection::ORTHO,
    -aspect * ZOOM, aspect * ZOOM,
    -ZOOM, ZOOM, 0, 1);
8

Add Animation

Animate the triangle by rotating it around the Z-axis:
FilamentApp::get().animate([&app](Engine* engine, View* view, double now) {
    auto& tcm = engine->getTransformManager();
    tcm.setTransform(tcm.getInstance(app.renderable),
            filament::math::mat4f::rotation(now, filament::math::float3{ 0, 0, 1 }));
});
The now parameter is time in seconds, creating a smooth rotation.
9

Set Up Scene and Skybox

Create a simple colored skybox:
app.skybox = Skybox::Builder().color({0.1, 0.125, 0.25, 1.0}).build(*engine);
scene->setSkybox(app.skybox);
view->setPostProcessingEnabled(false);
10

Cleanup Resources

Always destroy Filament objects in the cleanup callback:
auto cleanup = [&app](Engine* engine, View*, Scene*) {
    engine->destroy(app.skybox);
    engine->destroy(app.renderable);
    engine->destroy(app.mat);
    engine->destroy(app.vb);
    engine->destroy(app.ib);
    engine->destroyCameraComponent(app.camera);
    utils::EntityManager::get().destroy(app.camera);
};
11

Run the Application

Initialize and run the application:
int main(int argc, char** argv) {
    App app{};
    app.config.title = "hellotriangle";
    app.config.featureLevel = backend::FeatureLevel::FEATURE_LEVEL_0;
    
    FilamentApp::get().run(app.config, setup, cleanup);
    return 0;
}

Key Concepts

Vertex Attributes

Filament supports various vertex attributes:
  • POSITION: Vertex position in object space
  • COLOR: Per-vertex color (can be normalized)
  • UV0/UV1: Texture coordinates
  • TANGENTS: Tangent frame for normal mapping

Coordinate System

Filament uses a right-handed coordinate system:
  • X-axis points right
  • Y-axis points up
  • Z-axis points toward the viewer

Feature Levels

This example uses FEATURE_LEVEL_0, the most basic feature level that works on all platforms including WebGL 1.0.

Building and Running

# Build the sample
cmake --build . --target hellotriangle

# Run the sample
./hellotriangle

Next Steps

Build docs developers (and LLMs) love