基于Blender的虚拟摄影机插件开发
2022-09-28胡冰
胡 冰
北京影润文化传播有限公司,北京 102600
1 引言
摄影机作为影像创作的基本工具,决定了影视内容呈现的视角与方式。随着时代的进步,无论是艺术创作者还是观众,都对画面的呈现方式有着更高的要求与期待。以往那些难以通过实景拍摄获得的镜头效果,在数字技术的加持下,正在被逐一攻克。
图1 The Third Floor在 《复仇者联盟》中应用Tech Vis虚拟摄影机
当观众身处影院跟随疾驰的车辆穿过枪林弹雨,或是与各种科幻角色一起遨游太空,这些紧张刺激的画面视角,都离不开虚拟摄影机的协助。所谓虚拟摄影机,是指在三维或视效软件中虚构的摄影机对象,它们可以在三维空间内任意移动而不受物理上的限制,从而实现对数字场景的镜头化表达。不仅是在影视制作的后期阶段,随着Tech Vis等视效预览技术的广泛应用,虚拟摄影机还能为实景拍摄提供参考依据,或作为Motion Control机械臂的数据来源。因此,能够便捷高效地应用虚拟摄影机工具,对现今的影视创作与拍摄都有着重要的意义。
2 痛点与解决方案
尽管当今的三维与合成类软件都内置有摄像机(Camera)视角以及时间线动画功能,然而在使用它们创建摄影机动画时都需要较为繁琐的操作步骤。更为重要的是,普通的三维动画师往往并不具备十分专业的镜头语言表达能力,使得他们难以创建流畅、聚焦且引人入胜的镜头运动效果。因此,对现有制作软件的功能进行扩展,使其能够实现更加快捷、灵活、专业的虚拟镜头创建,就变得非常有价值。
针对上述需求,市场上已经存在部分面向主流DCC(Digital Content Creation)软件的虚拟摄影机插件产品,但在灵活性方面往往无法满足工作室用户的特定需求,且在采购成本上对于拥有大量工作站的工作室并不友好。因此,在实际项目中,大多数视效与动画工作室都会采取自研的方式进行开发,并将相关任务委派给Pipeline TD,即管线技术指导进行完成。Pipeline TD在接到开发任务后,会遵循以下三个步骤展开工作:插件需求设计、宿主软件扩展性调研,以及最终为实现插件功能编写代码。
2.1 插件需求设计
对于这款虚拟摄影机插件,其应具备两类基本功能。首先,通过包含一些经典、常用的摄影机运动方式预设,帮助用户便捷高效地创建拍摄镜头;另一方面,允许用户通过将三维空间内可自定义的平滑曲线作为摄影机的运动轨道,实现类似 “飞猫”的高空绳索摄影机视角,从而创建灵活度更高、视觉效果更加震撼的镜头语言。下文中,将以 “Movie-Cam”命名并指代此虚拟摄影机插件。
2.2 Blender开发扩展性调研
2.2.1 宿主软件选择
相对于传统影视制作中剪辑、调色等相对孤立的制作流程,视效与动画行业的内容生产则包含诸多环环相扣的紧密流程,即所谓管线 (pipeline)的概念。因此,与之对应的,专业DCC软件在其架构设计上就是面向制作管线的,通常都会提供非常全面、丰富的API开发接口,以满足行业用户对于自定义工作流程与生产效率方面的严苛要求。
对于像Maya、3ds Max、Houdini这类商业软件,无论是其API接口还是代码调试,相关支持都已十分成熟。近年来,随着开源生态的发展壮大,越来越多的内容创作者将目光投向Blender。作为一款免费开源的DCC软件,Blender同时具备建模、动画、视效合成等功能。根据Blender基金会官方发布的数据,在2020年期间,其软件官网下载量超过1400万次。选择将Blender作为虚拟摄影机插件的宿主平台,可显著降低工作室的软件采购成本,但在此之前,需要对其开发扩展支持做深入研究。
2.2.2 Blender Python API模块构成
在Blender软件的官方文档中有这样的描述:“能够使用Blender标准UI实现的功能,都能够通过对Blender API的调用来实现。”为此,Blender内置了丰富的开发接口。其中,“bpy”作为Blender Python API的主模块,包含了对Blender主要功能的调用接口。根据用途的不同,它们被划分为9类功能模块。
(1)上下文模块 (bpy.context):提供了对上下文状态及属性的访问。它将根据Blender软件当前所处的操作模式提供不同的成员选项。需要注意的是以上下文方式访问的值仅可只读。
(2)数据访问模块 (bpy.data):提供了对Blender项目工程中全部数据的可读写访问接口。
(3)消息总线模块 (bpy.msgbus):当使用数据API修改Blender项目中数据块 (datablocks)的属性时,消息总线系统可用于接收通知提醒。
(4)操作符模块 (bpy.ops):提供基于Python访问的可调用操作符 (Operators),它们由C、Python或宏语言编写,并以bpy.ops作为访问路径。
(5)类型模块 (bpy.types):以类 (Class)的形式提供了对Blender内置数据结构的抽象。
(6)实用工具模块 (bpy.utils):提供了诸如功能类注册、资源路径查找等非Blender项目数据相关的实用工具函数。
(7)路径工具模块 (bpy.path):类似于Python标准库中的os.path模块,包含了用于处理与Blender路径解析相关的实用工具函数。
(8)应用程序数据模块 (bpy.app):用于获取Blender应用程序及其内部组件的相关运行配置信息。
(9)属性定义模块 (bpy.props):用于通过自定义属性对Blender内部数据进行扩展。
从上述模块组件的描述中可以看出,Blender插件的开发采用面向对象的方式。Blender Python API通过预置类提供对软件功能的整合与扩展。用户通过创建子类来继承父类中预先定义的属性和方法,它们实现了与Blender程序间的交互接口。在上述模块组件的基础上,通过对其各自函数接口的深入调研后发现,Blender对于虚拟摄影机插件所涉及的对象创建、属性变更、时间线与关键帧设置等操作都提供有相应的API调用接口,可以满足需求设计中各项功能的实现。
2.2.3 Blender内置开发支持
在掌握了Blender开发接口的能力后,还需要对其代码调试的支持情况进行评估。任何程序的开发都不是一蹴而就,在编写代码的同时,还伴随着对程序功能、执行流程的持续测试与验证。Blender软件默认提供了一个名为 “Scripting”的脚本调试工作区,其中分别包含有一个交互式命令行、一个文本编辑器以及一个操作执行信息窗口。
图2 Blender脚本调试工作区
当Blender应用启动时,其内嵌的Python解释器也会同步运行。用户可通过交互式命令行执行单步脚本指令,并借助其代码自动补全功能,更便捷地检索对象的属性、方法等关键字信息。由于Blender API的结构庞大复杂,且在不断扩充,部分接口功能并未在官方文档中详细阐述。因此,在插件的实际开发过程中,很多函数及属性信息,都是借助交互式命令行的关键字提示功能,结合单词语义推测获取的。
相较于交互式命令行,Blender内置的文本编辑器则允许批量调试、运行多行代码。开发者可通过工作区左下角的信息窗口查看单行或多行代码的执行情况。此外,在Blender软件的用户偏好设置中,提供了 “开发者附加内容”(Developer Extras)与“Python工具提示”(Python Tooltips)选项。开启后,当鼠标指针悬停在软件界面任意控件的上方时,将会显示该UI控件所对应编程接口的调用路径,用户通过上下文菜单还可在文本编辑器中直接查看并修改对应的Blender源代码。
综上所述,作为一款开源DCC软件,Blender已经具备了较为完善的开发扩展支持能力,在满足虚拟摄影机插件开发需求的同时,也能为开发者带来良好的调试体验。
2.3 Blender虚拟摄影机插件开发流程
2.3.1 Blender插件基本结构
一个标准的Blender插件通常由四部分代码段所构成。首先是插件的元数据部分,由一个名为bl_info的字典变量构成,其中包含有当前插件的名称、开发者、版本、界面位置等基础信息,用户在安装插件时可从中获取有关此插件的基本信息。
第二部分则包含了插件各项功能的实现代码,通常由操作子类 (Operator Subclass)与函数构成,例如对场景中对象属性的变更或是创建时间线动画等。第三部分是用户交互界面代码,开发者在这部分代码中对插件的按钮、滑块、复选框等操作控件进行布局,同时将之前已创建的功能子类或函数绑定至相关的UI控件之上。
最后一部分则是插件的功能注册模块,包含register与unregister两个预置函数,用于对插件中自定义属性、操作子类以及键盘布局等项目在运行环境中的注册与注销。当用户在Blender软件中激活或禁用指定插件时,将会分别调用它们。
2.3.2 搭建外部开发环境
尽管Blender软件内置了便于脚本调试的交互式界面,但当进行插件这类结构化代码的编写时,仍然需要搭建一套专注可靠的开发环境。Movie-Cam插件选择使用Visual Studio Code作为代码编辑器,并搭配微软官方的Python扩展插件。在配置好Python解释器后,为了获得针对Blender API模块的代码提示等功能,还需要安装名为 “fakebpy-module”的Python软件包。具体安装指令为:
CMD∶>pip install fake-bpy-module-latest。
当Blender插件的外部开发环境搭建完成后,应详细记录所涉及到的各软件版本信息,以便在后期代码调试或版本迭代过程中保持一致。以下为Movie-Cam插件开发时的环境版本信息:
Windows 10专业版21 H2(19044.1889);
Blender v3.2.2;
Python v3.10.6 64bit;
Visual Studio Code v1.70.1;
fake-bpy-module-latest(20220815)。
2.3.3 虚拟摄影机插件功能实现
以下将分别阐述Movie-Cam插件主要功能模块所涉及的技术要点与代码实现。
图3 Movie-Cam插件功能模块图
(1)导入跟踪摄影机
为了能够更加精准地识别和操控虚拟摄影机对象,将其与Blender软件中的默认摄像机进行区分,在编写代码前,需要先使用Blender应用为Movie-Cam插件创建一套单独的外部资产。
此外部资产由三类对象组成,首先是一个标准的摄像机对象,用于实现基本拍摄参数的设置;其次,是一个摄影机造型的网格模型,便于用户更好地识别和选定虚拟摄影机;最后则是一个球状空物体,作为虚拟摄影机的目标跟踪对象。通过将摄影机网格模型与摄像机对象设定为父子关系,以实现两者间的绑定。同时,利用物体的约束 (Constraint)属性将虚拟摄影机的镜头朝向始终跟随至球状空物体,以便用户只需操作球状空物体,即可控制虚拟摄影机的拍摄视角。对于上述资产对象,都为其设置了专属的命名空间,从而为后续的代码编写提供便利。
图4 Movie-Cam插件外部资产模型
接下来便进入到功能代码的开发。由于Blender Python API提供了面向对象的功能绑定,因此在大多数情况下,插件的功能模块都是透过子类继承的方式予以实现。首先通过继承
(2)创建摄影机动画预置
此功能模块提供了不同类型的摄影机动画预置方案,当用户导入虚拟摄影机后,便可从中选择所需的运动效果快速创建专业的拍摄镜头。
通过
此处以滑动变焦 (Dolly zoom)动画预置为例介绍其功能实现。Dolly zoom也常被称作 “希区柯克变焦”,它通过镜头焦距与摄影机位间的反向变化,使得拍摄画面产生一种连续的透视变形,来营造迷离、眩晕的镜头质感。
首先,需根据用户所设定的动画持续帧数
当计算得出摄影机的目标空间向量坐标后,还需要获取随摄影机移动过程中镜头焦距的持续变化值。精确的镜头焦距改变量可通过摄影机与被摄物体间距离、被摄物体 (前景)在实际空间与像场中大小等参数计算得出。为了简化代码实现的复杂度,在Movie-Cam插件中直接将镜头焦距的改变值应用到摄影机动画的出点关键帧。从实际效果来看,在常用镜头焦段下,可以获得比较满意的透视变形效果。式2为简化后的Dolly zoom镜头焦距计算公式。
在上述功能的代码实现中,使用了Blender提供的独立数学工具模块mathutils来对摄影机、空物体等对象的空间向量进行定义与计算。
对于摄影机动画关键帧的创建,Blender提供了keyframe_insert方法来为当前对象在时间线中插入关键帧。需要注意的是,由于空间坐标位置与摄影机镜头焦距分别归于不同的对象属性类型 (物体对象与摄像机对象),故需要分别为这两类数据属性设定关键帧。
至此,滑动变焦这一摄影机动画预置便创建完成。开发者还可根据需要创建更多的摄影机预置动画效果,并添加至
(3)绳索摄影机动画
相较于之前的摄影机动画预置,绳索摄影机模块提供了更为灵活的拍摄视角设定。其实现原理主要基于Blender的钳制 (Clamp To)约束功能。通过将跟踪摄影机钳制约束到一条路径曲线 (NURBS Path)上,便可将摄影机的移动轨迹与之绑定。用户可根据需要在三维空间中自由设定路径曲线的走向,从而创建比现实中高空绳索摄影机更具视觉冲击力的镜头效果。
图5 绳索摄影机应用场景
此功能模块在编写时有两处细节需要注意:首先,应确保摄影机对路径曲线的钳制约束优先于对球状空物体的标准跟随 (Track To)约束,否则可能导致摄影机对球状空物体的跟踪异常。其次,在创建标准跟随约束后,还需要在代码中明确指定跟随轴(Track Axis)与向上 (Up)局部轴的属性参数以确保摄影机镜头视角对跟踪空物体的正确朝向。
与摄影机动画预置模块类似,通过分别创建的三个操作子类,结合动画持续帧数自定义场景属性,即可实现对时间线入点、出点以及动画播放的控制。
(4)动画曲线插值模式切换
当摄影机运动动画创建完成后,Blender会根据所设定的关键帧自动创建一条动画曲线,称作 “FCurve”。F-Curve基于关键帧之间的插值生成,它能够帮助用户更加便捷、平滑地控制动画对象的运动趋势。
默认情况下,F-Curve采用类似贝塞尔曲线(Bezier)的轨迹设置动画插值,即运动物体在整个动画行程中是逐渐加速或减速的。然而对于某些应用场景,使用者更希望摄影机始终保持匀速运动,这时便需要将F-Curve的插值方式从默认的贝塞尔曲线更改为线性 (Linear)模式,Movie-Cam插件通过一个开关选项允许用户在这两种模式间任意切换。
首先,在代码的注册段新建一个名为
对于已创建的摄影机动画,Blender会为每一个运动属性值设置对应的F-Curve通道,并将插值模式保存于动画曲线所覆盖的每个关键帧中。因此,
图6 Blender曲线编辑器
当用户通过插件切换摄影机动画插值模式时,可在Blender的曲线编辑器 (Graph Editor)中直接观察到F-Curve的曲率变化。
(5)构建UI操作面板
在完成了Movie-Cam插件主要功能部分的编写后,便是对用户交互界面的创建。Blender提供了
图7 Movie-Cam插件操作面板
此后,使用类中的draw方法来为UI面板绘制控件并配置布局。开发者即可以将先前创建的操作子类以按钮的形式添加至操作面板中,也可以直接使用Blender中的预置属性或用户自定义属性作为UI面板中的字段控件。当使用属性字段时,面板类会自动根据属性的数据类型匹配相应的UI控件。例如此前所创建的枚举型、整型以及布尔型自定义场景属性,分别会以下拉列表、数值滑块以及复选框的形式在UI面板中对应呈现。
由于Blender在绘制UI面板时,采用即时刷新机制。因此,在面板类中,还可以应用条件判断及数值绑定等手段,实现更为复杂的面板布局显示控制。
(6)镜头焦距快捷菜单
在现实拍摄中,摄影师会借助不同焦段的镜头实现不同的景别视角。同样,Movie-Cam插件参照ARRI Master Prime定焦镜头组中的几个常用焦段,创建了四个操作子类,用于将跟踪摄影机的镜头焦距分别设定至27mm、40mm、75mm以及100mm。
Blender提供了一种称作轮盘菜单 (Pie Menus)的交互界面,允许用户通过快捷键在3D视口中将其呼出,并在几个预设选项间快速切换。类似于视口操作面板,首先通过继承
图8 镜头焦距快捷菜单
接下来,还需要为这个轮盘菜单绑定快捷键。Blender对其插件采用独立的键盘映射,以避免与主程序内置的键盘映射产生冲突。因此需要为Movie-Cam插件创建一个专属的键盘映射,指定其作用域为3D视口,即仅在3D视口处于激活状态时响应用户的快捷键指令。此处,选择将Shift+F作为新的快捷键组合添加到键盘映射中,并设定其执行指令为呼出镜头焦距轮盘菜单。最后,将上述配置对象追加至插件的键盘映射列表 [addon_keymaps]中即可。应当注意的是,在插件末尾的注销函数段,同样需要将之前配置的快捷键映射清除以释放资源。
3 结语
最终编写完成的代码及虚拟摄影机资产文件以zip方式打包后,即可作为插件供Blender安装使用。Movie-Cam插件从其开发工具到宿主平台,均基于开源软件打造和实现。为供广大从业者使用和参考,现已将此插件免费开源发布。代码网址:https://github.com/filmlight/movie-cam-addon。
当下,以虚拟摄制技术为代表的新技术的兴起,促使传统影视与CG动画、数字游戏等领域在制作手段上有着越来越多的共通之处,Movie-Cam虚拟摄影机插件能够同时为上述领域的内容生产带来便利。后续,通过对该插件功能的进一步扩展,还可将其应用于Motion Control、教学演示等诸多场景。