Overview
This guide will walk you through creating your first Filament application. We’ll render a simple spinning colored triangle to demonstrate the core concepts of the Filament rendering engine.Before starting, make sure you have Filament installed on your platform. See the Installation Guide for details.
Choose Your Platform
- C++ (Desktop)
- Android (Kotlin)
- iOS (Swift/C++)
This example works on Linux, macOS, and Windows using the native C++ API.
Prerequisites
- CMake 3.22.1 or higher
- Clang 16.0 or higher
- Filament SDK installed
- SDL2 (included with Filament samples)
Core Concepts
A Filament application requires these essential components:Create an Engine
The
Engine is the main entry point and manages all Filament resources.Engine* engine = Engine::create();
Create a SwapChain
The
SwapChain connects Filament to your native window.SwapChain* swapChain = engine->createSwapChain(nativeWindow);
Create a Renderer
The
Renderer handles the actual rendering commands.Renderer* renderer = engine->createRenderer();
Complete Hello Triangle Example
Here’s a complete working example from the Filament samples:samples/hellotriangle.cpp
#include <filament/Camera.h>
#include <filament/Engine.h>
#include <filament/IndexBuffer.h>
#include <filament/Material.h>
#include <filament/RenderableManager.h>
#include <filament/Scene.h>
#include <filament/VertexBuffer.h>
#include <filament/View.h>
#include <utils/EntityManager.h>
using namespace filament;
using utils::Entity;
using utils::EntityManager;
// Define vertex structure
struct Vertex {
filament::math::float2 position;
uint32_t color;
};
// Triangle vertices (colored RGB)
static const Vertex TRIANGLE_VERTICES[3] = {
{{1, 0}, 0xffff0000u}, // Red
{{cos(M_PI * 2 / 3), sin(M_PI * 2 / 3)}, 0xff00ff00u}, // Green
{{cos(M_PI * 4 / 3), sin(M_PI * 4 / 3)}, 0xff0000ffu}, // Blue
};
static constexpr uint16_t TRIANGLE_INDICES[3] = { 0, 1, 2 };
// Setup function
auto setup = [](Engine* engine, View* view, Scene* scene) {
// Create a simple skybox
Skybox* skybox = Skybox::Builder()
.color({0.1, 0.125, 0.25, 1.0})
.build(*engine);
scene->setSkybox(skybox);
// Create vertex buffer
VertexBuffer* 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);
vb->setBufferAt(*engine, 0,
VertexBuffer::BufferDescriptor(TRIANGLE_VERTICES, 36, nullptr));
// Create index buffer
IndexBuffer* ib = IndexBuffer::Builder()
.indexCount(3)
.bufferType(IndexBuffer::IndexType::USHORT)
.build(*engine);
ib->setBuffer(*engine,
IndexBuffer::BufferDescriptor(TRIANGLE_INDICES, 6, nullptr));
// Create material (using pre-compiled material)
Material* mat = Material::Builder()
.package((void*) BAKED_MATERIAL_PACKAGE, sizeof(BAKED_MATERIAL_PACKAGE))
.build(*engine);
// Create renderable entity
Entity renderable = EntityManager::get().create();
RenderableManager::Builder(1)
.boundingBox({{ -1, -1, -1 }, { 1, 1, 1 }})
.material(0, mat->getDefaultInstance())
.geometry(0, RenderableManager::PrimitiveType::TRIANGLES, vb, ib, 0, 3)
.culling(false)
.receiveShadows(false)
.castShadows(false)
.build(*engine, renderable);
scene->addEntity(renderable);
};
// Render loop
if (renderer->beginFrame(swapChain)) {
renderer->render(view);
renderer->endFrame();
}
Building and Running
This example demonstrates Filament on Android using Kotlin.
Prerequisites
- Android Studio Flamingo or newer
- Android SDK
- Filament Android library (via Maven)
- Minimum SDK version depends on features used
Add Dependencies
Add Filament to yourbuild.gradle:build.gradle
repositories {
mavenCentral()
}
dependencies {
implementation 'com.google.android.filament:filament-android:1.69.4'
implementation 'com.google.android.filament:filament-utils-android:1.69.4'
}
Initialize Filament
Always initialize Filament before using any Filament APIs:
MainActivity.kt
class MainActivity : Activity() {
companion object {
init {
Filament.init()
}
}
}
Complete Hello Triangle Example
Here’s the complete working example from the Filament samples:MainActivity.kt
package com.google.android.filament.hellotriangle
import android.animation.ValueAnimator
import android.app.Activity
import android.opengl.Matrix
import android.os.Bundle
import android.view.Choreographer
import android.view.Surface
import android.view.SurfaceView
import android.view.animation.LinearInterpolator
import com.google.android.filament.*
import com.google.android.filament.RenderableManager.PrimitiveType
import com.google.android.filament.VertexBuffer.AttributeType
import com.google.android.filament.VertexBuffer.VertexAttribute
import com.google.android.filament.android.UiHelper
import java.nio.ByteBuffer
import java.nio.ByteOrder
import kotlin.math.PI
import kotlin.math.cos
import kotlin.math.sin
class MainActivity : Activity() {
companion object {
init { Filament.init() }
}
private lateinit var surfaceView: SurfaceView
private lateinit var uiHelper: UiHelper
private lateinit var choreographer: Choreographer
private lateinit var engine: Engine
private lateinit var renderer: Renderer
private lateinit var scene: Scene
private lateinit var view: View
private lateinit var camera: Camera
private lateinit var material: Material
private lateinit var vertexBuffer: VertexBuffer
private lateinit var indexBuffer: IndexBuffer
@Entity private var renderable = 0
private var swapChain: SwapChain? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
surfaceView = SurfaceView(this)
setContentView(surfaceView)
choreographer = Choreographer.getInstance()
setupSurfaceView()
setupFilament()
setupView()
setupScene()
}
private fun setupSurfaceView() {
uiHelper = UiHelper(UiHelper.ContextErrorPolicy.DONT_CHECK)
uiHelper.renderCallback = SurfaceCallback()
uiHelper.attachTo(surfaceView)
}
private fun setupFilament() {
engine = Engine.Builder()
.featureLevel(Engine.FeatureLevel.FEATURE_LEVEL_0)
.build()
renderer = engine.createRenderer()
scene = engine.createScene()
view = engine.createView()
camera = engine.createCamera(engine.entityManager.create())
}
private fun setupView() {
scene.skybox = Skybox.Builder()
.color(0.035f, 0.035f, 0.035f, 1.0f)
.build(engine)
view.camera = camera
view.scene = scene
}
private fun setupScene() {
loadMaterial()
createMesh()
renderable = EntityManager.get().create()
RenderableManager.Builder(1)
.boundingBox(Box(0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.01f))
.geometry(0, PrimitiveType.TRIANGLES, vertexBuffer, indexBuffer, 0, 3)
.material(0, material.defaultInstance)
.build(engine, renderable)
scene.addEntity(renderable)
startAnimation()
}
private fun createMesh() {
val floatSize = 4
val intSize = 4
val vertexSize = 3 * floatSize + intSize
data class Vertex(val x: Float, val y: Float, val z: Float, val color: Int)
fun ByteBuffer.put(v: Vertex): ByteBuffer {
putFloat(v.x)
putFloat(v.y)
putFloat(v.z)
putInt(v.color)
return this
}
val vertexCount = 3
val a1 = PI * 2.0 / 3.0
val a2 = PI * 4.0 / 3.0
val vertexData = ByteBuffer.allocate(vertexCount * vertexSize)
.order(ByteOrder.nativeOrder())
.put(Vertex(1.0f, 0.0f, 0.0f, 0xffff0000.toInt()))
.put(Vertex(cos(a1).toFloat(), sin(a1).toFloat(), 0.0f, 0xff00ff00.toInt()))
.put(Vertex(cos(a2).toFloat(), sin(a2).toFloat(), 0.0f, 0xff0000ff.toInt()))
.flip()
vertexBuffer = VertexBuffer.Builder()
.bufferCount(1)
.vertexCount(vertexCount)
.attribute(VertexAttribute.POSITION, 0, AttributeType.FLOAT3, 0, vertexSize)
.attribute(VertexAttribute.COLOR, 0, AttributeType.UBYTE4, 3 * floatSize, vertexSize)
.normalized(VertexAttribute.COLOR)
.build(engine)
vertexBuffer.setBufferAt(engine, 0, vertexData)
val indexData = ByteBuffer.allocate(vertexCount * 2)
.order(ByteOrder.nativeOrder())
.putShort(0).putShort(1).putShort(2)
.flip()
indexBuffer = IndexBuffer.Builder()
.indexCount(3)
.bufferType(IndexBuffer.Builder.IndexType.USHORT)
.build(engine)
indexBuffer.setBuffer(engine, indexData)
}
private fun startAnimation() {
val animator = ValueAnimator.ofFloat(0.0f, 360.0f)
animator.interpolator = LinearInterpolator()
animator.duration = 4000
animator.repeatMode = ValueAnimator.RESTART
animator.repeatCount = ValueAnimator.INFINITE
animator.addUpdateListener { a ->
val transformMatrix = FloatArray(16)
Matrix.setRotateM(transformMatrix, 0, -(a.animatedValue as Float), 0.0f, 0.0f, 1.0f)
val tcm = engine.transformManager
tcm.setTransform(tcm.getInstance(renderable), transformMatrix)
}
animator.start()
}
inner class SurfaceCallback : UiHelper.RendererCallback {
override fun onNativeWindowChanged(surface: Surface) {
swapChain = engine.createSwapChain(surface)
}
override fun onDetachedFromSurface() {
swapChain?.let { engine.destroySwapChain(it) }
}
override fun onResized(width: Int, height: Int) {
val zoom = 1.5
val aspect = width.toDouble() / height.toDouble()
camera.setProjection(Camera.Projection.ORTHO,
-aspect * zoom, aspect * zoom, -zoom, zoom, 0.0, 10.0)
view.viewport = Viewport(0, 0, width, height)
}
}
override fun onDestroy() {
super.onDestroy()
// Clean up resources
engine.destroyEntity(renderable)
engine.destroyRenderer(renderer)
engine.destroyVertexBuffer(vertexBuffer)
engine.destroyIndexBuffer(indexBuffer)
engine.destroyMaterial(material)
engine.destroyView(view)
engine.destroyScene(scene)
engine.destroy()
}
}
Building and Running
Filament on iOS uses the native C++ API, typically bridged through Objective-C++ or Swift.Then install:
Prerequisites
- Xcode (latest version recommended)
- iOS 11.0 or higher
- CocoaPods
Add Filament to Your Project
Add to yourPodfile:Podfile
pod 'Filament', '~> 1.69.4'
pod install
Key Differences from Desktop
iOS uses either aCAEAGLLayer (OpenGL ES) or CAMetalLayer (Metal, preferred) for the swap chain:// Metal (preferred)
SwapChain* swapChain = engine->createSwapChain(metalLayer);
// OpenGL ES
SwapChain* swapChain = engine->createSwapChain(eaglLayer);
The core rendering code is identical to the desktop C++ example above. See the
ios/samples directory in the Filament repository for complete working examples.Understanding the Code
Entity Component System
Filament uses an Entity Component System (ECS) architecture:- Entities are lightweight identifiers (just integers)
- Components store data and behavior (like
RenderableManager,TransformManager) - Systems process components (handled internally by Filament)
// Create an entity
Entity entity = EntityManager::get().create();
// Add a renderable component to it
RenderableManager::Builder(1)
.geometry(0, ...)
.build(*engine, entity);
Vertex Buffers
Vertex buffers define the geometry:VertexBuffer* vb = VertexBuffer::Builder()
.vertexCount(3) // 3 vertices
.bufferCount(1) // 1 interleaved buffer
.attribute(VertexAttribute::POSITION, 0, AttributeType::FLOAT2, 0, 12)
.attribute(VertexAttribute::COLOR, 0, AttributeType::UBYTE4, 8, 12)
.normalized(VertexAttribute::COLOR) // Normalize color to 0-1 range
.build(*engine);
Materials
Materials in Filament are compiled using thematc tool. For quick prototyping, you can use pre-compiled materials included with the samples.
See the Materials Guide for details on creating custom materials.
Next Steps
Load 3D Models
Learn how to load glTF 2.0 models with the gltfio library
Materials
Create custom materials with the material system
Lighting
Add lights and image-based lighting to your scene
Camera Controls
Implement camera movement and controls