在Unity中创建全景视频



为什么需要全景视频?



我的大部分博客文章都是针对游戏开发人员的。如果您是其中之一,则可能想知道您可能对创建360°视频感兴趣。虚拟现实是一个非常成功的行业,每年都会在各种平台上发布更多游戏。许多玩家通过预告片了解VR游戏,不幸的是,这些预告片是以2D形式录制的。





如果您想更进一步,可以创建一个可选的全景预告片以展示游戏的全部潜力。尽管播放VR通常需要昂贵的耳机,但YouTube本身就支持全景视频,而无需其他硬件。而且,如果您有电话,可以使用Google Cardboard轻松将其变成VR耳机



这可以为人们提供更加身临其境的体验,并使他们对游戏的意义有所了解。电影It(请参见下面的预告片)和The Conjuring等电影通过创建VR游戏(全景视频中的预告片)充分利用了这一点,以提供更互动的体验。





最后,全景视频非常适合用于教育目的,如《所有发现的系外行星:叙述的360 VR旅程》中所示



处理全景视频



您很可能熟悉YouTube及其运作方式。但是,全景视频并不为许多人所熟悉。大多数视频记录在仅捕获一小部分环境的摄像机上。全景视频同时记录了各个方向上发生的一切。这通常需要称为全向摄像机的特殊摄像机。他们要么使用曲面镜将周围环境反射到传统相机中(就像鱼眼镜头一样),要么使多个相机朝向不同的方向。一个这样的相机就是GoPro Omni(请参阅下文),它实际上是一个包含六个标准鱼眼镜头的设备。





显而易见,为什么录制全景视频所需的设备通常比传统相机要贵。但是,这不是限制其分布的唯一原因。正确播放全景视频需要特殊的软件和硬件。屏幕像摄像机一样,仅设计用于重现环境的一小部分。通过允许观看者“旋转”视频以便他们环顾四周,YouTube得以解决了这一限制。如果您正在观看来自手机或平板电脑的全景视频,通常会在太空中移动它以360°角度观看“球体”的不同部分。



YouTube支持两种类型的全景视频:单声道立体声(后者通常称为VR视频))。两者之间的区别在于,立体声视频专用于VR耳机,可以传达传统视频无法感受到的深度感。这不是通过一个视频而是两个视频,每个眼睛一个视频来实现的。这两个视频是由两个摄像机同时录制的,它们之间的距离可与两眼之间的距离相媲美。结果,就像在日常生活中一样,VR视频可以“欺骗”大脑以感知真正的距离。



下表显示了这两类视频的要求。



名称 360°视频 虚拟现实视频
单核细胞增多症 立体声
2D 3D
链接 支持 支持
帧频 24、25、30、48、50、60 25、30、50、60
格式 等距

纵横比2:1
上下等距

纵横比1:1
解析度 推荐:7168 x 3584,最大8192 x 4096 5120 x 5120至8192 x 8192


请注意,YouTube一直在努力改善对全景视频的支持,因此将来可能会有所改变。在遵循这些规范之前,请阅读表中的参考信息。



投影影片



全景视频的第一个棘手的部分是将球体编码为平坦的表面。这是必要的,因为尽管全向摄像机可以在所有方向上进行记录,但是仍然需要将每个帧转换为传统的平面图像。



有很多种方法改造(严格地说,项目)的球体上的矩形。也许最有名的是圆柱投影,自1569年制图师Gerard Mercator开始使用圆柱投影以来,圆柱投影就很流行,将行星表面转换成平面图。



但是,等角投影最常用于全景视频中(请参见下文)。





空间声音



全景视频(单声道和立体声)都可以支持空间音频。标准视频具有两个音频通道(左和右),用于在使用立体声耳机收听时产生方向感。空间音频使您可以编码声源的真实方向,而不仅仅是左右。这意味着使用正确的设备,您可以完全沉浸其中,并理解周围声音的方向。



尽管有VRVR视频的名称,但它不如“真实” VR游戏好,因为即使VR耳机可以跟踪头部的动作,视频也是从固定点渲染的。就像在VR游戏中一样,无法移动并看到物体背后的东西。这可能会有些混乱,尤其是在与环绕声结合使用时。当您长时间从事VR项目(游戏或视频)时,您可以习惯它。重要的是进行足够详细的游戏测试,以确保最终产品的便利性,并找出使玩家或观众感到不适的限制。



