本文章转载自 智伤帝的个人博客 - 原文链接
前言
最近我的学弟找我咨询关于 Unreal Sequencer 渲染输出的问题。
之前没有折腾过这个一块,于是就跟进了一下,顺便学习 Sequencer 的序列输出。
另外最近另一个师弟也研究了差不多的问题,发了一篇 B站专栏 , 在这里推荐一下 链接
手动操作
在自动化操作渲染之前,需要先搞清楚怎么手动操作 Sequencer 进行渲染。
其实操作起来不难,打开 Unreal 的定序器,点击上面的 Render 图标打开 Render Movie Setting
然后配置好渲染设置就可以点击渲染,就可以将影片批量渲染出来。
自动化操作
下面就是将手动操作转为 Python 的自动操作。
具体的操作脚本其实可以参考官方的脚本,在官方 SequencerScripting 插件里面有渲染相关的 Python 脚本。
安装了 Unreal 引擎之后可以根据地址查找 \Engine\Plugins\MovieScene\SequencerScripting\Content\Python
sequencer_examples
就有输出的 Python 代码,不需要自己查文档研究怎么搭建代码。
参照 render_sequence_to_movie
的代码即可输出。
其中比较坑的点在于 OnRenderMovieStopped
的 delegate
接入 Python 回调需要一个 global 函数才可以,否则执行完成的回调函数不会触发。
官方的案例是放到最外层执行的,如果不凑巧回调函数写在函数里面,就需要利用 global 关键字解决这个问题。
官方案例还没能实现一个需求,就是批量将不同 Sequence 同时渲染出来。
然而 render_movie 这个函数是不阻塞的,如果使用 for 循环会一直把所有的 render_movie 持续执行。
所以这里进行渲染需要通过回调来实现逐个渲染的调用。
Python 代码
# -*- coding: utf-8 -*-
"""
渲染 sequencer 的画面
选择 LevelSequence 批量进行渲染
"""
from __future__ import division
from __future__ import print_function
from __future__ import absolute_import
__author__ = 'timmyliang'
__email__ = '[email protected]'
__date__ = '2020-07-14 21:57:32'
import unreal
import os
import subprocess
from functools import partial
def alert(msg):
unreal.SystemLibrary.print_string(None,msg,text_color=[255,255,255,255])
def render(sequence_list,i,output_directory="C:/render",output_format="{sequence}"):
# NOTE 如果超出数组则退出执行
if i >= len(sequence_list):
# NOTE 输出完成 打开输出文件夹的路径
subprocess.call(["start","",output_directory], creationflags=0x08000000,shell=True)
return
# NOTE 获取当前渲染序号下的 LevelSequence
sequence = sequence_list[i]
# NOTE 配置渲染参数
settings = unreal.MovieSceneCaptureSettings()
path = unreal.DirectoryPath(output_directory)
settings.set_editor_property("output_directory",path)
settings.set_editor_property("output_format",output_format)
settings.set_editor_property("overwrite_existing",True)
settings.set_editor_property("game_mode_override",None)
settings.set_editor_property("use_relative_frame_numbers",False)
settings.set_editor_property("handle_frames",0)
settings.set_editor_property("zero_pad_frame_numbers",4)
settings.set_editor_property("use_custom_frame_rate",True)
settings.set_editor_property("custom_frame_rate",unreal.FrameRate(24, 1))
# NOTE 渲染大小
w,h = 1280,720
settings.set_editor_property("resolution",unreal.CaptureResolution(w,h))
settings.set_editor_property("enable_texture_streaming",False)
settings.set_editor_property("cinematic_engine_scalability",True)
settings.set_editor_property("cinematic_mode",True)
settings.set_editor_property("allow_movement",False)
settings.set_editor_property("allow_turning",False)
settings.set_editor_property("show_player",False)
settings.set_editor_property("show_hud",False)
# NOTE 设置默认的自动渲染参数
option = unreal.AutomatedLevelSequenceCapture()
option.set_editor_property("use_separate_process",False)
option.set_editor_property("close_editor_when_capture_starts",False)
option.set_editor_property("additional_command_line_arguments","-NOSCREENMESSAGES")
option.set_editor_property("inherited_command_line_arguments","")
option.set_editor_property("use_custom_start_frame",False)
option.set_editor_property("use_custom_end_frame",False)
option.set_editor_property("warm_up_frame_count",0.0)
option.set_editor_property("delay_before_warm_up",0)
option.set_editor_property("delay_before_shot_warm_up",0.0)
option.set_editor_property("write_edit_decision_list",True)
# option.set_editor_property("custom_start_frame",unreal.FrameNumber(0))
# option.set_editor_property("custom_end_frame",unreal.FrameNumber(0))
option.set_editor_property("settings",settings)
option.set_editor_property("level_sequence_asset",unreal.SoftObjectPath(sequence.get_path_name()))
# NOTE 设置自定义渲染参数
option.set_image_capture_protocol_type(unreal.CompositionGraphCaptureProtocol)
protocol = option.get_image_capture_protocol()
# NOTE 这里设置 Base Color 渲染 Base Color 通道,可以根据输出的 UI 设置数组名称
passes = unreal.CompositionGraphCapturePasses(["Base Color"])
protocol.set_editor_property("include_render_passes",passes)
# protocol.set_editor_property("compression_quality",100)
# NOTE 设置全局变量才起作用!
global on_finished_callback
on_finished_callback = unreal.OnRenderMovieStopped(
lambda s:render(sequence_list,i+1,output_directory,output_format))
unreal.SequencerTools.render_movie(option,on_finished_callback)
def main(output_directory="C:/render",output_format="{sequence}"):
# NOTE 获取当前选择的 LevelSequence
sequence_list = [asset for asset in unreal.EditorUtilityLibrary.get_selected_assets() if isinstance(asset,unreal.LevelSequence)]
if not sequence_list:
alert(u"请选择一个 LevelSequence")
return
if not os.access(output_directory, os.W_OK):
alert(u"当前输出路径非法")
return
elif not os.path.exists(output_directory):
# NOTE 路径不存在则创建文件夹
os.makedirs(output_directory)
elif os.path.isfile(output_directory):
# NOTE 如果传入文件路径则获取目录
output_directory = os.path.dirname(output_directory)
render(sequence_list,0,output_directory,output_format)
if __name__ == "__main__":
main()
选择 Sequencer 执行上面的脚本,就可以自动批量输出 Sequencer 了。
总结
理论上 Python 调用蓝图方法做到的功能, 蓝图应该也可以做到的。
但是经过我的测试,我发现 蓝图 的 get_image_capture_protocol
返回值是 基类。
导致无法获取 CompositionGraphCaptureProtocol
这个类
也就无法设置 include_render_passes
的值了,这样导致蓝图输出会将所有通道输出,而不能实现单一通道的输出。
另外这一次没有制作 GUI ,我还在纠结使用 Qt 还是 Unreal 原生的界面。
Unreal使用 Editor Utility 创建的 UI 是二进制 uasset ,无法向前兼容 Unreal 版本。
UI的功能响应上也没有 Qt 成熟。
但是无论如何,Unreal 的 UMG 是原生体验,嵌入样式各方面都比较舒服的。
虽然 Qt 可以写一套 Qss 来解决样式问题,但是在 Unreal 中实现 Dock 目前还是无解。