back to index

rendering bim in ar

Problem: BIM models aren't built for real-time rendering. They're optimized for accuracy and information, not performance. Try to render a complete building on a mobile phone and you'll get single-digit frame rates.

Solution: A rendering pipeline that bridges BIM's precision and AR's performance demands. The goal was 60fps on mobile with filtering, selection, and real-time updates.

Pipeline

This separation means changing one stage doesn't break another.

Mesh generation

BIM elements arrive as raw data: vertex arrays and index lists. Each element type contains packed vertex data (position, normal, UV in sequence) and submeshes that define triangle lists with associated colors. A single door might have three submeshes: frame, panel, and glass, each with different materials.

Mesh mesh = new Mesh();
mesh.vertices = ConvertToVector3Array(elementType.vertices);

mesh.subMeshCount = elementType.submeshes.Length;

for (int i = 0; i < elementType.submeshes.Length; i++)
{
    mesh.SetTriangles(elementType.submeshes[i].indices, i);
}

mesh.RecalculateNormals();
mesh.RecalculateBounds();

Normal recalculation happens once during import because BIM data sometimes lacks normals or has incorrect ones.

Material handling

Rather than creating unique materials for every element (which would destroy batching), the system uses template materials.

Material baseMaterial = Resources.Load<Material>("Materials/Template_Mtl");

foreach (Submesh submesh in elementType.submeshes)
{
    Material instanceMaterial = new Material(baseMaterial);
    instanceMaterial.color = submesh.color;
    materials.Add(instanceMaterial);
}

MeshRenderer renderer = prefab.AddComponent<MeshRenderer>();
renderer.materials = materials.ToArray();
Shared shaders batch effectively while per-element colors enable visual distinction.

Category-based filtering

Construction professionals don't want to see everything at once. The importer handles 23 distinct BIM categories using a flags enum:

public enum Category
{
    None = 0,
    Wall = 1 << 1,
    Floor = 1 << 2,
    Door = 1 << 3,
    Window = 1 << 4,
    Structure = 1 << 5,
    InstallationMechanical = 1 << 6,
    // ... 17 more categories
}
Check multiple categories with bitwise operations. Toggle visibility without traversing the entire hierarchy.
public void SetCategoryVisibility(Category categories, bool visible)
{
    foreach (Transform categoryTransform in categoryTransforms.Values)
    {
        Category cat = (Category)Enum.Parse(typeof(Category), 
            categoryTransform.name);
        
        if ((categories & cat) != 0)
        {
            categoryTransform.gameObject.SetActive(visible);
        }
    }
}

Storeys provide additional spatial filtering. A ten-storey building has ten times the geometry of one floor. If you're on floor three, rendering all floors wastes GPU cycles on geometry you can't see. Each storey loads independently with UI controls for toggling visibility. Combined with categories, "show mechanical systems on ground floor" becomes a simple filter operation. Two toggles instead of traversing thousands of elements.

Performance

Mobile AR demands efficiency. The phone is already running camera processing, AR tracking, and the regular OS workload. What's left for rendering BIM geometry has to be used carefully.

GPU instancing batches similar elements when they use identical materials. This is why the type library pattern matters: 500 doors using the same type become one draw call instead of 500. Occlusion culling prevents rendering hidden geometry. BIM models have many occluded elements; walls hide interior systems.

Unity's layer system separates objects for rendering and raycasting. Shell objects render to one depth buffer, inside objects to another. The composition shader compares both to determine visibility. The BIM model renders to texture, not directly to screen, enabling depth-based occlusion and edge blending with the camera feed.

Thermal throttling is the silent killer on mobile. A phone that starts at 60fps drops to 30fps after five minutes of sustained load. Frame budget monitoring adapts quality settings to device capabilities. These optimizations maintain performance across device generations.

Model controller

The ModelController provides a clean API for the rest of the application: category visibility, element selection, metadata queries, transform manipulation. Other systems don't need to know about hierarchy organization, layer assignments, or coordinate corrections.

The result is practical AR-BIM on mobile devices, running at 60fps with full filtering and selection.