acecase 发表于 2022-7-6 11:56

MoverRenderQueue联机远程渲染

前言:近期的任务是做成一个类似渲染农场的东西,所有制作人员可以直接发送渲染任务到主机进行远程渲染,类似于以前UE静态构建时代的swarm-agent,区别是大家的配置普遍年老色衰,就不费心费力的折腾了,仅制作客户端和渲染农场,也就是服务端的通信渲染
<hr/>如何联机
首先想到需要做计算机之间的互相识别,一开始的想法是通过客户端的编辑器直接发送到服务端的编辑器,但ue本身常驻内存并不是很好的解决方案,何况你不确定是否有别人用得到渲染机工作,所以长期持有是不可能的,随后想到可以做一个服务器运行的微端,主要任务是监测是否有渲染任务接入进来,而这一步,并不需要UE的参与,在获取到正确的UE信息以后,再打开编辑器,进行读取配置操作,然后在输出.输出结束后再把UE关闭,使用PySimpleGU搭建一个交互界面实际代码不超过300行.


实践中用的是python的强制关闭,直接干掉ue的各类附带进程:
os.system('taskkill /f /t /im '+exe_name)一开始的exe_name是UnrealEdit.exe,后来有想到这台电脑并不是只会存在一个ue程序,那直接杀掉UnrealEditor.exe,正在这台电脑工作的程序也会强制结束.
进行到这里的时候并没有卡很久,有想到其实UnrealEditor-Cmd.exe也可以用,甚至这才是最佳使用方式,首先它启动快,其次它是独立进程,而且还支持自定义脚本在启动后直接运行,不想在编辑器打开那样,你还需要放到指定的目录才能生效,然后想到在找这方面的资料时看到了官方的介绍,里面有提到他们内部其实是实现了这一远程渲染的功能的,需要特殊说明的是,ue的启动名字到UE5才是UnrealEditor,4.26和4.27都是UE4Editor,为了识别当前需要关闭的是哪一个版本,你可以在客户端发送的数据包里通过os.path.abspath(os.curdir)去解析引擎版本,也可以在服务端做这一步操作,但相信最简单的办法是这个:



在引擎的中存放的参考样例也说明了 cmd模式是非常契合mrq的.所以任务只不过是把客户端的mrq参数用文本形式或者其他格式保存下来,然后发送到服务端,再解析回UE能识别的内容就好.
<hr/>网络通信
考虑到把整个项目发送给服务端是不现实的,因为大部分项目大而细碎,传输时间会很久,那只能考虑直接通过网络访问服务端路径了, 如果是开放端口访问这里存在的三个问题,第一如果是一百台客户端,服务器就需要记录一百台端口信息,这显然不是最优解,第二是必须做到开放的端口在渲染结束以后马上关闭,不然每个计算机直接就有了相互读取的可能,第三则是由于端口开放会导致可能的计算机安全隐患,IT那边大概率不同意这么干,就算同意了,在多个部门的局域网策略下,未必每台电脑都能ping通
<hr/>映射路径
后来的解决办法是用网络映射,固定的网络映射,当客户端发送任务时,将当前项目所在路径局域网共享出去,当客户端接收到渲染完成事件的时候,断开网络共享,与之相对的,服务端只需要根据接收的数据解包,获取共享路径,映射链接,执行渲染任务,然后断开链接就可以,因为对于网盘映射这个功能来说,只需要知道局域网的名字或者ip就能将他固定到统一的位置,新的任务再刷新映射就行,当然如果你发现自己的脚本没有管理员权限设置共享,需要使用bat去处理文件客户端共享的问题,但是那也很简单 不在本文范围内,
当然,这种方式带来的限制就是,你必须把项目的缓存数据设置到项目路径下,这样服务端就不需要再重新编译你的项目材质了,打开的速度完全取决于你的磁盘速度和网络读取速度:


<hr/>配置设置
在python中配置渲染任务主要使用unreal.MoviePipelineQueueSubsystem;
通过get_queue获得渲染队列,在队列中的任务的各类设置则是get_configuration.


<hr/>修改设置
比如如果需要修改输出的尺寸大小,可以使用job.get_configuration().find_or_add_setting_by_class(unreal.MoviePipelineOutputSetting). output_resolution获得以及修改;同样的,如果是需要修改采样次数,就使用job.get_configuration().find_or_add_setting_by_class(unreal.MoviePipelineAntiAliasingSetting),
# outputSetting = job.get_configuration().find_or_add_setting_by_class(unreal.MoviePipelineOutputSetting)
# temp_xy=str(outputSetting.output_resolution ).split("x: ").split("}>")
# temp_x=str(outputSetting.output_resolution.x)
# temp_y = str(outputSetting.output_resolution.y)
# outputSetting.file_name_format = "{sequence_name}.{frame_number}"
# antiAliasing= job.get_configuration().find_or_add_setting_by_class(unreal.MoviePipelineAntiAliasingSetting)
# antiAliasing.spatial_sample_count=8
# antiAliasing.temporal_sample_count=4
# # none=0 fxaa taa msaa
# # antiAliasing.anti_aliasing_method="AAM_MSAA"最初的想法是把常用的会修改的参数列出来写入文件然后传输给服务端,但是后来发现需要写的东西太多了,不只是输出尺寸,采样次数,暖场帧数,渲染帧率,控制台命令,输出格式之类的东西,也包括了很多不确定是否用的到的渲染通道,当然可以用穷举法把他们都列完,但是一来太费时间,而来如果后期有新增的自定义渲染通道,还需要手动更新维护,这并不是好的解决方案.
翻了官网python节点不少时间 锁定export_config_to_asset这个函数,它的功能是直接保存当前设置的配置信息,刚好够用,不至于前期有一个参数就加一个参数,那样传输数据非常多而且不好控制修改.
unreal.MoviePipelineEditorLibrary.export_config_to_asset(job.get_configuration(),
                                                                     "/Game/Developers/Star_Render/",
                                                                     "Star_MasterConfig",
                                                                     True)
