转载

John Carmack大神亲操刀,为Oculus开发Netflix应用

10月14日-16日,由CSDN和创新工场联合主办的 MDCC 2015中国移动开发者大会 将在北京新云南皇冠假日酒店隆重召开,现在抢注大会门票,即享多重好礼!

Hi,大家好!我是Netflix的工程副总裁Anthony Park。Netflix跟Oculus目前在合作开发一款适用于Samsung Gear VR的Netflix应用。其中的“Netflix Living Room(Netflix起居室)”能让用户随时随地借助Gear VR头盔来体验Netflix打造的温馨虚拟起居室。该应用现已上线供Oculus用户下载。

我们跟Oculus的CTO兼王牌程序员 John Carmack 密切合作,为Gear VR头盔添加了TV用户界面。其实是John完成了大部分开发!所以这次邀请他做特约博主,与大家分享新应用的开发经验。接下来就让John带着我们一睹为快吧!

John Carmack大神亲操刀,为Oculus开发Netflix应用

Gear VR上的Netflix Living Room

The Netflix Living Room

铁杆玩家也好,虚拟空间也罢,抛开抽象的专业术语,很多人只想简单来点VR(虚拟现实)观影体验。其实Samsung在开发Gear VR时,其内部就采用了“头戴式影院(Head Mounted Theater)”的定义,简称HMT。当前的VR头盔暂且不能媲美真正的高端家庭影院,但便携的优势让你随时随地把家庭影院带在身边。

上月,我们来自Oculus的员工在Netflix总部开了个会。看情形一切都很顺利,我忍不住脱口而出:“有个能干的工程师就没问题,明天开始吧!”这话似乎太乐观了点儿,好在差不多一周后,Vijay Gondi和Anthony Park从Netflix总部(加州,洛斯盖多斯市)赶到达拉斯市(德州),第二天就让UI在VR中顺利运行了,很快视频也能播放了——我们成功了!

我们的“绝地反击”是将Netflix TV代码库呈现在VR的TV屏幕上。如果一切顺利,那么Netflix代码即使在普通的2D屏幕上也能不断获取事件,绘制图像。

我开发了一个“VR 2D Shell(虚拟现实2D外壳)”应用,相当于简化版的Oculus Cinema(观影模式)应用;大屏幕用顶尖的TimeWarp图层支持(layer support)来渲染,显示内容不同,动态灯光效果也不同。凡是能用纹理渲染的东西都能放在大屏幕上。

Netflix核心应用使用用户界面层和视频解码层,这两个Android Surface须作为OpenGL纹理才能在VR中呈现出来。具体做法是:创建一个OpenGL纹理ID来初始化一个SurfaceTexture对象,再用这个SurfaceTexture对象初始化一个Surface对象,传给Netflix。

这一做法很适合UI界面——当Netflix代码使用Swapbuffers时,VR代码可帮助更新SurfaceTexture,将最新图像转为EGL外部图像,然后由GPU纹理映射到几何图像上。

视频图层就比较麻烦了。为确保回放流畅,视频帧都是标着“启动时间”提前半秒排队,而Android窗口图像合成器需要“启动时间”,好来得及挑选最好的画面。SurfaceTexture界面相当于普通的用户程序,只有一个“Update”方法来返回上传的最新图像。所以视频才比音频早半秒,而且音频卡得厉害。

要解决这个问题,得对Netflix视频解码系统做点小调整,好让它在画面提交后立刻回应我的VR代码,我就知道系统提交了新的画面以及启动时间用了多久,然后立即更新Surface texture,将它复制到我自己的帧队列中,同时保存启动时间。这样的做法缺点是太浪费内存,因为我一直在复制已在Surface上缓冲了的十多个视频帧;但优点是,我可以自己控制时间。

最开始信息输入由仿效遥控器LRUD / OK按钮的蓝牙Joypad(模拟游戏手柄程序)处理,但是否能做到仅用Touchpad来操控对Gear VR很重要。我们首选的VR界面是“一瞄一点”式的——在VR中,光标就漂浮在你眼前,只需像鼠标一样轻点即可。一般来说,这比游戏手柄好控制一点,但不如鼠标方便,需要大幅度转动头部尤其麻烦。Netflix虽然支持光标,但还做不到很多人设想的随关随开。

