Over the years, I have worked on numerous real-time and digital-twin projects worldwide, developing a deep expertise in performance optimization for Unreal Engine environments. Many of these projects involved highly complex architectural datasets; often originating from tools like Autodesk Navisworks, which are designed for engineering and construction rather than real-time visualization. As a result, these models typically lack critical game-engine requirements such as UV maps, proper topology, and optimized geometry, making performance optimization an essential part of the pipeline.
When we talk about performance optimization, it’s important to remember that the process always begins with performance profiling. Before making any adjustments, we must first diagnose where the bottlenecks lie; whether they stem from heavy geometry, inefficient materials, or rendering overhead. Only after identifying the root cause can we apply targeted solutions to achieve optimal frame rates and stability.
In this section, I will outline my approach to diagnosing and resolving performance issues in Unreal Engine, with a focus on three primary areas:
Geometry Optimization
Texture & Material Optimization
Rendering & Shading Optimization
Through this process, I aim to transform raw, unoptimized architectural datasets into high-performance, visually accurate, and responsive Unreal Engine environments ready for interactive use or cinematic presentation.
Before diving into optimization, the first and most essential step is performance profiling. Without identifying the actual cause of slowdowns, optimization efforts can be inefficient or even counterproductive. Profiling allows us to determine whether the performance issue is GPU bound, CPU bound, or occasionally, both.
A simple and effective way to perform a quick health check inside Unreal Engine is by using the command:
"stat unit"
stat unit command
This command displays key timing values for Frame, Game, Draw, and GPU, helping us understand which part of the system is the bottleneck:
If GPU ≈ Frame and is the highest → GPU bound
The rendering hardware is at its limit (typically caused by complex shaders, heavy lighting, or excessive geometry).
If Game or Draw is highest → CPU bound
The bottleneck lies in the game thread or draw thread (often due to too many draw calls, unoptimized blueprints, or tick-heavy logic).
If Game < Draw < GPU → probably GPU bottleneck
The GPU is most likely the limiting factor, with moderate CPU overhead still present.
Once the bottleneck is identified, we can take immediate targeted actions before moving into detailed optimization steps:
If GPU bound:
Investigate complex materials, large render targets, post-process effects, Nanite usage, and screen resolution. Also, review anti-aliasing methods such as MSAA. Next diagnostic step: use "stat gpu" or "profilegpu" (the GPU Visualizer) to break down GPU cost per pass:
stat gpu visualizing lighting issue
profile gpu illustrating the lighting issue with more details
If a deeper investigation is required, we can use the Unreal Insights tool by starting a trace, then opening it and looking for spikes. Checking each tick will help diagnose the issue in more depth.
starting trace
opening the trace to check the spikes and ticks
If CPU bound:
Focus on reducing draw calls, combining small meshes, enabling occlusion culling, baking animation when possible, offloading work to worker threads, and minimizing tick cost.
By profiling first, we gain clear insight into where time is being spent each frame. This ensures that every optimization that follows (whether geometry, texture, or rendering related) is based on solid data and produces measurable results.
Now that the bottlenecks have been identified through profiling, it’s time to address them systematically. As mentioned earlier, I categorize Unreal Engine performance optimization into three primary areas: Geometry Optimization, Texture & Material Optimization, and Rendering & Lighting Optimization. Each area targets a specific set of potential bottlenecks that collectively determine the overall performance of a real-time environment.
Geometry plays a significant role in both CPU and GPU performance. Optimizing it ensures that assets maintain their visual quality while remaining lightweight and efficient for real-time rendering.
1. Poly Count Management
Every project should start with clear polygon budgets per asset. When there is flexibility, we can often reduce polygon density without compromising the silhouette or shading quality. For instance, in the Puma Shoes project, I modeled the LOD2 version with a polygon limit of 1,350 tris, achieving a strong balance between detail and performance.
2. Instancing Repeated Geometry
For elements that are identical (like chain links, bolts, or structural panels) using instanced meshes significantly reduces draw calls. This can be achieved both in Maya (through instancing, as applied in the Solar Panel Array setup around the Robotic Butterfly project.) and Unreal Engine (through instancing & level packing, as applied in the The Shadow of Musashi project, or using tools like Spline Mesh Components to procedurally generate repetitive geometry along splines).
3. Level of Detail (LOD) System
Implementing LODs ensures that assets reduce polygon complexity based on camera distance or screen size. This is particularly effective for subdivided models or high-fidelity geometry originally designed for offline rendering. Here is an example of the LOD system in Unreal (demonstrated using a can model I created in Blender to showcase proper topology and edge flow):
LOD0, LOD1, and LOD2 (from left to right)
LOD0 (the camera is very close to the object)
LOD1 (the camera is close to the object)
LOD2 (the camera is far from the object)
4. Nanite Optimization
Nanite offers a powerful alternative to traditional LODs, automatically managing mesh detail based on screen resolution. However, it currently has limitations with:
Two-sided materials
Translucent materials
Highly complex layered shaders (dust, dirt, debris)
Certain ray-traced shadows
When using Nanite alongside ray tracing, specific console commands can fine-tune its behavior:
r.RayTracing.Nanite.Mode 1
r.RayTracing.NormalBias (1-5)
r.RayTracing.Shadows.EnableTwoSidedGeometry 0
These adjustments help maintain optimal visual quality without overburdening the GPU.
Texture and material efficiency directly impact both memory usage and shader complexity. Proper management ensures that the project maintains high visual fidelity while remaining lightweight and responsive.
1. Baking High-Poly to Low-Poly
Details from high-resolution meshes can be transferred to low-poly models via normal map baking, preserving the appearance of fine detail at a fraction of the performance cost, applied effectively on the Puma Shoes project using Substance Painter.
2. UV Efficiency
Efficient UV layout maximizes texel density. For repetitive elements—like a flower with five nearly identical leaves—overlapping UV shells can save texture space, reducing the need for additional materials or UDIMs. It's been applied to some of the flowers that I modeled and textured for the Sheikh Zayed Grand Mosque project.
3. Texture Channel Packing
Combining multiple grayscale maps (e.g., roughness, metallic, ambient occlusion) into a single texture’s RGB channels minimizes texture calls and memory footprint.
In Unreal (and most real-time engines), each texture you sample adds to both memory usage and GPU cost. If you have separate textures for roughness, metallic, and ambient occlusion, that means three separate texture lookups — one for each map.
To optimize performance, these grayscale maps (which each use only one color channel) can be combined into a single RGB texture usually called ORM, where:
R channel stores Roughness
G channel stores Metallic
B channel stores Ambient Occlusion
This way, Unreal performs one texture fetch instead of three, reducing draw calls, texture bandwidth, and memory footprint. Modern texturing tools (like Adobe Substance 3D Painter) simplify this process by offering export presets for game engines such as Unreal Engine or Unity. These presets automatically pack multiple grayscale maps into one texture based on the engine’s conventions.
RGB channels
Red channel
Green channel
Blue channel
Unreal material
4. Simplifying Materials
Whenever possible, redundant texture maps should be removed. For example, if an object has uniform reflectivity, a constant node can replace a full texture map—reducing shader instructions and compilation time.
original material
simplified material
5. In-Engine Texture Resolution Control
Adjust texture resolution directly in Unreal for individual textures, or use the Property Matrix tool to modify multiple textures simultaneously, avoiding the time-consuming process of editing them one by one. Scale down all textures globally, then selectively restore resolution where visual quality is most impacted.
individual texture using texture details panel
multiple textures using property matrix
6. Enabling Mipmaps
Mipmaps reduce aliasing and streaming overhead for distant textures. In the Material Editor, set the texture sample node to Relative to the Computed Mip Level and control bias via a constant parameter for fine-tuning.
project settings
material setup
7. Shader Complexity Optimization
Use the Shader Complexity View (Alt + 8) to visualize expensive materials. Elements like dirt, dust, and layered effects can be baked into textures to reduce runtime cost. In many cases, baking Unreal materials directly within the engine provides efficient results.
shader complexity view
material graph
baked material
8. Avoiding Two-Sided Materials
Two-sided materials effectively double shading cost. For thin geometry, using shell geometry (a thin duplicate of the surface slightly offset along the normals to mimic thickness ) or duplicated, flipped normals offers better performance. This method has been used for some tropical plants in the SeaWorld Abu Dhabi project.
9. Material Instancing
Employ Material Instances to standardize shaders across assets. This reduces compilation time, draw calls, and memory overhead while enabling easy per-instance parameter adjustments. This applies to all Unreal projects I have worked on.
Rendering and lighting are often the most performance-intensive parts of any Unreal project. Careful tuning can significantly improve frame rates without noticeably degrading visual quality.
1. Distance Culling
Use a "Cull Distance Volume" to define how far objects remain visible. By setting object size and cull distance indices, smaller or less important objects can automatically disappear at longer distances. It can also be integrated with the LOD system which I have used for many unreal digital-twins. Here is an example:
Distance 01 (LOD0 for both)
Distance 02 (big can:LOD1 & small can: LOD2)
Distance 03 (LOD2 for both)
Distance 04 (the small can disappeared)
Distance 05
Distance 06 (the big can disappeared too)
2. Occlusion Culling
Unreal Engine automatically performs Hierarchical Z-Buffer Occlusion Culling, hiding objects that are out of view (behind walls or beyond camera frustum). It can be enabled and fine-tuned in Project Settings. We also can adjust the object’s "Bounds Scale" in the Details panel for specific cases, and visualize bounds (via Visibility → Advanced → Bounds) for troubleshooting. Also, we can use the "FreezeRendering" console command to pause scene updates to analyze what’s being culled. This way, we can check if there is anything visible behind the wall. This command helped me a lot while I was troubleshooting many of the unreal digital-twin projects.
3. Material and Instance Management
When multiple assets share the same material, performance can improve through Level Instancing or Packed Levels (Blueprint-based instances of a level). I have to mention that it is also possible to have some color variation by using the "HueShift" node to introduce color variation without duplicating materials. In the Details panel, enable Num Custom Data Floats = 1 for instance-level parameter control. I optimized the performance of projects such as the Natural History Museum project and several others by using instancing and texture packing, combined with other optimization techniques of course.
4. Merging Actors
Leverage the Merge Actors Tool or Hierarchical Level of Detail (HLOD) to combine static background geometry. This reduces draw calls and improves rendering throughput, especially in large environments. Merging actors may sound simple, but it makes a much bigger difference than you might expect; it has surprised me many times!
Tip: check UVs, lightmap UV generation, collision, pivot and material assignments. Fix any atlas/material issues in the merged asset if needed.
merging 30 actors
5. Collision Optimization
Replace complex collision meshes with simple primitives (box, capsule, sphere) or custom simplified proxies. This reduces CPU workload during physics and interaction checks. Here is an example of a barrel with complex collision consists of 7 primitives. We can optimize it by deleting the original collision and adding a simple shape which is a box in this example:
original collision
simplified collision
6. Lighting Optimization
Baking lighting remains the most performance-efficient approach, but it requires high-quality lightmaps, which often need manual adjustments to achieve optimal results. I should also mention that for interactive or dynamic scenes, selectively use dynamic lights for localized shadowing (Tip: spot Lights often offer the best performance). We can also use Lighting Complexity View (Alt + 7) to profile light cost and adjust attenuation radius or remove unnecessary lights. For static lights that don’t move but shouldn’t be baked, set them to Stationary instead of Movable. Furthermore, when using ray tracing, also monitor ray tracing samples to balance quality and cost.
complex lighting
adjusted attenuation radius
optimized lighting
7. MegaLights
A real GAME CHANGER! Unreal’s MegaLights are extremely performance-friendly and ideal for large-scale scenes. We can enable them in the project settings and use a Post Process Volume for improved scalability and consistency.
project settings
post process volume
light details panel
The following is the official MegaLights demo released by Epic Games showcased at Unreal Fest Seattle 2024 during the keynote:
I hope this overview has provided some valuable insights into my approach to diagnosing and resolving performance bottlenecks in Unreal Engine. My goal was not only to share practical methods and techniques but also to demonstrate the depth of experience I’ve developed through optimizing real-time and digital-twin projects across the globe.
Performance optimization is both an art and a science (balancing visual fidelity with real-time efficiency) and it’s a discipline I’ve refined through years of hands-on work with diverse teams and complex environments.