从虚幻 4 中采集 360 度立体电影
从虚幻 4 中采集 360 度立体电影
Gavin Costello, Lead Programmer, Ninja Theory Ltd 在 May 10, 2016 | 功能学习设计导言
早在放出《地狱之刃(Hellblade)》的“垂直切片”构建版供评测之前,我们就一直在努力采集 360 度视频,而我们完整采集的第一个 360 度立体视频被用在了我们的第一个实机演示影片中。
最初我们编写了自己内部的平面 360 度采集系统,它以立方体采集为基础,投影到球体上,再经过变形处理得到最终影像。
虽然这个系统效果尚可,但由于它的平面性质,影像中的一切都给人“巨大”的感觉,而且它不能体现出视频主体的任何亲切感。哪怕影像呈现在您周围,您也会感觉自己是遥远的旁观者,没有身临其境的感觉。
(我认为平面镜头之所以会丧失尺度感,让人感觉过于巨大,是因为人脑有一种从您自身角度出发进行评估的潜意识。它可以判断出您没有立体的会聚感,没有头部移动引起的视差,因此看到的那个物体肯定非常遥远。然后它会把这个信息与那个物体占据了很大一部分视野的事实结合起来,从而让您感到那个物体非常非常大,又非常非常遥远。)
您可以自己试试,采集一帧立体影像,再采集一帧平面影像。来回切换这两帧影像,您会注意到自己不仅丧失了深度感,还丧失了所有尺度感,看到的物体似乎越来越大。
大概就在这个时候,我们开始研究是否能生成具有相应偏移的左眼和右眼影像,从而得到真正的立体影像。在这个过程中我们发现了由 Kite and Lightning 开发人员提供的立体 360 度采集插件。
当时它刚刚出现在 GitHub 上,还不是 UE4 分发版的一部分,但是如今它已经成为虚幻引擎 4 的附赠插件,我强烈建议您试一试。:)
这篇文章的余下部分将谈谈我们忍者理论(Ninja Theory)公司用来采集 360 度立体电影的具体设置和工作流程,我们刚刚公开发布供大家观看的视频就是这样采集的:
原来的非 360 度预告片:
360 度立体版本:
这篇文章假定读者使用的是 UE4 的最新版本(在本文写作时是 4.11.2),不过我们从 4.9 版开始就一直在使用这个插件,因此大部分操作是可以直接应用的(您可能只需要对代码稍作修正,我在结尾的疑难解答部分会提到)。
启用“立体全景电影采集(Stereo Panoramic Movie Capture)”插件,并执行一次快速测试采集:
首先,我们需要确保启用了相应的插件。
在编辑器打开的情况下,转到“编辑(Edit)-> 插件(Plugins)”,然后选择左边的“电影采集(Movie Capture)”设置,确保对“立体全景电影采集(Stereo Panoramic Movie Capture)”选择“启用(Enabled)”。
然后需要关闭并重新启动编辑器。
注:您可能还需要再次快速“构建”,具体取决于您是否在分支中获得了本地更改,因为工具附带的插件 dll 可能已经“陈旧”。
当编辑器重新启动后,再次转到“编辑器(Editor)-> 插件(Plugins)-> 电影采集(Movie Capture)”,并再次检查其是否已启用。
这个插件有多个可以通过控制台命令切换的设置,但是在更改设置前,应该执行一次测试采集,以确保默认设置的工作效果符合预期。
打开命令控制台,用 SP.OutputDir 设置您希望用于转储输出影像的合适文件夹,例如
SP.OutputDir F:/StereoCaptureFrames
注:每次加载编辑器或游戏时都必须进行此操作,因为此设置不会保留。
然后执行一次单帧采集,例如
SP.PanoramicScreenshot
此时系统可能会长时间无响应(估计有一分钟左右),然后将会有两帧影像转储到您在先前用 SP.OutputDir 指定的目录中(好吧,其实是在该目录中的一个日期和时间目录下);一个是左眼影像,另一个是右眼影像。
快速查看一下它们,确保一切都符合预期。如果此时出现色带之类的失真,不用太担心,因为我们会在稍后设法解决(还有一些效果可能有问题,例如光轴不起作用,还有屏幕空间效果——我们也会在更后面的部分解决)。
使左右眼影像自动组合成单一影像的代码更改
在我们的内部工作流程中,我们希望每“帧”只转储一个完整的上/下分格影像。这可以大大方便我们“把所有帧构建成一段电影”,而不必在这个过程中手动组合左右眼影像。
如果这个方法对您也适用,而且您愿意更改代码,下面就是一小段用于在输出影像前组合左右眼影像的代码(未作专门优化)。
1. 打开 SceneCapturer.cpp
2. 定义一个控制变量,以便在已组合和未组合的影像之间切换。我们内部始终希望输出已组合的影像,因此我们只设置了一个全局常量布尔值。
- const bool CombineAtlasesOnOutput = true;
3. 在 USceneCapturer::SaveAtlas 的底部有条件地禁用当前对应每个眼睛的输出(CombineAtlasesOnOutput 检查是唯一的新代码)。
IImageWrapperPtr ImageWrapper = ImageWrapperModule.CreateImageWrapper( EImageFormat::PNG );
if (!CombineAtlasesOnOutput) //*NEW* - Don't do this here if we're going to combine them.
{
ImageWrapper->SetRaw(SphericalAtlas.GetData(), SphericalAtlas.GetAllocatedSize(), SphericalAtlasWidth, SphericalAtlasHeight, ERGBFormat::BGRA, 8);
const TArray& PNGData = ImageWrapper->GetCompressed(100);
FFileHelper::SaveArrayToFile(PNGData, *AtlasName);
}
请注意,这也会揭示出下面“GenerateDebugImages->GetInt() != 0”分支中的一个代码错误,它输出的是 PNGData,但是它应该输出 PNGDataUnrpojected…所以也要修正该错误。
4. 然后添加一些用来组合双眼影像并输出单一影像的新代码;在 USceneCapturer::Tick 中,找到此行:
“TArraySphericalLeftEyeAtlas = SaveAtlas( TEXT( "Left" ), UnprojectedLeftEyeAtlas );”
插入下列代码(我加上了前后的一些代码,以便您确定插入位置是否正确)
TArraySphericalLeftEyeAtlas = SaveAtlas( TEXT( "Left" ), UnprojectedLeftEyeAtlas );
TArraySphericalRightEyeAtlas = SaveAtlas(TEXT("Right"), UnprojectedRightEyeAtlas);
//*NEW* - Begin
if (CombineAtlasesOnOutput)
{
TArrayCombinedAtlas;
CombinedAtlas.Append(SphericalLeftEyeAtlas);
CombinedAtlas.Append(SphericalRightEyeAtlas);
IImageWrapperPtr ImageWrapper = ImageWrapperModule.CreateImageWrapper(EImageFormat::JPEG);
ImageWrapper->SetRaw(CombinedAtlas.GetData(), CombinedAtlas.GetAllocatedSize(), SphericalAtlasWidth, SphericalAtlasHeight * 2, ERGBFormat::BGRA, 8);
const TArray& PNGData = ImageWrapper->GetCompressed(100);
// Generate name
FString FrameString = FString::Printf(TEXT("Frame_%05d.jpg"), CurrentFrameCount);
FString AtlasName = OutputDir / Timestamp / FrameString;
FFileHelper::SaveArrayToFile(PNGData, *AtlasName);
ImageWrapper.Reset();
}
//*NEW* - END
// Dump out how long the process took
FDateTime EndTime = FDateTime::UtcNow();
FTimespan Duration = EndTime - StartTime;
UE_LOG( LogStereoPanorama, Log, TEXT( "Duration:%g seconds for frame %d" ), Duration.GetTotalSeconds(), CurrentFrameCount );
StartTime = EndTime;
这就行了!(也许 Epic 或 K&L 会把这些代码做到插件里。)
注:我们还在上面把输出格式设置成了 JPEG,这是因为在每秒 60 帧的情况下输出 PNG 格式的 4096x4096 帧将会占用许多硬盘空间!
如前文所述,设置 SP.OuputDir,然后调用 SP.PanoramicScreenshot,您应该会看到像这样将左右眼影像组合而成的上下分格影像:
如果您有立体 360 度影像查看器,直接播放该影像,您就会有身临其境的感觉。真是个激动人心的开头!
关于采集过程的超简要说明
下面非常简要地说明一下在您进行采集时发生的事情,主要目的是介绍下面的设置所发挥的作用……如果您需要,可以在网上查到更详细的信息,不过您不必了解更多信息就能使用插件。
您需要了解的一个要点是:如果要以正确方式从 2D 左/右眼影像中采集立体信息,对于任何给定的方向,场景中真正显示正确立体视觉的只有屏幕中央附近的一小部分(也就是“两眼之间”的那一部分),至少对于采集来说是这样。采集器在后台渲染整个标准游戏视图(只不过使用了提供的另一个 FOV),然后丢弃其中的大部分。
在现实中,这一小块区域的宽度取决于 HorizontalAngularIncrement 和 CaptureHorizontalFOV,但是对于“高质量”采集设置,这个宽度是非常小的!
因此,当插件渲染 360 度视图时,它实际上会进行多次不同的采集,每次将摄像机转动一点角度,仅提取中央的一小部分供稍后使用。可以这样理解:采集的样本越多,对于任何一个给定点的立体视觉信息就越精确。
有两个变量控制此过程:
- SP.HorizontalAngularIncrement 控制各次采集之间水平转动多大角度。
- SP.VerticalAngularIncrement 控制每次完成 360 度水平一周旋转后垂直旋转多大角度(从一极到另一极)。
在这两个变量中,尤其需要把 HorizontalAngularIncrement 调整为很低的值。比方说,如果您将它设置为 10 度(完成一次 360 度旋转总共需要进行 36 次渲染),您就会发现影像的深度消失了,看到的是一个个“色带”……不过这里不是字面意义上的色带,而是感知的深度信息呈带状。
配置插件设置以获得最佳质量
要获得更优质的采集结果,除了 SP.OutputDir,您还需要配置该插件的另几个 CVar 控制的设置;这些设置控制的对象包括您要渲染多少水平视图和垂直视图来生成帧(后者生成看上去更准确的立体视觉),您希望目标输出有多大,您希望各个视图有多大(这和您的输出分辨率无关),等等。
我会在这里说明我们使用的具体设置,不过在 StereoPanoramaManager.cpp 顶部也可以找到完整的列表。
我们使用的设置是:
SP.OutputDir F:/StereoCaptureFrames
SP.HorizontalAngularIncrement 2
SP.VerticalAngularIncrement 30
SP.CaptureHorizontalFOV 30
SP.ConcurrentCaptures 6
SP.CaptureSlicePixelWidth 720
SP.ShouldOverrideInitialYaw 0
对于 4096x4096 采集(每个眼睛 4096*2048)
SP.StepCaptureWidth 4096
或者对于 6144x6144 采集
SP.StepCaptureWidth 6144
您可以看到,我们使用的 HorizontalAngularIncrement 值非常小,因此每完成一次 360 度旋转需要分别渲染场景 180 次(每个眼睛)……取更小的值质量会更好,不过渲染每一帧的时间也会加长,我们发现 2 是“甜点”,也就是说取更低的值所带来的质量改进是肉眼察觉不到的。
我们给 VerticalAngularIncrement 设置了一个大得多的值。我们发现垂直方向上的立体视觉信息的差异没有那么大,至少对我们要采集的场景来说是这样(其中包括了人物模型,也就是说并非都是平板墙!),30 是我们的甜点。
这里有必要提醒一下,每帧要渲染的视图总数就是由这两个参数决定的。
事实上,您做的事就是朝头顶上方看,采集一个场景,转动 HorizontalAngularIncrement 度,再次采集,再转 HorizontalAngularIncrement……如此反复,直到转满 360 度。然后向下转 VerticalAngularIncrement,把前面的整个过程重复一遍,然后重复以上过程,直到您看着脚底下为止。
所以实际上,要生成一个立体 360 度采集帧,您需要进行的总渲染次数是:
(360 / HorizontalAngularIncrement) * (180 / VerticalAngularIncrement) * 2(因为它是对应每只眼睛的)
所以这意味着按照以上设置,仅仅为了采集一个立体 360 度帧,您就需要渲染 2520 帧!再说得明白一点,如果您为 60hz 的游戏渲染 2520 帧,通常会生成 42 秒的输出,而上面这种方法生成的输出仅仅是前者的 1/60(假如进行采集是为了生成 60fps 的电影的话)。
SP.ConcurrentCaptures 是上面另一个需要考虑的重要设置,主要是因为如果把它设置得太高(它的默认值就是 30!),就会遇到 VRAM OOM 问题。
这个 CVar 控制的是同时创建和渲染多少采集元件,每个元件都需要与之关联的渲染目标内存,因此会严重消耗 GPU 存储资源。我前面说了“同时”,但实际上 GPU 一次只会处理一个元件,因此在某个时刻,您会遇到在 CPU 上处理渲染帧和用 GPU 帧运行之间的平衡,而这很可能在远低于 30 的情况下出现。我们发现“6”是我们的甜点,如果将它进一步增大(最高可达 30),基本上只会让一帧的渲染时间缩短几秒,考虑到耗用的 VRAM 和长达几天的采集过程中死机(由于 OOM)的可能性,这并不合算!
最后,SP.CaptureSlicePixelWidth 也是需要在这里谈一下的重要设置。我们在很长一段时间里都让它保留默认值,通常是 2048 左右。这个值表示您渲染的“每个视图”的大小(每帧需要渲染 2520 个视图!),因此降低这个值会对总渲染时间造成产生重大影响。它实际上与最终的输出影像无关,您应该根据自己需要做多少垂直“步骤”(180/VerticalAngularIncrement) 以及要为最终影像进行多少多重取样来确定它的大小。
实际上,假设缓冲区是 2048,而您要做 6 个垂直步骤,那么您实际渲染的是一个高 12288 像素的影像。假设最终输出的是每个眼睛 4096x2048 的影像,那么您可以看到,实际上一开始的高度不需要达到那么多像素;有 6xFSAA 就够了!我们在这里实际上只考虑垂直分辨率,您可能还记得上文的影像,我们不会使用全宽影像,只采集中间的一小部分,因此宽度在这里不太重要(只要宽度足以让我们从中央提取足够像素即可)。
相比之下,如果将值设置为 720,然后要渲染实际高度大约 4320 的影像并且将其降低取样至 2048,仍然相当于 2xFSAA(我们发现这肯定已经够好了),那么您对于这 2520 个视图的渲染时间就大约是 40 秒,而原来是 3 分钟左右(并不是严格的线性关系,但效果还是很好的)。
在蓝图中设定所有设置
好,上文已经说过了,这些设置在每次引擎/编辑器运行以后都不会保存。它们都是临时的,每次启动都要重新设置,这真的很麻烦。
因此我们内部的工作方式是写一条控制台命令,它可以替我们设置好所有这些参数,每次采集前调用它就行了。
或者,您也可以把控制台命令输入蓝图中;很抱歉,下图用了表格式的布局,我们只是希望把内容塞进一幅可读的图像中!
有了这样的蓝图,您就可以执行“ce NTCaptureStereo”,它会在开始采集前处理所有参数的设置。这不仅是更方便的工作方式,也会大大降低出错概率,如果要开始持续大约两天的采集,您肯定不希望自己不小心忘记设置某个参数!!!
采集一段电影
好,我们已经讲过了所有重要的设置,还介绍了一个好方法:使用一条控制台命令从蓝图设置并触发采集。
在采集电影时,首先一定要记住的最重要的事情是:要按固定的时间步长运行。
采集一帧需要 40 秒以上,所以您要告诉引擎,必须按固定的时间步长使时间流逝,除非您希望 80 秒的电影只有 2 个输出帧。
多亏了虚幻引擎,这个操作很简单。您只需要提供以下命令行
-usefixedtimestep -fps=
例如,假设您将帧率设置为 60,那么就会在每生成一个完整的帧以后按 16 毫秒的时间步长更新,因此每过一秒钟时间,您就会采集 60 帧。如果您只打算生成 30hz 的电影,那么理论上可以将这个参数设置为 30,不过我们一直是按 60 来采集的,这样我们就可以选择从采集到的帧来生成 60hz 或 30hz 电影。
另外,此时使用 -notexturestreaming 关闭纹理流也是个好主意;如果您花了一天采集电影,然后发现地板纹理全都模糊了,那您肯定会很恼火 ;)
举一个完整的示例:我们在内部要进行采集时,都会在启动游戏/编辑器时传递以下命令行
-usefixedtimestep -fps=60 -notexturestreaming
那么,实际进行电影采集时要怎么操作呢?
我前面提到过 SP.PanoramicScreenshot,不过如果您仔细查看上面的蓝图截屏就会发现还有一个办法可以直接采集一些帧,那就是:
SP.PanoramicMovie
这里的帧数确实就是“从您发出命令时起引擎已运行其更新循环的次数“,比如,如果您以 fps=60 运行,并且将 startime 设置为 120,将 endtime 设置为 240,那么在命令执行后它将等待 2 秒(120 帧),然后采集相当于 2 秒的帧(也就是 120 帧),而您可以再将它们编码为 2 秒长度的 60hz 视频……以此类推。
我们倾向于就从这里的 matinee 采集(毕竟按照固定的时间步长和 40 秒来采集每个帧并不是真正的“实机运行”帧率),因此我们在触发 SP.PanoramicMovie 命令的同时总是会启动 matinee,然后根据我们希望开始和停止采集的 matinee 中有多少秒就可以轻松得出起始帧和结束帧(只要乘以 60 就能得到答案)。
在采集到帧之后
当采集完成后,在您的 your SP.OutputDir// 文件夹中会有一系列标注为 Frame_00000 -> Frame_EndFrame 的帧和一个 Frames.txt 文件。(看到这些您就知道采集完成了!)
如果您使用了上文的自定义代码,这些帧会合并为上下分格的影像,随时可以编码。
然后您可以使用很多方法将它转成电影,不过我们倾向于只使用 ffmpeg 把这些帧编码为 h264 60hz 视频。注:ffmpeg 在网上可以免费下载。
例如,下面的命令行把指定目录中的所有帧编码为一段名为 MyMovie.mp4 的 h264 60hz 电影:
“ffmpeg.exe -framerate 60 -i F:/StereoCaptureFrames/2016.04.26-13.04.34/Frame_%%5d.jpg -c:v libx264 -profile:v high -level 4.2 -r 60 -pix_fmt yuv420p -crf 18 -preset slower MyMovie.mp4”
我不想在这里介绍 ffmpeg 的细节,网上有许多文档,我只知道大多数人会希望使用别的编辑软件来编码。
这时输出的电影基本上就可以使用了,不过您还需要混入音频才能将它发布出去。:)
这就行了!
恭喜……假如我的文章写得还不坏,希望您现在已经知道了如何设置并采集可以在 GearVR 上播放或上传到 YouTube 或 Facebook 的 360 度全立体影像。
关于上传到 YouTube 的说明:必须附加元数据并按合适的分辨率/长宽比输出,例如“4K/UHD/(3840x2160, 16:9)”,在这个例子中不是 4096x4096。否则它在流式播放时会显得非常非常模糊。源帧可以是 4096x4096,但是在编码影片时(不管您用的是什么程序)要记住输出“标准”UHD 分辨率和 16:9 长宽比,而不是 4096x4096 1:1 长宽比影像(虽然这可以用在 GearVR/Oculus360 播放中)。
其他问题和说明
还有几个问题我觉得可以在这里谈一谈。其中有些是一直都有的,有些已经在 4.11 中修正了,但在较早版本的引擎中有。
1. 不是所有效果都有用
这条很重要,我希望读者可以综合上面的信息来理解。您的场景实际上是被分成一块块“方砖”来采集的(使用了放大的 FOV),您要执行一系列水平步骤,然后在一系列垂直步骤中重复所有水平步骤。因此,应该应用于整个“场景”的屏幕空间效果(例如晕映)不会起作用,应该关闭。
而且这意味着光轴也不会起作用。您可能会得到一系列有轴源在屏幕上的“瓷砖”,而在下一个瓷砖上因为屏幕上没有轴源,所以就没有光轴。这会使最终的影像中出现开启光轴和关闭光轴的部分,而不会达到您需要的效果。因此很遗憾,光轴也不能使用(虽然这是很精彩的效果)。
如果您不明白为什么我们的电影中光轴效果很正常,那是因为我们有一个自定义的全局空间参与媒体解决方案,使全局空间效果都能起作用。:)
同样的,如果您使用了屏幕空间扭曲,它也不会起作用。因为您在每个点上只截取屏幕中央的部分,所以只有屏幕中央会出现扭曲,周围不会。
请注意,全局空间扭曲仍然有效,所以在粒子之类对象上的扭曲效果还是会很棒。:)
一般而言,屏幕空间效果不会起作用,所以在考虑内容时要注意这一点。
2. 并不是所有东西都遵循固定时间步长
在制作垂直切片场景的过程中,这个问题很早就困扰我们了。我们在游戏中放了一段表现一张脸的过场剧情电影,但是不知为什么我们只能得到大约 2 帧的视频,然后就没有了。这是因为系统是按“正常”速率处理视频的,而不是按我们所需要的每次渲染 16ms 的固定速率,也就是说我们每采集一帧就会跳过大约 40 秒的视频!
与此类似,在 GPU 上每帧使用真实 Δ 计算(而不是“游戏时间”)的项目也不会遵循这个设置。我们过去遇到过一些有趣的例子,比如使用普通材质的火焰看上去很不错,而火焰中的 GPU 粒子“燃屑”却以不可思议的速度乱飞,这是因为它们的更新速度没有相应地绑定。可以使用粒子系统中的一些设置来设定固定的更新时间,以绕过这个问题。
3. 默认设置下(出厂设置)采集不会拾取后期处理体积!
我们解决这个问题的办法是让采集视图使用来自播放器的后处理设置。
举一个简单的例子,如果您转到 USceneCapturer::InitCaptureComponent 然后添加下列代码(在调用 RegisterComponentWithWorld 前),它就会使用播放器的后处理设置。
//*NEW* Set up post settings based on the player camera manager
if (GetWorld())
{
APlayerController* PlayerController = GetWorld()->GetFirstPlayerController();
if (PlayerController && PlayerController->PlayerCameraManager)
{
CaptureComponent->PostProcessSettings = PlayerController->PlayerCameraManager->CameraCache.POV.PostProcessSettings;
CaptureComponent->PostProcessBlendWeight = PlayerController->PlayerCameraManager->CameraCache.POV.PostProcessBlendWeight;
}
}
// Disable effects that we don't want for capture
CaptureComponent->PostProcessSettings.bOverride_GrainIntensity = true;
CaptureComponent->PostProcessSettings.GrainIntensity = 0.0f;
CaptureComponent->PostProcessSettings.bOverride_MotionBlurAmount = true;
CaptureComponent->PostProcessSettings.MotionBlurAmount = 0.0f;
CaptureComponent->PostProcessSettings.bOverride_ScreenSpaceReflectionIntensity = true;
CaptureComponent->PostProcessSettings.ScreenSpaceReflectionIntensity = 0.0f;
CaptureComponent->PostProcessSettings.bOverride_VignetteIntensity = true;
CaptureComponent->PostProcessSettings.VignetteIntensity = 0.0f;
//*NEW*
注:为了让某些后处理效果(例如材质效果)起作用,您可能需要强制设定采集元件具有 ViewState。
当然,初始化只会进行一次,因此如果您要在各种体积之类中穿行,那就需要提取这段代码,然后每帧调用一次,或者改写 FScene::CreateRenderer 以复制播放器的后处理(每帧、每个采集视图都进行)。
4. 我的输出文件变绿了!帮帮我!!
我们在使用较早版本的引擎时遇到过这个问题。这是因为您有多个采集元件(别忘了 SP.ConcurrentCaptures),但是它们的名称都相同,因此系统只是反复使用其中的第一个,然后却依次读取它们。因为绿色是“透明”的颜色,所以您看到的就是从您没有渲染的帧中读取的结果。
解决这个问题的办法就是在 USceneCapturer::InitCaptureComponent 中给它们各起唯一的名称
在 4.11 中这是通过 MakeUniqueObjectName 调用完成的,但是如果您使用的是较早的版本,可以用您喜欢的其他任何办法操作(我们是设了一个静态索引,让它通过增量方式生成唯一的名称)。
5. 阴影只出现在一个眼睛的影像中!帮帮我!!
好吧,我们也遇到了这个问题,它在 4.11 中似乎已经被修正了。这个问题是立体渲染路径的优化造成的,这个优化让您只生成一次阴影缓冲区,然后将这些缓冲区用于双眼的影像。
目前我们的解决办法就是强制它为每个眼睛重新生成阴影。
也是在 USceneCapturer::InitCaptureComponent 中,只要注释掉设置 CaptureComponent -> CaptureStereoPass 的那一行,就能解决这个问题。
这意味着您要多做一些阴影深度渲染,不过渲染本来就已经很慢了,您也许不会注意到它的影响。
6. 场景跟我预计的相比转了 180 度!帮帮我!!
我认为这个问题也已经在 4.11 中修正了,不过我们也遇到过。我们的解决办法就是手动加 180 度。在 USceneCapturer::Tick 中,找到这一行:
“Rotation.Yaw = (bOverrideInitialYaw) ?ForcedInitialYaw :Rotation.Yaw;”
把它改为:
“Rotation.Yaw = (bOverrideInitialYaw) ?ForcedInitialYaw :Rotation.Yaw + 180.0f;”
7. 跨多台机器分散采集
这绝对是缩短渲染整段过场剧情的最佳方法。
但是请记住,并不是所有效果都是确定性的,例如粒子可能有一些生成随机起始位置的节点,使粒子显得多样。如果您用两台机器采集一个场景,那么当您从一组帧切换到另一组帧时,可能会看到画面突变,因为这些元素不是在同一个位置。
可以在粒子系统中“播种”,使他们每次运行时都生成相同的粒子,不过您需要非常仔细地考虑到每一种情况,否则您可能做了很长时间的采集,仍然发现某个效果出现突变。
我们在传统上不是这样做的,而是找到场景中的“自然切割点”,也就是摄像机镜头跳转的地方,淡入淡出的地方,或者观看者不会注意到内容突变的任何地方,然后把在这些地方切割的影片放在不同机器上采集。
不过要注意,如果要采用这个办法,一定要在切割片段的头尾额外采集一点“缓冲”部分。如果您不小心过早地停止采集某切割片段,或者在 matinee 中采集下一切割片段时开始得过晚,那么您可能需要重新采集整个切割片段,这会耗费大量时间……特意将切割片段提早/延后一秒左右,可以保证得到您需要的所有帧。
有一点很好,那就是如果 matinee 在一个大的 matinee 中,那么帧编号会与之相应。如果在不同的帧开始和停止,不同切割片段的帧的名称 (Frame_StartFrame->Frame_EndFrame) 也会不同,您可以把它们放到同一个文件夹中,不必另外将它们组合成影片。
8. 弹出了一些窗口,把我的编辑器/采集拖延一整晚,恨啊!
在“编辑(Edit)-> 编辑器首选项(EditorPreferences)-> (常规)其他((General)Miscellaneous)”下面有一个很方便的设置,叫做“在后台运行时减少对 CPU 的使用(Use Less CPU when in Background)”。
把它关了……(在采集的时候)。这样一来,如果您离开机器,让它整夜采集,当有人给您发送一条消息导致弹出窗口时,不会使编辑器暂停,延误您的采集。
9. 极点的深度不正确
很遗憾,这是这个技术的局限性之一。一般说来,当您直视上方时不会注意到这个问题,不过当您“站”在地上时,肯定会在看地面时注意到。
我们解决这个问题的办法是,通过消退遮蔽每个帧中的底部行,所以看上去就好像您脚下有个黑圈一样。这也许不能让所有人满意,不过我们觉得这实际上可以让人们不愿意看那里或体验不正确的深度,这还是有好处的!
10. 单眼/双眼影像中有些几何体消失了
我们解决这个问题的办法是禁用遮蔽查询。我不记得这个问题具体是怎么回事,不过它和引擎使用上一帧的遮蔽结果的方式和更新“上一帧遮蔽”信息前进行了多少渲染有关,是帧更新间隔中的并发采集数造成的。
在采集前把 r.AllowOcclusionQueries 设置为 0 就可以解决这个问题。
好,现在真的说完了!
我知道这篇博客很长,不过我希望无论您是刚开始采集,还是采集了一段时间但遇到了上面提到的一些问题,都能找到有用的信息。
祝您进行自己的采集时好运。得到大量优质的立体 360 度镜头肯定是好事!!:)
~Gav
一些供试看的《地狱之刃》帧 (6K)
单击每个图像可访问 6K 版本。
再分享一下我老师大神的人工智能教程吧。零基础!通俗易懂!风趣幽默!还带黄段子!希望你也加入到我们人工智能的队伍中来!https://blog.csdn.net/jiangjunshow