后来我们受到启发,做了改进。电影开始播放时,光标自动隐藏;手指滑动时不会更新光标,触发操作,因为只有当它抬起而不是按下时才能执行操作。这一方案并不完美,但也很不错了。

John Carmack大神亲操刀,为Oculus开发Netflix应用

Netflix Living Room中的Android Surfaces分层

Display(显示)

Gear VR屏幕分辨率均为2560x1440,一分为二后,两眼各占1280x1440,几乎覆盖90度的视野。之前体验过Oculus头盔的人会发现Gear VR屏幕的像素密度是DK2的两倍多,DK1的四倍多。乍一听可能觉得这视频分辨率挺不错,但真相是——没多少人希望TV屏幕覆盖90度的视野,就算是真实世界里的大屏幕通常也只占45度而已。

头盔中的光学元件会放大图像,将你的目光吸引过去。但却会引发大幅度空间失真和色差两个问题。失真状态下的像素全部挤到中间,呈从里向外延伸的趋势,而显眼的中间位置上,分辨率反而更高了。为整体内容设置一个理想的分辨率非常困难。若用mip映射调整中间的像素,就会损失外围的像素;如果调整外围的像素,那中间的像素就会被拉开。

通常移动的合成环境下,我们会将3D渲染效果图调整到适合外围的大小,两眼各占约1024x1024。在顾及整体性能的前提下,中间位置可能会有点儿模糊。在高端PC系统上,即使实际的头盔分辨率低于Gear VR,有时高分辨率场景经过渲染后,显示屏中间仍能获取最大值,不论mip映射后的大部分像素是不是混杂在一起。

Netflix用户界面的图像分辨率大概是1280x720,若渲染后变成一个覆盖60度视野的巨大虚拟TV屏幕,两眼各呈现1024x1024的缓冲图像,那么图像会变得模糊,因为损失了3/4的像素。 mip映射后的图像会是一团糟,而且当头部微微随画面变换而转动时,眼前呈现的文字也会迅速变模糊。

为了解决这个问题,我们的方法是:专门为视野中的屏幕编写一个代码,这样在必要的失真度计算完成后,屏幕可直接采集一个纹理矩形图像,跟原本的eye buffers结合在一起。这些是我们的“Time Warp图层”,虽然灵活性有限,但虚拟屏幕非常清晰(Oculus 360相册中的全景式立方地图也是)。如果Joypad绑定了手机,可直接通过启动按钮切换该功能。UI变得大不同,视频内容也大大提升。

不过这相当于在900像素的屏幕上绘制一个宽为1280像素的UI,损失是无法避免的。屏幕中间的图像因为失真而有点拉宽了。UI中的像素清晰可辨。越是外围区域,特别是角落,UI像素就混杂得越厉害。一些Netflix的UI布局不太合理;角落里的文字更难看清。

4K和全高清?还是算了吧。720p高清对如今的VR头盔来说已属最高像素了。

再来谈谈内容保护(content protection)。大多数工作室坚持高清内容需要一个安全的执行环境,将盗版风险降到最低。现代Android系统的视频编解码器可以解码成特殊的内存缓冲器,只有视频屏幕扫描硬件可以读取其中的内容;在CPU和GPU上,不受信任的软件无法在缓冲器中窃取图像。图像窃取通常发生在硬件层面,比起软件保护,这一类问题更不容忽视。

问题是,要在VR下绘制一个虚拟TV屏幕,GPU必须读取电影的surface纹理。最近应用扩展又囊括了新的机型,所以我们可以把整个GPU framebuffer移到受保护的内存中,然后读取受保护的纹理。但由于不能在其他地方编码,所以无法使用mip映射。屏幕中心可以是高分辨率,但外围会失真,而且会损失以1x1屏幕mip映射为基础的动态环境灯光效果。而且用户定时队列几乎不可能获取同步音频。

唯一合理的做法是将视频流限制为720x480的SD分辨率。要不是考虑到执行环境的安全性,我可能会选择更高分辨率。就算是这么低的分辨率,边边角角的像素还是有点混杂。

VR下的SD视频帧流程图

理想条件下,VR中的比特率/分辨率的平衡点略有不同。如果是Retina显示频,很多原本看不见的压缩伪影因为VR上像素急剧放大而无处可藏。有效分辨率有固定数值,但所有可见的压缩伪影没有固定数值的比特率。

Power Consumption(电量消耗)

