Roblox Optimizations for Models By BIackoutDev
Roblox sadly Is not exactly the most optimized engine, so on the artist’s end of things we need to do everything we can to try and keep models and environments as optimized as possible, this document will continue to evolve as I think of more things to add to it, but this should be a good start.
Triangle limitations
Every scene/model is made up of triangles, and trying to limit the number of them that you use will greatly improve performance and QOL for your game and players. Every device can only render so many triangles in a scene before they start to get bogged down and start dropping in framerate. This is a good slide from MrChickenRocket’s talk at RDC 24 showing a visual example of the number of allotted triangles scene for different devices.
Targeting the numbers that are allotted for lower end devices will allow your game to be ran by more players on a larger variety of devices. You must keep in mind that not everyone owns a 4090 or a PS5 or the top model phone. So, optimizing your environments accordingly is extremely important.
Roblox’s max import size for an individual mesh via the 3d Importer is 21k Tri per individual mesh and 10k via the Asset manager or any other form of importer.
.OBJ vs .FBX
****.OBJ import’s everything as a single mesh (it will combine all parts selected when exported/imported)
.OBJ models are also usually more accurate to scale within studio without having to change any specific settings in blender/maya.
.FBX allows you to import up to 200 individual pieces/objects in a 3d project at per FBX. This means that if you have 1,000 objects in your project you will need to break it down into groups of 200 and then import 5 separate .FBX files.
You can identify how many objects are in your scene in blender by checking the “statistics” box under the viewport overlays drop down.
To reduce this problem, it’s always a good idea to try joining objects that use the same texture map to reduce the number of textures required as well as meshes to deal with.
In most cases you should try and keep every mesh as simple/low LOD as you physically can while still retaining the silhouette of the model. Try and keep as many of the really intricate details to the texture process as possible.
ALTHOUGH because the tri limit per mesh is 10k you can technically import up to 200 meshes with 20k or less tri in a single .FBX file as opposed to a single mesh that is overall 20k tri with a .obj
Also try to remove any hidden faces/tri as possible. These could be faces/vert’s that are hidden under other geometry.
Within blender there is a shortcut to do this for you (Select → Select all by Trait → Interior Faces → delete)
But you can also prevent this by just having clean geometry, a good example is when you combine two cylinders together. Instead of having just two intersecting cylinders you can connect them directly and have a lot cleaner geometry, plus it is subdividable and better for texturing.
This Tree is a perfect example of how you can cut down the number of loop cuts/faces on a model without having any major differences in appearance but even a 1% improvement in performance per model really matters at scale. (obviously, these are two different tree models, so it is not a perfect example, but it is a good example of a well optimized tree vs the opposite)
It is also important to note the performance differences between parts and meshes, parts are models that are specifically optimized and designed to work perfectly in studio, and when ever you get the chance to replace a mesh with a part you should do so. The different is very minimal but at scale it will make a difference.
These two huts are physically identical using the same number of pieces that are the same size and textured the exact same way in studio. The only difference being one is made using parts and one is made using meshes. And when exported from studio as .obj files the one made using parts is ever so slightly smaller.
Hero Assets:
“What is a hero asset?”
a hero asset is a model that is shown very frequently and often up very close to the camera, examples could be weapons or key story items in a game, or even characters.
If the asset is a hero asset that requires going above that 10k limit, try to separate the mesh into multiple parts to allow you to add more geometry to the model.
Splitting up the model also allows you to use multiple texture sets, for example you can break up your model into multiple sections and have a set of 1024x1024 textures dedicated to just the stock of a rifle and another texture set dedicated to just the receiver and barrel to allow for the highest amount of textured detail.
Texture limitations
General Info
Max texture size is 1024x1024 HOWEVER we should try to keep as many things as possible below this at either 512x512 or 256x256 pixels.
An important thing to mention on this subject is that a 1024x1024 texture is not as performant as a 512x512 or even 256x256 texture. BUT! 1x 1024x1024 texture is significantly more performant than 4x 512x512 textures. Even though they take up the same overall texture resolution when combined.
This is because Roblox’s compression aims to compress files to a certain file size rather than being directly scalable; this means in some cases despite a 512x512 texture containing 4x less data than a 1024x1024 texture they may both be compressed to similar file sizes. I.e the 512 can end up around 80% of the file size of the 1024 rather than only being 25% of the size.
Not to mention the additional draw calls with one 1024x1024 texture set compared to 4x 512x512 texture sets.
Another quick tip is If your Metalic or roughness maps are a solid color (usually a black or white value) just make it a single pixel for the sake of optimization. Larger texture sizes should be reserved for large objects or hero assets that are going to be seen up close frequently.
Roblox has three ways of texturing things.
Texture ID slot in mesh object:
This is just a single-color map texture slot that overlays the image over the model like a normal albedo map.
You CAN use .PNG images with the transparency slider on this and just make the mesh .1 transparent so that it registers the alpha or transparency, but it is not ideal. (we will get into the types of OIT rendering with different texture types shortly)
- Surface appearance: this is an object that can be added to meshes to apply this list of material types.
- Albedo
- metalic
- Normal
- Roughness
(substance painter has a export preset for this)
Emissive properties
Emissive is mainly possible through the in-engine Neon Material so any parts of a mesh that you want to have emissive properties need to be separated so they can be textured IN ENGINE.
There are some sneaky tricks to get emissive maps to work in studio without the neon property, because color3 and vertex colors are unclamped you can set their RGB value way above its default max (255), when you set it above 255 there will be some visual bugs that result in the textures glowing. If you layer a specialmesh over top of your base mesh and then either give the specialmesh a decal or set its textureID to your emissive map and then crank the RGB value over 255 it will glow.
Out of these two options the first is easier and more performant but sometimes if you’re going for a very specific look the 2nd is an option.
Transparent objects
Making objects transparent or semitransparent in Roblox can be an interesting subject Roblox uses depth sorting per instance for transparency and works specifically from back to front of the scene.
The more transparent objects you have overlapping also will have a noticeable impact on performance. But for environments this is often a necessary evil for things like foliage.
When you do not use alpha maps for transparency the edges of your texture are going to be less accurate, this mixed with mipmapping often results in a white haze around the edge of your texture at distance and weird layering/blurring up close.
With these issues you may want to use alpha maps for images with transparency like foliage. I outlined how to use Alpha channels with .TGA images in photoshop in this dev forum post.
(Fern model using an alpha map embedded in a .tga image file)
Another note about transparent objects is that they are not rendered if fully transparent, however their physics are still rendered, which is why you can have invisible walls.
However keep in mind that batching does not apply to transparent objects, so every time you make a model slightly transparent the engine creates a new drawcall for each transparent object + its material.
Link:
Optimization tips for surface appearance:
We really want to avoid having excessive amounts of surface appearance objects, if possible, because of the impacts on load times and performance.
Any parts of your mesh that use the same set of materials (same surface appearance object) should be grouped into one mesh unless it negatively affects the collisions or function of the model.
If there is a piece of the mesh that needs to move/animate it should also be separated.
Ideally, we want to lean mostly on material service for most of our texturing that is not a hero asset/character.
Material Service:
Material service uses Roblox’s built in ability to texture objects using tileable materials, it also holds the same slots as the Surface Appearance object however it is much more optimized than having tons of material service objects inside of your models because it only needs to load the texture once. Overall resulting in less draw call’s in your environment.
In engine Optimizations
Render Fidelity:
Under the properties window there is a section called Render fidelity with three options
This affects the LOD of the object as you get further from the object.
- Performance
- Automatic
- Precise
In 95% of cases everything should be set to performance because it will cull objects out when they are a certain distance away. Only select objects like mountain ranges should use the precise option because it will stay at the highest LOD no matter the distance.
However there is a workaround for distant objects where you can keep it at performance, if you import your model as a specialmesh and place it at a location close to where the players will be, and then go to the “mesh” object inside of the specialmesh and set its offset to the location that you want the mesh to be at.
This allows you to have extremely large objects in the background of your environment that are able to interact with changes in the lighting rather than having to bake them into the skybox or have them a lot closer than they need to be.
Specialmeshes also allow you to have extremely large objects that are much bigger than the typical 2048 studs cubed. With the only downside being that there is no surfaceapperance support for specialmeshes.
Collision Fidelity:
Also found in the properties window under the collision tab you can find the options for “Collision fidelity”. There is four options for this.
- Box
- Hull
- Default
- Precise Convex Decomposition
You can visualize the effects of these options using the “mesh Optimization tools” plugin and click the “show mesh decomposition” button.
OR via the viewport options button at the top right of the viewport window.
Plugin Link: https://create.roblox.com/store/asset/414923656/Mesh-Optimization-Tools
These dictate the collisions and how the physics engine handles the object. The goal is to make them as simple as possible without making a noticeable difference in how the object affects the player’s experience. because collision fidelity affects a model’s data on both the users end and the physics end of the engine setting these to be as simple as possible reduces the amount of memory taken up on a user’s computer and will result in faster load times.
Using this medieval cart with body bags as an example
Here is a visualization of the different collision fidelity types.
For this model, the only collisions that would be noticeable to the player if they were not BOX or HULL would be the body bags, wheels, and handle of the cart which would make my process this.
- Make the wheels hull so it gets the actual shape of the wheels
- Make the body bags hull so you get the rough collisions with them without having any noticeable floating on them if stood on.
- Make the body BOX because it is just a box
- Make the handle default so that the player will not float before touching the handle.
It really is model specific but for most objects you can just make box, and it will not really change much about how the player interacts with it.
Also, if there is parts of your model that the player would not notice interacting with at all just disable the collisions overall, like buttons on a wall or bolts.
A final option is that you can set everything to box with can collide off and rebuild the necessary collisions with invisible parts.
Shadows
During the process of editing the collisions it can also be a good idea to look at how enabling and disabling your mesh’s ability to cast shadows affects the overall visuals of the shadows on the whole model.
If you can disable shadows for any part of a model without changing the overall look you should disable them.
A good way to start is to completely disable shadows on one instance of the model and test it side by side with the same model, and toggle cast shadow on and off to see what really affects the main shadow being cast.
Example
For this stove model there is a lot that likely wont affect the visuals of the model, like if I disable the shadows on the feet of the stove, door of the stove, and the nobs of the stove and the metal for the burners.
Before:
After:
The kind of performance gained from disabling shadows on a large number of objects is quite significant especially an scale for a whole environment.
Another extremely important note is what type of light you are using to illuminate your scene, as well as what lighting engine you are using for your environment.
There are three types of lights in studio, as visualized below.
- Spotlight [Casts light in a single direction from a point in a cone]
- Surface Light [casts in a single direction across the entire surface of the object]
- Point Light [Omnidirectional light cast from a single point]
These lights are visually different and cast light in different ways, and when it comes to performance the surface and spotlight are significantly more performance friendly than the spotlight is. You can have two surface lights (one pointing up and one pointing down) with a 180-degree casting angle and will still be more performance friendly than a single point light.
Because point lights cast light in every single direction it also casts shadows in every single direction which results in a heck of a lot more shadow draw calls and thus lowers performance.
Lights that move also cause significantly more draw calls on your scene because they must be re rendered every time they move.
Draw calls
This is a quick TLDR of drawcall’s and how they work and how you can reduce them. MrChickenRocket did an amazing talk on drawcalls and batching in his talk during RDC 24 and that can be found here.
Using Roblox Tools to Improve Performance | RDC 2024
A draw call is an order sent to the GPU to render a specific object, and the more draw calls you have the harder your computer must work to render the environment. And for each model the GPU must call the material as well as the object.
A good budget of draw calls to shoot for on roblox is around 500 to account for lower end devices.
So reducing the number of draws calls along with the tri count can dramatically improve the performance of your environment.
One straightforward way to reduce your draw call count is to use as few unique meshi d’s as possible in your environments. If you can re-use a model or a part of a model as well as its materials, they will batch together and only take up one draw call for all the duplicated models/materials.
Even things like UI create their own draw calls as well, so be sure to keep that in mind while working on other aspects of the game beyond the environment.
Final Example
I am going to go over Alex’s beautiful elevator as an example of what I would change about this submitted model.
The first thing that I noticed was how many things inside of this model are separated and using their own surface appearance object.
Currently there is 40 surface appearance objects each with 4 materials inside of them which makes about 160 textures JUST for this elevator that need to be loaded and 42 separate meshes that need to be loaded in.
Even though the main texture set only takes up one draw call per texture map reducing this number will still slightly increase performance and make this model alot easier to work with.
The goal of this would be to reduce those two numbers as far as possible.
- There seems to be around 4 texture sets used throughout the whole model which means we can potentially reduce this as low as 16 textures used for the entire elevator.
- We can reduce the number of objects down from 42 to around 8 max.
This is how I grouped up the model’s pieces by texture set.
The armrest mesh has its own texture set that isn’t used anywhere else, however it could be completely removed and just be textured in engine using Roblox’s materials service, so it does not take up an extra texture set. I also would not have attached it to the panel hosting all the floor buttons on the exterior.
- At this point what I would do in engine is remove collisions from this object and convert its collision fidelity to box.
Ideally If we wanted to retain the collisions of the handrails, I would leave them un textured in substance (or whatever you use) and separate them into 3 pieces to be textured in studio. And then set them to box.
I would also combine the information from the panel on the exterior to the texture set used for the interior button panel
It looks like this area here has space for it.
Next thing I would do is combine all the lights and buttons/panels that are covered under their texture set (shown above on the left) into one mesh and remove the collisions on them and set the collision fidelity to box.
I would also suggests potentially even leaving the buttons completely un textured so they can be textured in engine.
The benefit of that would be that we could use code to make the buttons glow as you pass each floor from the lobby.
- if that were the case then I would have them stay untextured and keep them as separate pieces rather than combine them all into one so that they could change individually.
- if we did leave them un textured that would open even more texture real estate to combine the Armrest textures and door panel textures.
Because the doors are moving parts it makes sense to have them separate with their own surface appearance objects.
Collision Fidelity
- Buttons/Panels would be Box with collisions disabled
- Handrails/ exterior panel would be set to box with collisions disabled
- Walls/floor/ceiling would be set to PreciseConvexDecomposition because the player needs to accurately interact with them
- The doors would be set to box because they are just boxes
- Lights/buttons would also be set to box with collisions disabled.
CONCLUSION:
I hope that at least something in this document is helpful to you and your future creations! This was a collaborative effort where I wrote down everything that I thought was relevant to creating environments on Roblox with input and revisions by both Peter McNeill (MrChickenRocket) As well as various others at the Roblox Rendering team.
If there is something you feel I got incorrect or something additional to add please feel free to comment! I will continue to update this as I think of additional information I find relevant.























