从零开始 blender插件开发

blender 插件开发

文章目录

  • [blender 插件开发](#blender 插件开发)
    • 环境配置
      • [1. 偏好设置中开启相关功能](#1. 偏好设置中开启相关功能)
      • [2. 命令行打开](#2. 命令行打开)
      • 运行脚本
    • API学习
      • 专有名词
      • [1. bpy.data 从当前打开的blend file中,加载数据。](#1. bpy.data 从当前打开的blend file中,加载数据。)
      • [2. bpy.context 可用于获取活动对象、场景、工具设置以及许多其他属性。](#2. bpy.context 可用于获取活动对象、场景、工具设置以及许多其他属性。)
      • [3. bpy.ops 用户通常通过按钮、菜单项或快捷键访问的工具。](#3. bpy.ops 用户通常通过按钮、菜单项或快捷键访问的工具。)
    • [将脚本融入blender, 成为blender的一部分](#将脚本融入blender, 成为blender的一部分)
      • [1. 通过添加ui](#1. 通过添加ui)
      • [2. 通过添加功能(operator)](#2. 通过添加功能(operator))
    • 从脚本到add-on
      • [1. 组成部分](#1. 组成部分)
      • 2.示例
    • 资料

环境配置

1. 偏好设置中开启相关功能

打开blender, 快捷键ctrl+,, 打开偏好设置面板。进行下列相关设置(下图为设置好的状态)

设置好后, 鼠标浮动在会出现工具提示,右键可以展示相关的菜单。

2. 命令行打开

第一种方法:

  • 将blender.exe所在路径添加到环境变量中
  • 打开命令行,输入blender, 即可打开blender,命令行中会出现软件运行相关的信息, 包括python的print输出. 注意要先关掉软件,再关掉命令行。
    ps: 目前来看,命令行主要是用来展示信息,而不是输入命令。

第二种方法:

  • 打开blender之后,点击如下图所示。

运行脚本

点击如下按钮。

之后,界面变为下图。

API学习

专有名词

  • context

1. bpy.data 从当前打开的blend file中,加载数据。

2. bpy.context 可用于获取活动对象、场景、工具设置以及许多其他属性。

请注意,上下文是只读的,这意味着不能直接修改这些值。但是,可以通过运行 API 函数或使用数据 API 来更改它们。

3. bpy.ops 用户通常通过按钮、菜单项或快捷键访问的工具。

从用户的角度来看,它们是一个工具,但 Python 可以通过 bpy.ops 模块使用自己的设置运行这些。

许多工具(运算符)都有一个 "poll" 功能,用于检查光标是否在有效区域中,或者对象是否处于正确的模式(Edit Mode、Weight Paint Mode 等)。当运算符的 poll 函数在 Python 中失败时,会引发异常。

python3 复制代码
if bpy.ops.view3d.render_border.poll():
    bpy.ops.view3d.render_border()

将脚本融入blender, 成为blender的一部分

1. 通过添加ui

  • By defining menus, headers and panels.

    通过定义菜单、标题和面板。

  • By inserting new buttons into existing menus, headers and panels.

    通过将新按钮插入到现有菜单、标题和面板中。

python 复制代码
import bpy  # 导入Blender的Python API模块


# 定义一个自定义面板类 HelloWorldPanel,继承自 bpy.types.Panel
class HelloWorldPanel(bpy.types.Panel):
    """创建一个面板,在 '对象属性' 窗口中显示"""
    
    # 面板的标签,会显示在Blender UI中
    bl_label = "Hello World Panel"
    
    # 面板的唯一ID,用于Blender内部识别
    bl_idname = "OBJECT_PT_hello"
    
    # 面板显示的空间类型,这里是 'PROPERTIES',即对象属性窗口
    bl_space_type = 'PROPERTIES'
    
    # 面板显示的区域类型,这里是 'WINDOW',表示在窗口区域显示
    bl_region_type = 'WINDOW'
    
    # 面板显示的上下文,这里是 'object',即与对象相关的上下文
    bl_context = "object"

    # 面板的绘制方法,这个方法会在面板中绘制UI元素
    def draw(self, context):
        layout = self.layout  # 获取面板的布局对象,用于在面板中添加控件

        obj = context.object  # 获取当前选中的活动对象

        # 创建一行(row),并在其中添加一个标签,显示 "Hello world!" 并使用 'WORLD_DATA' 图标
        row = layout.row()
        row.label(text="Hello world!", icon='WORLD_DATA')

        # 创建新的一行,显示当前活动对象的名称
        row = layout.row()
        row.label(text="Active object is: " + obj.name)

        # 创建新的一行,添加一个控件,让用户可以编辑活动对象的名称
        row = layout.row()
        row.prop(obj, "name")  # 添加一个属性控件,允许修改对象的 'name' 属性

        # 创建新的一行,添加一个操作按钮,点击时会添加一个立方体到场景中
        row = layout.row()
        row.operator("mesh.primitive_cube_add")  # 这个操作会添加一个立方体到当前场景


# 注册类和面板的函数
def register():
    bpy.utils.register_class(HelloWorldPanel)  # 注册 HelloWorldPanel 面板类


# 注销类和面板的函数
def unregister():
    bpy.utils.unregister_class(HelloWorldPanel)  # 注销 HelloWorldPanel 面板类


# 这部分代码确保如果脚本直接执行时,面板会被注册
if __name__ == "__main__":
    register()  # 注册面板
 

2. 通过添加功能(operator)

python 复制代码
import bpy


def main(context):
    # 遍历当前场景中的所有对象
    for ob in context.scene.objects:
        print(ob)  # 打印每个对象的信息


class SimpleOperator(bpy.types.Operator):
    """Tooltip"""
    bl_idname = "object.simple_operator"  # 操作的唯一标识符
    bl_label = "Simple Object Operator"  # 操作的名称,显示在UI中

    @classmethod
    def poll(cls, context):
        # 确保只有在有活跃对象时才允许执行该操作
        return context.active_object is not None

    def execute(self, context):
        # 当操作被触发时,调用 main 函数打印所有对象信息
        main(context)
        return {'FINISHED'}  # 操作完成


def menu_func(self, context):
    # 将操作添加到 Blender 对象菜单中
    self.layout.operator(SimpleOperator.bl_idname, text=SimpleOperator.bl_label)


# 注册操作和菜单项
def register():
    bpy.utils.register_class(SimpleOperator)  # 注册 SimpleOperator 操作类
    bpy.types.VIEW3D_MT_object.append(menu_func)  # 将操作添加到对象菜单


# 注销操作和菜单项
def unregister():
    bpy.utils.unregister_class(SimpleOperator)  # 注销 SimpleOperator 操作类
    bpy.types.VIEW3D_MT_object.remove(menu_func)  # 从对象菜单中移除操作


if __name__ == "__main__":
    # 如果脚本直接执行,注册操作和菜单项
    register()

    # 测试调用 SimpleOperator 操作,打印场景中所有对象
    bpy.ops.object.simple_operator()

从脚本到add-on

附加组件,也就是我们说的插件 add-on。本质上就是对脚本的一种包装。

1. 组成部分

从一个最简单的示例看起

python 复制代码
bl_info = {
    "name": "My Test Add-on",
    "blender": (2, 80, 0),
    "category": "Object",
}
def register():
    print("Hello World")
def unregister():
    print("Goodbye World")

bl_info 添加插件的时候,显示的相关信息。包括插件名字,版本号等等。
registerenable插件的时候运行。
unregister disable插件的时候使用。

2.示例

接下来的示例将展示,一个python脚本如何包装为插件

python脚本如下

python 复制代码
import bpy

scene = bpy.context.scene
for obj in scene.objects:
    obj.location.x += 1.0
首先将脚本包装为一个函数, 将函数作为一个类的excute方法, 并添加相关属性:用作菜单项和按钮的提示信息。
python 复制代码
class ObjectMoveX(bpy.types.Operator):
    """My Object Moving Script"""      # 用作菜单项和按钮的提示信息。
    bl_idname = "object.move_x"        # 唯一标识符,用于按钮和菜单项引用。
    bl_label = "Move X by One"         # 显示在界面上的名称。
    bl_options = {'REGISTER', 'UNDO'}  # 启用撤销和注册功能。

    def execute(self, context):        # 执行操作时调用此方法。

        # 原始脚本
        scene = context.scene            # 获取当前场景
        for obj in scene.objects:        # 遍历场景中的所有对象
            obj.location.x += 1.0        # 将对象的X轴坐标加1.0

        return {'FINISHED'}              # 告诉Blender操作已成功完成。
    

接下来将这个功能添加到菜单中。先写一个添加到菜单的函数。

python3 复制代码
def menu_func(self, context):
    self.layout.operator(ObjectMoveX.bl_idname)  # 将操作按钮添加到菜单中。

接下来,根据插件的三大组成:信息+注册+注销

信息

python 复制代码
bl_info = {
    "name": "Move X Axis",               # 插件名称
    "blender": (2, 80, 0),                # 兼容的Blender版本
    "category": "Object",                 # 插件所属分类
}

注册函数,注册分为两部分,一是功能注册,而是ui注册

python 复制代码
def register():
    bpy.utils.register_class(ObjectMoveX)         # 注册操作类
    bpy.types.VIEW3D_MT_object.append(menu_func)  # 将新的操作添加到现有菜单中。

注销,注销只需要注销功能。

python 复制代码
	def unregister():
    bpy.utils.unregister_class(ObjectMoveX)       # 注销操作类

很好,再看一示例。

先看python脚本

python 复制代码
import bpy
from bpy import context

# 获取当前场景
scene = context.scene

# 获取 3D 游标的位置
cursor = scene.cursor.location

# 获取当前激活的对象(假设我们有一个激活的对象)
obj = context.active_object

# 现在复制这个对象
obj_new = obj.copy()

# 新对象必须被添加到场景中的一个集合里
scene.collection.objects.link(obj_new)

# 现在可以将新对象放置在 3D 游标的位置
obj_new.location = cursor

这段代码的功能是,将选中激活的物体,复制到cusor位置,代码执行前

代码执行后

接下来省级一下这个python脚本,实现从选中物体到光标之间复制多个物体.

python 复制代码
import bpy
from bpy import context

scene = context.scene
cursor = scene.cursor.location
obj = context.active_object

# 目前使用固定值,将来可以让用户调整这个值
total = 10

# 在场景中添加 'total' 个对象
for i in range(total):
    obj_new = obj.copy()  # 复制对象
    scene.collection.objects.link(obj_new)  # 将新对象添加到场景的集合中

    # 根据 'i' 将新对象放置在游标和活动对象之间
    factor = i / total  # 计算当前对象的位置比例
    # 将新对象的位置设置为介于活动对象和游标之间
    obj_new.location = (obj.location * factor) + (cursor * (1.0 - factor))

代码执行前同上,代码执行后

接下来,我们将其变为一个插件.

第一步,依然是包装这个脚本为一个函数,包装这个函数为一个类中的excute方法. 并为这个类添加相关属性

python 复制代码
class ObjectCursorArray(bpy.types.Operator):
    """Object Cursor Array"""  # 操作符的描述
    bl_idname = "object.cursor_array"  # 操作符的 ID
    bl_label = "Cursor Array"  # 操作符的显示名称
    bl_options = {'REGISTER', 'UNDO'}  # 操作符的选项,包括注册和撤销支持

    def execute(self, context):
        # 获取当前场景
        scene = context.scene
        # 获取 3D 游标的位置
        cursor = scene.cursor.location
        # 获取当前激活的对象
        obj = context.active_object

        # 设置要创建的对象数量
        total = 10

        # 创建并定位对象
        for i in range(total):
            # 复制当前激活的对象
            obj_new = obj.copy()
            # 将新对象添加到场景的集合中
            scene.collection.objects.link(obj_new)

            # 计算新对象的位置,使其位于活动对象和游标之间
            factor = i / total
            # 将新对象的位置设置为活动对象和游标之间的插值位置
            obj_new.location = (obj.location * factor) + (cursor * (1.0 - factor))

        # 返回操作完成
        return {'FINISHED'}

第二步,将这个功能添加到菜单或者ui中.

python 复制代码
def menu_func(self, context):
    self.layout.operator(ObjectCursorArray.bl_idname)

第三步,添加插件三部 信息+注册+注销

python 复制代码
bl_info = {
    "name": "Cursor Array",  # 插件的名称
    "blender": (2, 80, 0),  # 支持的 Blender 版本
    "category": "Object",  # 插件类别
}
python 复制代码
def register():
    bpy.utils.register_class(ObjectCursorArray)
    bpy.types.VIEW3D_MT_object.append(menu_func)
python 复制代码
def unregister():
    bpy.utils.unregister_class(ObjectCursorArray)

合并之后,整体代码

python 复制代码
bl_info = {
    "name": "Cursor Array",  # 插件的名称
    "blender": (2, 80, 0),  # 支持的 Blender 版本
    "category": "Object",  # 插件类别
}

import bpy


class ObjectCursorArray(bpy.types.Operator):
    """Object Cursor Array"""  # 操作符的描述
    bl_idname = "object.cursor_array"  # 操作符的 ID
    bl_label = "Cursor Array"  # 操作符的显示名称
    bl_options = {'REGISTER', 'UNDO'}  # 操作符的选项,包括注册和撤销支持

    def execute(self, context):
        # 获取当前场景
        scene = context.scene
        # 获取 3D 游标的位置
        cursor = scene.cursor.location
        # 获取当前激活的对象
        obj = context.active_object

        # 设置要创建的对象数量
        total = 10

        # 创建并定位对象
        for i in range(total):
            # 复制当前激活的对象
            obj_new = obj.copy()
            # 将新对象添加到场景的集合中
            scene.collection.objects.link(obj_new)

            # 计算新对象的位置,使其位于活动对象和游标之间
            factor = i / total
            # 将新对象的位置设置为活动对象和游标之间的插值位置
            obj_new.location = (obj.location * factor) + (cursor * (1.0 - factor))

        # 返回操作完成
        return {'FINISHED'}

def menu_func(self, context):
    self.layout.operator(ObjectCursorArray.bl_idname)
    
# 注册操作符
def register():
    bpy.utils.register_class(ObjectCursorArray)
    bpy.types.VIEW3D_MT_object.append(menu_func)


# 注销操作符
def unregister():
    bpy.utils.unregister_class(ObjectCursorArray)


# 如果是直接执行脚本,注册操作符
if __name__ == "__main__":
    register()

此外,官方还给出了一些示例,可以从这里打开

资料

官方给出了多个链接用于学习

相关推荐
Dream_Snowar19 分钟前
速通Python 第四节——函数
开发语言·python·算法
西猫雷婶21 分钟前
python学opencv|读取图像(十四)BGR图像和HSV图像通道拆分
开发语言·python·opencv
汪洪墩1 小时前
【Mars3d】设置backgroundImage、map.scene.skyBox、backgroundImage来回切换
开发语言·javascript·python·ecmascript·webgl·cesium
程序员shen1616112 小时前
抖音短视频saas矩阵源码系统开发所需掌握的技术
java·前端·数据库·python·算法
人人人人一样一样2 小时前
作业Python
python
四口鲸鱼爱吃盐3 小时前
Pytorch | 利用VMI-FGSM针对CIFAR10上的ResNet分类器进行对抗攻击
人工智能·pytorch·python
四口鲸鱼爱吃盐3 小时前
Pytorch | 利用PI-FGSM针对CIFAR10上的ResNet分类器进行对抗攻击
人工智能·pytorch·python
小陈phd3 小时前
深度学习之超分辨率算法——SRCNN
python·深度学习·tensorflow·卷积
CodeClimb3 小时前
【华为OD-E卷-简单的自动曝光 100分(python、java、c++、js、c)】
java·python·华为od