One of the popular methods for speeding up rendering 3D scenes is to use LOD – Level Of Details. When using LOD, objects close to the camera have the highest detail, midgrounds have medium detail, and backgrounds objects are very rough and undetailed because they are still almost invisible. Such a scene is rendered much faster than a scene in which all objects have maximum detail.
The simplest way to organize LOD in Blender is to use the Subdivision Surface modifier, which smooths objects the more strongly, the higher the level of subdivision specified in the modifier.
For example, let’s fill the scene with cubes, having the Subdivision Surface modifier appended to each one. Set the subdivision level in the modifier to 0.
Next, depending on the position of the camera, we will increase the level of the modifier subdivision, the closer the cube is located to the camera.
To process all the cubes, loop through all scene objects of the “MESH” type:
1 2 |
for obj in bpy.data.objects: if obj.type == 'MESH': |
Check if the current mesh has the Subdivision Surface modifier, not to process objects without a modifier, and save the pointer to the modifier in the “subsurf_modifier” variable:
1 |
subsurf_modifier = next((m for m in obj.modifiers if m.type == 'SUBSURF'), None) |
If the desired modifier is existing on the object, calculate the distance between this object and the camera:
1 2 |
if subsurf_modifier: distance = (obj.location - bpy.context.scene.camera.location).length |
Next, check the following conditions:
- If the distance between the object and the camera is less than 5, set the modifier subdivision level to 2
- If the distance is greater than 5, but less than 10, set the subdivision level equal to 1
- For all other cases (the distance from the object to the camera is more than 10) – reset the modifier subdivision level to 0
1 2 3 4 5 6 7 8 9 |
if distance <= 5: subsurf_modifier.levels = 2 subsurf_modifier.render_levels = 2 elif 5 < distance <= 10: subsurf_modifier.levels = 1 subsurf_modifier.render_levels = 1 else: subsurf_modifier.levels = 0 subsurf_modifier.render_levels = 0 |
If we now execute this code, objects that are located close to the camera will be subdivided by the modifier to level 2, at an average distance – to level 1, and all others will remain unsubdivided.
We got the required result, but it makes no sense to run this code manually every time if the camera changes its position.
Link the execution of this code to the scene update process, which is controlled in the Blender API with depsgraph.
Wrap all the code in a function so that it can be easily called:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
def racalc_subdivision(scene, depsgraph): for obj in bpy.data.objects: if obj.type == 'MESH': subsurf_modifier = next((m for m in obj.modifiers if m.type == 'SUBSURF'), None) if subsurf_modifier: distance = (obj.location - bpy.context.scene.camera.location).length if distance <= 5: subsurf_modifier.levels = 2 subsurf_modifier.render_levels = 2 elif 5 < distance <= 10: subsurf_modifier.levels = 1 subsurf_modifier.render_levels = 1 else: subsurf_modifier.levels = 0 subsurf_modifier.render_levels = 0 |
The parameters that we defined for the function are not important now; they are passed when the function is called from the depsgraph update process. However, you can use them to optimize the code inside the function by yourself.
In order for our function to be called every time the scene is updated, we add its call to the “depsgraph_update_post” handler:
1 |
bpy.app.handlers.depsgraph_update_post.append(racalc_subdivision) |
Now let’s execute our code and try to move the camera in the scene.
Now, whenever the camera moves around the scene, objects located next to it are automatically set to a high subdivision level, at medium distances – a medium level, and for objects far from the camera there is no subdivision at all.