Filament Rendering Engine¶
Overview¶
Filament is a real-time physically based rendering (PBR) engine designed for Android, iOS, Windows, Linux, macOS and WebGL. It is designed to be as small as possible and as efficient as possible.
MINE Indoor Navigation Engine leverages Filament to deliver high-performance 3D map rendering with realistic lighting, shadows, and material systems optimized for indoor navigation scenarios.
-
High Performance
Optimized rendering pipeline with minimal overhead, achieving 60+ FPS on mid-range devices
-
Physically Based
Industry-standard PBR workflow with accurate material representation and lighting
-
Cross-Platform
Write once, deploy everywhere with consistent rendering across all platforms
-
Lightweight
Small binary footprint (~2MB) with minimal runtime memory overhead
Why Filament for Indoor Navigation?¶
MINE chose Filament as its rendering foundation for several critical reasons:
1. Mobile-First Architecture¶
Filament is designed from the ground up for mobile constraints, ensuring smooth performance even on budget Android devices (API 24+).
2. Real-Time Rendering¶
Indoor navigation requires instant visual feedback. Filament's deferred rendering pipeline delivers consistent frame rates during camera transitions and floor changes.
3. Advanced Material System¶
Support for custom materials allows MINE to represent different venue surfaces (marble floors, carpet, glass, metal) with realistic visual properties.
4. Dynamic Lighting¶
Ambient occlusion and shadow mapping enhance spatial awareness, helping users identify landmarks and navigate complex multi-floor structures.
Architecture Integration¶
Rendering Pipeline¶
graph LR
A[Map JSON] --> B[Scene Builder]
B --> C[Filament Engine]
C --> D[GPU Renderer]
D --> E[SceneView]
E --> F[User Display]
G[Camera Controller] --> C
H[Material Assets] --> C
I[Light Sources] --> C
MINE's Filament Setup¶
The MINE SDK initializes Filament with optimized defaults for indoor scenarios:
// Engine initialization with custom configuration
val engine = Engine.create(
EngineBackend.DEFAULT,
Config().apply {
stereoscopicEyeCount = 1
resourceAllocatorCacheSizeMB = 16
resourceAllocatorCacheMaxAge = 2
}
)
// Scene setup for indoor navigation
val scene = engine.createScene().apply {
skybox = null // Indoor scenes rarely need skybox
indirectLight = iblBuilder.build(engine)
}
// Camera configuration for top-down navigation view
val camera = engine.createCamera(engine.entityManager.create()).apply {
setProjection(45.0, aspect, 0.1, 100.0, Camera.Fov.VERTICAL)
lookAt(
eyeX = position.x, eyeY = position.y + 10.0, eyeZ = position.z,
centerX = position.x, centerY = 0.0, centerZ = position.z,
upX = 0.0, upY = 0.0, upZ = -1.0
)
}
Key Features Used by MINE¶
Material System¶
MINE leverages Filament's PBR materials to differentiate venue zones:
// Floor material with custom properties
val floorMaterial = MaterialInstance(materialAsset).apply {
setParameter("baseColor", Color(0.9f, 0.9f, 0.95f, 1.0f))
setParameter("metallic", 0.0f)
setParameter("roughness", 0.6f)
setParameter("reflectance", 0.5f)
}
// POI marker material with emissive glow
val markerMaterial = MaterialInstance(markerAsset).apply {
setParameter("baseColor", themeColor)
setParameter("emissive", Colors.RgbaType(themeColor, 2.0f)) // Intensity
setParameter("metallic", 0.8f)
setParameter("roughness", 0.2f)
}
Dynamic Lighting¶
// Ambient light for general illumination
val ambientLight = EntityManager.get().create()
LightManager.Builder(LightManager.Type.SUN)
.color(1.0f, 1.0f, 0.98f)
.intensity(50000.0f) // Lux
.direction(0.0f, -1.0f, 0.2f)
.castShadows(true)
.build(engine, ambientLight)
scene.addEntity(ambientLight)
Camera Controls¶
// Smooth camera transitions for floor changes
fun animateToFloor(targetFloor: Int, duration: Long = 500) {
ValueAnimator.ofFloat(0f, 1f).apply {
this.duration = duration
interpolator = DecelerateInterpolator()
addUpdateListener { animator ->
val progress = animator.animatedValue as Float
camera.setModelMatrix(
lerp(currentTransform, targetTransform, progress)
)
}
start()
}
}
Performance Optimizations¶
1. Frustum Culling¶
Only entities within the camera view are rendered, dramatically reducing draw calls in large venues.
// Automatic culling by Filament
scene.setFrustumCullingEnabled(true)
2. Level of Detail (LOD)¶
MINE implements distance-based LOD for complex 3D assets:
fun selectLOD(distanceToCamera: Float): RenderableAsset {
return when {
distanceToCamera < 10f -> highDetailModel
distanceToCamera < 30f -> mediumDetailModel
else -> lowDetailModel
}
}
3. Texture Compression¶
Using ETC2/ASTC formats reduces memory footprint by 4-6x:
val textureBuilder = Texture.Builder()
.format(Texture.InternalFormat.ETC2_RGB8) // Compressed format
.levels(mipLevels)
.sampler(Texture.Sampler.SAMPLER_2D)
Rendering Metrics¶
| Metric | Target | Achieved on Pixel 4a |
|---|---|---|
| Frame Rate | 60 FPS | 62 FPS (1) |
| Frame Time | <16.67ms | 14ms avg |
| Draw Calls | <500 | 280 avg |
| GPU Memory | <150MB | 120MB |
| Initialization Time | <500ms | 380ms |
- Tested with a 5-floor venue, 200+ POIs, dynamic lighting enabled
Advanced Topics¶
Custom Shaders¶
For specialized effects, MINE includes custom material definitions:
// Custom gradient material for floor highlighting
void material(inout MaterialInputs material) {
prepareMaterial(material);
vec2 uv = getUV0();
vec3 baseColor = mix(uBottomColor, uTopColor, uv.y);
material.baseColor = vec4(baseColor, 1.0);
material.metallic = uMetallic;
material.roughness = uRoughness;
material.emissive = vec4(baseColor * uEmissiveStrength, 1.0);
}
Shadow Mapping¶
// Configure shadow rendering
val shadowOptions = View.ShadowOptions().apply {
mapSize = 1024
shadowCascades = 3
shadowFarHint = 50.0f
screenSpaceContactShadows = true
}
view.setShadowOptions(shadowOptions)
Post-Processing¶
// Bloom effect for glowing POI markers
val bloomOptions = View.BloomOptions().apply {
enabled = true
threshold = 1.0f
strength = 0.15f
resolution = 512
}
view.setBloomOptions(bloomOptions)
Debugging & Profiling¶
Render Stats¶
// Enable debug overlay
view.setRenderTarget(View.RenderTarget.COLOR_AND_DEPTH)
view.setDebugOverlay(View.DebugOverlay.SHOW_OVERDRAW)
// Query performance metrics
val stats = renderer.userTime
Log.d("Filament", "Render time: ${stats}ms")
Common Issues¶
GPU Compatibility
Filament requires OpenGL ES 3.0+ or Vulkan. Devices older than 2014 may not support all features.
Memory Management
Always destroy Filament resources explicitly to prevent leaks:
override fun onDestroy() {
engine.destroyEntity(entity)
engine.destroyScene(scene)
engine.destroy()
super.onDestroy()
}
Platform-Specific Notes¶
// Initialize with SurfaceView
val surfaceView = SurfaceView(context)
val uiHelper = UiHelper(UiHelper.ContextErrorPolicy.DONT_CHECK).apply {
renderCallback = SurfaceCallback()
attachTo(surfaceView)
}
// Using LWJGL backend
val window = glfwCreateWindow(1280, 720, "MINE Viewer", NULL, NULL)
glfwMakeContextCurrent(window)
val engine = Engine.create(Engine.Backend.OPENGL)
Currently, MINE focuses on Android. iOS support via Kotlin Multiplatform is planned for Q2 2025.
Resources¶
-
Official Repository
-
API Documentation
-
Material Guide
-
Sample Projects
Next Steps¶
- SceneView API: Learn how MINE wraps Filament in a Compose-friendly component
- Theme Customization: Customize materials and lighting for your brand
- Performance Tuning: Optimize rendering for your target devices
Last updated: December 2024 | Filament version: 1.46.0