全景视频不需要空间音频。YouTube支持两种格式:



  • 一阶Ambisonics
  • 具有头锁立体声的一阶Ambisonics


Ambisonics是一种声音格式,不仅可以记录声音,还可以记录声音的来源。一阶Ambisonics(FOA)使用四个音频通道对声源方向性进行编码。为了大致了解这是如何工作的,我会说录制FOA声音有点像用四个麦克风录制。常见的误解是这四个麦克风位于主要位置(一个在观看者前面,另一个在后面,左侧第三个和右侧第四个)。这不是Ambisonics的工作方式,但是我们可以在其他文章中谈论它。



尽管FOA使用4个频道,但YouTube还支持6频道版本,该版本仅添加了传统的左右立体声频道。这种格式称为带头锁立体声的FOA...



应特别注意编码全景视频(立体声或单声道),因为并非所有格式都支持4或6个音频通道。YouTube建议以下内容:



  • 格式:MP4,MOV
  • 编解码器:H.264,ProRes,DNxHR


如果您使用的是Premiere Pro 2018或更高版本,则最好将视频编码为Quicktime ProRes编解码器。您可以在此处阅读有关YouTube支持的空间音频格式的更多信息



如何在Unity中创建全景视频



Unity 2018.1中新增的众多功能之一是能够以与YouTube全景视频兼容的格式从相机拍摄屏幕截图。在Unity网站上的帖子中对此功能进行了简要描述:Stereo 360图像和视频捕获不幸的是,其中没有用于测试的场景参考。



理论



通常,在运行游戏时会记录游戏剪辑。这适用于标准视频,但通常不适用于全景视频。主要原因是渲染全景游戏需要更大的面积,这会导致帧速率急剧下降。一种更标准的方法是在后台渲染和导出每个帧,以便以后可以在第三方软件(例如ffmpeg或Premiere Pro)中对其进行编辑。这就是我们将在本教程中执行的操作。



在Unity中渲染全景场景是一个非常简单的三步过程:



  • 将场景渲染为立方体贴图纹理(Unity首选的全景纹理格式)
  • 将立方体贴图转换为等角投影(YouTube全景视频的首选格式)
  • 将等距投影另存为PNG


由于相机在Unity中的工作方式,前两个步骤是必需的。



如果您熟悉Unity,则可能知道存储全景纹理的首选方法是使用立方体贴图,这是一种包装六个不同图像而不会变形的方法(请参见下文)。立方贴图通常用于天空盒和反射探测器,因此您之前可能遇到过。





要创建全景纹理,Unity会六次渲染场景,每次以不同角度渲染。摄像机平移时就好像与立方体的六个侧面之一对齐。结果,每个全景屏幕截图都是链接在一起的六个传统屏幕截图。



填写立方体贴图后,第二步是将其转换为YouTube兼容格式。如前所述,YouTube需要等角全景视频。这种转换通常需要对复杂的着色器进行编码,但是幸运的是,Unity为此专门添加了一个功能。



最后一步将是将等距纹理导出到PNG文件。渲染完所有视频帧后,您可以使用ffmpeg或Premiere Pro等程序合并它们。



代码



第一步是创建一个脚本(在下面的代码片段中称为脚本Camera360),并将其附加到要渲染的相机上。但是,代码将根据我们是要以单声道(360°/单声道/ 2D)还是以立体声(VR /立体声/ 3D)渲染视频而改变。



单核细胞增多症



让我们从单声道版本开始:



public class Camera360 : MonoBehaviour
{
    public Camera Camera;
    
    public RenderTexture EyeCubemap;
    public RenderTexture EquirectTexture;

    void Update ()
    {
        Camera.RenderToCubemap(EyeCubemap, 63, Camera.MonoOrStereoscopicEye.Mono);
        EyeCubemap.ConvertToEquirect(EquirectTexture, Camera.MonoOrStereoscopicEye.Mono);
    }
}


上面的脚本将以称为的渲染纹理渲染每一帧Equirect,其中将包含保存在等角矩形投影中的全景屏幕截图。



