API Blender предоставляет возможность рисовать в окне вьюпорта (3D Viewport) при помощи модуля gpu.
Для примера нарисуем в центре сцены упрощенный виджет системы координат, состоящий из трех линий разного цвета.
Кастомное рисование во вьюпорте производится по тем же принципам, по которым в нем отображаются обычные меши. Для того, чтобы нарисовать во вьюпорте какой-либо объект, нужно задать расположение его точек и назначить им шейдер для отображения.
Зададим координаты шести точек, каждая пара из которых образует отрезок из центра координат – точки (0.0, 0.0, 0.0) в точку отстоящую на 1 по нужной оси (X, Y и Z):
1 2 3 |
vertices = [(0.0, 0.0, 0.0), (1.0, 0.0, 0.0), (0.0, 0.0, 0.0), (0.0, 1.0, 0.0), (0.0, 0.0, 0.0), (0.0, 0.0, 1.0)] |
Для каждой точки зададим цвета в формате RGBА. Для первой пары точек – красный (1.0, 0.0, 0.0), для второй – синий (0.0, 1.0, 0.0) и для третьей пары – зеленый (0.0, 0.0, 1.0).
1 2 3 |
col = [(1.0, 0.0, 0.0, 1.0), (1.0, 0.0, 0.0, 1.0), (0.0, 1.0, 0.0, 1.0), (0.0, 1.0, 0.0, 1.0), (0.0, 0.0, 1.0, 1.0), (0.0, 0.0, 1.0, 1.0)] |
Создадим шейдер для заданных цветов:
1 2 3 |
import gpu shader = gpu.shader.from_builtin('3D_SMOOTH_COLOR') |
И соберем все вместе в единый пакет для отображения во вьюпорте:
1 2 3 |
from gpu_extras.batch import batch_for_shader batch = batch_for_shader(shader, 'LINES', {"pos": vertices, "color": col}) |
Значение второго параметра “LINES” отвечает за режим отображения геометрии, в данном случае – рисование линий по заданным точкам.
Определим функцию, которая будет осуществлять собственно отрисовку заданного пакета во вьюпорте:
1 2 3 |
def draw(): shader.bind() batch.draw(shader) |
и добавим ее в обработчик события перерисовки окна вьюпорта.
1 2 3 |
import bpy draw_handler = bpy.types.SpaceView3D.draw_handler_add(draw, (), 'WINDOW', 'POST_VIEW') |
Функция draw теперь будет вызываться каждый раз при обновлении вьюпорта.
Для начальной отрисовки обновление вьюпорта можно вызывать с помощью кода:
1 2 3 |
for area in bpy.context.window.screen.areas: if area.type == 'VIEW_3D': area.tag_redraw() |
Итоговый код выглядит следующим образом:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
import bpy import gpu from gpu_extras.batch import batch_for_shader vertices = [(0.0, 0.0, 0.0), (1.0, 0.0, 0.0), (0.0, 0.0, 0.0), (0.0, 1.0, 0.0), (0.0, 0.0, 0.0), (0.0, 0.0, 1.0)] shader = gpu.shader.from_builtin('3D_SMOOTH_COLOR') col = [(1.0, 0.0, 0.0, 1.0), (1.0, 0.0, 0.0, 1.0), (0.0, 1.0, 0.0, 1.0), (0.0, 1.0, 0.0, 1.0), (0.0, 0.0, 1.0, 1.0), (0.0, 0.0, 1.0, 1.0)] batch = batch_for_shader(shader, 'LINES', {"pos": vertices, "color": col}) def draw(): shader.bind() batch.draw(shader) draw_handler = bpy.types.SpaceView3D.draw_handler_add(draw, (), 'WINDOW', 'POST_VIEW') for area in bpy.context.window.screen.areas: if area.type == 'VIEW_3D': area.tag_redraw() |
Если его выполнить, во вьюпотре будет отображаться созданный нами виджет:
На текущий момент еще не все возможности перенесены в gpu модуль AIP. Например для задания толщины линий, нужно воспользоваться модулем bgl:
1 2 3 4 5 |
import bgl bgl.glLineWidth(5) ... bgl.glLineWidth(1) |
Финальный код:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
import bpy import gpu from gpu_extras.batch import batch_for_shader import bgl vertices = [(0.0, 0.0, 0.0), (1.0, 0.0, 0.0), (0.0, 0.0, 0.0), (0.0, 1.0, 0.0), (0.0, 0.0, 0.0), (0.0, 0.0, 1.0)] shader = gpu.shader.from_builtin('3D_SMOOTH_COLOR') col = [(1.0, 0.0, 0.0, 1.0), (1.0, 0.0, 0.0, 1.0), (0.0, 1.0, 0.0, 1.0), (0.0, 1.0, 0.0, 1.0), (0.0, 0.0, 1.0, 1.0), (0.0, 0.0, 1.0, 1.0)] batch = batch_for_shader(shader, 'LINES', {"pos": vertices, "color": col}) def draw(): bgl.glLineWidth(5) shader.bind() batch.draw(shader) bgl.glLineWidth(1) draw_handler = bpy.types.SpaceView3D.draw_handler_add(draw, (), 'WINDOW', 'POST_VIEW') for area in bpy.context.window.screen.areas: if area.type == 'VIEW_3D': area.tag_redraw() |
Для удаления отрисовки виджена нужно удалить функцию draw из обработчика обновления окна.
1 |
bpy.types.SpaceView3D.draw_handler_remove(draw_handler, 'WINDOW') |