对于观影应用来说,电量消耗当然比动作小游戏更重要了。我的目标是:70%的电量仍能支持一部两小时的电影。优化之后,这个目标实现了,但VR应用消耗的电量仍是标准Netflix Android应用的两倍多。

现代Android系统播放视频时,应用只是随机从网络上获取高度压缩的视频数据,继而放在硬件视频编码解码器上,待解压后存入私人缓冲区,然后由执行YUV转换的硬件composer块读取;应用直接根据显示频来调整画面大小,不用在framebuffer上再编写一个中间值。GPU甚至在电量耗光后才关闭——这太了不起了——要知道不久前,PC软件完成上述过程消耗的电量可能达到100倍以上。

除了标准应用在VR中发挥作用之外,我们还渲染了立体3D场景,每一幅画面都包含成千上万的三角形和好几兆的纹理;之后再次通过渲染校正失真的画面。

我第一次采取的最直接了当的方式,就是用UI和视频图层复合成每一帧画面。不到20分钟,手机就迅速升温到极限。整个过程中,我们不断探索简化工作流程的方法,同时力求将质量损失降到最低。

一般观影体验纯粹只是看视频而已,单单mip映射,显示一个720x480的图像就够了,不需要1280 x720的UI。Netflix代码库并不能帮助判断UI surface什么时候完全变成透明的,所以我从之前画面帧的UI组合上回读1x1单像素mip映射,然后再看alpha通道:0表示UI是完全透明的,电影surface自动形成;255表示UI是不透明的,电影surface可以忽略。介于两者之间的数值则意味着它们需要合成在一起。结果就有点出人意料——耗电量因字幕而明显增加。

最开始我将VR光标放入UI组成surface,再在VR中执行光标;UI组成能有效反映交点计算情况,但也意味着:即使UI完全静止,每一画面帧也会出现一个UI组成。把VR光标放回到它自己的3D几何图形中,只要其他条件没变,屏幕就继续使用之前的组合;浏览内容时,通常只呈现多一半的画面。

VR系统的一大特征是“异步Time Warp”,就是说,随头部移动而进行的屏幕重绘和失真校正从应用绘制的3D世界中解耦了。理想情况下,应用每秒绘制60个与Time Warp同步的立体镜头;如果应用未能传递一组新画面,Time Warp会重新使用最近用过的一个画面,根据最新的头部信息追踪对其进行再投射。如果在静态的环境下环顾四周,这种方式效果很好;但视野中的物体平稳移动时,或者你的视角在物体表面移动时,局限性就显现出来了。

视频内容是30或24帧/每秒(fps),且没有VR视点运动,所以电影回放时,我把画面更新速度限制到30帧/每秒,大大节约了电量。屏幕重绘仍然是60帧/每秒,这样回顾四周的时候,画面就不再“波涛起伏”了。灯光亮起,画面回到60帧/每秒;如果换做30帧/每秒,VR光标和滚动画面看起来会糟糕很多。

对VR环境没有那么挑剔的话,推荐体验“void theater(空白影院)”,里面除了视频屏幕,全是黑白色的,当然很省电。如果省略失真校正,一直到人脸解锁屏幕出现,其本质上消耗的电量跟普通的Netflix应用无异,不过画面会很丑,让人不舒服。

一年前,在考虑Gear VR的成功必备因素时,我列了一个小清单。其中一项便是Netflix。我们赶在了Oculus Connect开发者大会之前,在这么短的时间里就成功开发出这一应用,实在很有成就感了。现在终于可以舒舒服服卧在我的虚拟沙发上,看全季的《超胆侠》了——为了测试应用啊,这还用说?

-John

文章来源: The Netflix Tech Blog ,译者:张新慧,审校:唐小引

预告: 2015中国移动开发者大会(MDCC 2015) 将于10月14日-16日在北京新云南皇冠假日酒店召开。大会特设九大技术专场:平台与技术(iOS)、平台与技术(Android)、平台与技术(跨平台)、产品与设计、游戏开发、企业移动化、虚拟现实专场、硬件开发与技术、嵌入式开发。大会将聚集国内最具实力的产品技术团队,与开发者一道进行最前沿的探讨与交流。 

第一时间掌握最新移动开发相关信息和技术,请关注mobilehub公众微信号(ID: mobilehub)。

John Carmack大神亲操刀,为Oculus开发Netflix应用

正文到此结束
Loading...