63方法中使用的值RenderToCubemap意味着我们要从多维数据集的所有六个侧面截取屏幕截图。



两者EyeCubemap,并且EquirectTexture可以在任何编辑器或代码中创建。





立体声



立体声版本代码非常相似,但是需要执行额外的步骤:



    void Start ()
    {
        Camera.stereoSeparation = 0.064f; // 64mm
    }

    void Update ()
    {
        Camera.RenderToCubemap(EyeCubemap, 63, Camera.MonoOrStereoscopicEye.Left);
        EyeCubemap.ConvertToEquirect(EquirectTexture, Camera.MonoOrStereoscopicEye.Left);

        Camera.RenderToCubemap(EyeCubemap, 63, Camera.MonoOrStereoscopicEye.Right);
        EyeCubemap.ConvertToEquirect(EquirectTexture, Camera.MonoOrStereoscopicEye.Right);
    }


要渲染立体图像,您需要渲染两个立方体贴图。stereoSeparation设置这些立方体贴图之间的距离。标准值为64毫米,大约等于左眼和右眼之间的距离。





创建和保存纹理



上面的两个代码片段仅写入纹理,但是它们没有将其保存在任何地方。如果我们希望将帧保存到磁盘,则必须手动完成。



不幸的是,将渲染纹理导出到PNG文件并不容易。第一个问题是Unity不允许您直接访问单个像素RenderTexture。首先,需要将渲染纹理复制到object Texture2D



下面的代码段使用方法,正是ReadPixels从当前活动的渲染纹理复制像素来完全执行此任务



public string FileName;

void Update ()
{
    ...    

    // Creates buffer
    Texture2D tempTexture = new Texture2D(EquirectTexture.width, Equirect.height);

    // Copies EquirectTexture into the tempTexture
    RenderTexture currentActiveRT = RenderTexture.active;
    RenderTexture.active = EquirectTexture;
    TempTexture.ReadPixels(new Rect(0, 0, EquirectTexture.width, EquirectTexture.height), 0, 0);

    // Exports to a PNG
    var bytes = tempTexture.EncodeToPNG();
    System.IO.File.WriteAllBytes(FileName, bytes);

    // Restores the active render texture
    RenderTexture.active = currentActiveRT;
}


只要它FileName包含正确的PNG文件路径,上述代码即可完成。只需做一个小更改:确保脚本不会一遍又一遍地覆盖同一文件。为此,只需将文件名计数器添加到重复重写中frame.png,他正在写入中frame_0.pngframe_1.png等等。



问题



该解决方案存在问题。其中最重要的是某些后期处理效果可能无法按预期工作。



性能



在Unity中渲染全景框架是一项非常昂贵的任务。在我的解决方案中,场景被渲染了6或12次(取决于视频格式-单声道还是立体声)。合并图像并将每个帧保存到磁盘所需的后处理会将性能降低10-15倍。如果您以高质量(8192 x 8192像素)渲染视频,则每个单独的帧的大小都可能超过100 MB。显然,如果您没有非常强大的机器,您将无法同时实时播放和导出全景照片。



后期处理



例如,渐晕会在相机周围添加深色光晕。由于Unity通过六次渲染场景来创建全景图像,因此渐晕效果将应用于每个图像。结果,在图像的交界处将出现奇怪的黑晕。





同样,模糊光晕效果也会在边缘引起错误,表明存在接缝。这个问题没有简单的解决方案,因为Unity创建的所有后处理堆栈都不适合用于全景图像。



但是,模糊和光晕效果仍然可以适度应用。如果需要这些效果,最好在使用Premiere Pro的后期制作中应用它们。



线渲染器



全景图像的一个巨大问题是线条渲染器可能无法正确显示如果设置了一条线以使其必须始终看着相机,则在以全景图像进行渲染时,Unity会简单地丢弃它。这是非常令人讨厌的,因为没有真正的原因。



Unity开发了一种行式渲染器的替代版本,称为XRLineRenderer,它可以正常工作。尽管它不支持标准Line Renderer组件的所有功能,但您将能够实现大多数功能



XRLineRenderer还具有创建简单的发光效果的能力,这可以降低上一节中描述的问题的重要性。



也可以看看:






All Articles