<hr/>读取设置
接下来就简单了 启动cmd模式的UE,这里的方案是写入bat文件中执行;
UE4Editor-Cmd.exe <path_to_uproject> <map_name> -game -MoviePipelineLocalExecutorClass=/Script/MovieRenderPipelineCore.MoviePipelinePythonHostExecutor -ExecutorPythonClass=/Engine/PythonTypes.MoviePipelineExampleRuntimeExecutor -LevelSequence=<path_to_level_sequence> -windowed -resx=1280 -resy=720 -log这段命令的意思就是打开指定项目<path_to_uproject>的指定地图 <map_name>,然后执行MoviePipelineLocalExecutor类下面的python重载事件,也就是MoviePipelineExampleRuntimeExecutor,他的位置在
\Engine\Plugins\MovieScene\MovieRenderPipeline\Content\Python\MoviePipelineExampleRuntimeExecutor.py,修改里面的MoviePipelineExampleRuntimeExecutor函数,
只需要在服务端获取到Star_MasterConfig这个保存文件的位置,然后打开就行,但只是加上unreal.load_asset(unrealsoftpath)不生效,因为这是配置,还需要挂载在job上,job就用pipelineQueue.allocate_new_job(unreal.MoviePipelineExecutorJob)生成一个空的,然后记得指定sequence和map.
这里面有两个坑点,第一是<map_name>必须有,<map_path>也要存在,不然无法指定job.map,第二不支持中文项目名,这是我的bat命令行:
chcp 65001
"C:\Program Files\Epic Games\UE_5.0\Engine\Binaries\Win64\UnrealEditor-Cmd.exe" B:\render_0622.uproject Level_LimeStoneTemple -game -MoviePipelineLocalExecutorClass=/Script/MovieRenderPipelineCore.MoviePipelinePythonHostExecutor -ExecutorPythonClass=/Engine/PythonTypes.MoviePipelineExampleRuntimeExecutor -LevelSequence=/Game/Developers/baixiaosheng/NewLevelSequence_2.NewLevelSequence_2 -MapPath=/Game/Assets_Content/Other/Map/LimeStoneTemple/Level_LimeStoneTemple.Level_LimeStoneTemple -windowed -resx=1280 -resy=640.0 -log对比官方,"chcp 65001" 可以让命令行编码接受中文,你需要添加一个MapPath参数,然后使用UE自带的命令行解析函数parse_command_line获取参数的实际路径。
最后执行MoviePipelineExampleRuntimeExecutor.activeMoviePipeline.initialize开始渲染
<hr/>监听事件
程序到这告一段落,接下来需要添加的是各种判断条件,比如停止渲染,比如重新渲染,我们通过socket来解决服务端和客户端的通信,告诉服务器当前的状态,从而执行exit功能或者break功能.


这里存在的问题是,当第二个客户端进入通信的时候,如何判断服务器当前的状态,简单的办法是不让第三方进入通信,当服务端正在渲染任务时,创建一个自定义文件夹,客户端判断文件是否存在来确定当前服务端是否正在运行渲染任务,如果强制终止别人的任务,自然能从第三方变成直接通信,把前一个渲染任务挤掉.
<hr/>事件回调
监听事件是在服务端中的运行程序里实现调用,因为更容易和客户端进行通信,而判断渲染任务本身的状态,比如当前进度,每一帧要调用的事件,渲染结束以后的事件,都应该放在服务端的ue运行脚本里,使用的官方的MoviePipelineExampleRuntimeExecutor直接修改,因为这里面的MoviePipelineExampleRuntimeExecutor函数已经默认封装好了,只需要重载自己想要的功能就可以,在on_movie_pipeline_finished函数中,我们做清理工作,比如断开映射,清楚渲染状态,设置结束标志.甚至如果觉得不安全,在这一步,你已经可以使用kill命令自杀UE完成整个渲染任务的最后环节
在客户端的自定义脚本里,启用多线程,当渲染开始的时候执行check_frame,循环监听任务完成标志,当任务完成时可以自定义弹窗提示,也可以直接打开渲染的文件夹,甚至你也可以用socket来不断更新渲染进度在状态栏里显示,进度栏的用法是job. set_status_progress(float)


<hr/>样例中有提到可以重载remoteUI,理论上将客户端脚本绑定上去没问题,但更好的办法是新增一个UI,因为原本的remoteUI相当于在本机上新开一个程序运行,对于测试场景和蓝图的bug是很有用的,不能取代。
如果你对客户端部分的执行代码感兴趣,可以点击此处

参考:
Movie Render Queue Enhancements in 4.26
export_config_to_asset
UE4批渲染cmd篇 - 戴巍的文章 - 知乎
页: [1]
查看完整版本: MoverRenderQueue联机远程渲染