Одним из популярных методов для ускорения рендера в 3D сценах является использование LOD – Level Of Details. При использовании LOD объекты, располагающиеся близко к камере, имеют самую высокую детализацию, на средних планах – среднюю детализацию, а на задних планах используются очень грубые, недетализированные объекты т.к. они все равно почти не видны. Такая сцена рендериться гораздо быстрее, чем сцена у которой на всех объектах выставлена максимальная детализация.
Простейшим способом организации LOD в Blender является использование модификатора Subdivision Surface, который сглаживает объекты тем сильнее, чем больший уровень сглаживания указан в модификаторе.
Для примера заполним сцену кубиками, предварительно добавив на каждый куб модификатор Subdivision Surface. Уровень подразделения в модификаторе выставим равным 0.
Далее мы в зависимости от положения камеры будем увеличивать уровень подразделения модификатора тем больше, чем ближе куб располагается к камере.
Чтобы обработать все кубы, пройдем в цикле по всем объектам сцены, имеющим тип “MESH”:
1 2 |
for obj in bpy.data.objects: if obj.type == 'MESH': |
Проверим, есть ли на текущем меше модификатор Subdivision Surface, чтобы не обрабатывать объекты без модификатора, и сохраним указатель на модификатор в переменную subsurf_modifier:
1 |
subsurf_modifier = next((m for m in obj.modifiers if m.type == 'SUBSURF'), None) |
Если нужный модификатор на объекте имеется, вычислим расстояние между этим объектом и камерой:
1 2 |
if subsurf_modifier: distance = (obj.location - bpy.context.scene.camera.location).length |
Проверим следующее условие:
- Если расстояние меньше 5 – установим значение подразделения модификатора равным 2
- Если расстояние до камеры больше 5, но меньше 10 – установим подразделение равным 1
- Для всех остальных случаев (расстояние от объекта до камеры больше 10) – сбросим подразделение модификатора в 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 |
Если теперь мы выполним этот код, объекты которые расположены близко к камере будут подразделены модификатором на уровень 2, на среднем расстоянии – на уровень 1, а все остальные останутся не подразделенными.
Требуемый результат мы получили, однако не имеет смысла каждый раз запускать данный код вручную, если камера меняет свое положение.
Привяжем выполнение этого кода к процессу обновления сцены, который контролируется в Blender API через депсграф.
Для начала обернем весь код в функцию, чтобы его можно было легко вызывать:
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 |
Параметры, которые мы определили для функции, сейчас на не важны, они передаются при вызове функции из процесса обновления депсграфа. Однако их можно использовать в дальнейшем для оптимизации кода внутри функции.
Для того чтобы наша функция вызывалась при каждом обновлении сцены, добавим ее вызов в хендлер depsgraph_update_post:
1 |
bpy.app.handlers.depsgraph_update_post.append(racalc_subdivision) |
Теперь выполним наш код и попробуем подвигать камеру в сцене.
Теперь при любом перемещении камеры по сцене для объектов, располагающихся рядом с ней, автоматически устанавливается высокий уровень подразделения, на средних дистанциях – средний уровень, а для удаленных от камеры объектов подразделения нет совсем.