VR开发的最大挑战之一是对高帧率与高分辨率结合的要求。我们通过把顶点转化为“镜头空间”,删除了需要全屏渲染的纹理,这样就可以大规模提高手机性能。

下面的技术使用谷歌的Cardboard Unity SDK进行开发,应用于Cardboard Design Lab(CDL),然而,利用透镜畸变效果,给与适当的失真系数去构成顶点着色器,并且SDK允许禁止渲染纹理,这样同样的方法可以适用于任何VR系统。

Google公司的Cardboard Design Lab,在Google Play这个场景中可能使用了唯美的处理,但实际上是非常细化。

一些背景


在审查这个技术之前,重要的是要了解大多数的VR渲染是如何处理的。对于为什么我们需要镜头畸变,以及典型实施的来历,可以查看Oculus rift documentation这一部分。

镜头畸变

VR应用的镜头畸变阻碍了头戴式显示器(HMDs)的玻璃透镜的物理畸变,从而得到一个无明显失真图像的宽视野。

在大多数VR软件中,反畸变是通过将每个照相机渲染成一个单独的渲染目标来执行,然后将后处理效果的图像进行扭曲。无论是在一个像素着色器,或者通过将渲染目标投影到一个扭曲的网格上,都将最终输出渲染到屏幕上。

本文演示了一种技术,在场景中的每个对象都在顶点着色之前执行渲染,从而消除对单独渲染目标的需求。渲染目标的消除同时几乎消除掉移动设备上的热问题,并允许在全分辨率上MSAA增加2至4倍也不会对散热或目标帧率有显着影响。

值得注意的是,我不是第一个想到这一点的人,但据我所知,Cardboard Design Lab是第一个使用这种技术开发VR应用的。此外在项目开始的时候,谷歌的团队已经做了顶点变形版本的雏形,最终成为代码是包含在今天的SDK。

所以充分证明,我会因为坚持顶点变形为基础的镜头校正能够在生产应用中工作而受到大家的好评,它对于在谷歌已经写CGine文件的一些性能算法工程师是一种喜讯。我写这篇文件是因为(A)它确实能使用,(B)希望更多的开发者使用它使VR应用变得更好;(C)引擎开发者实现正向渲染仍然很棒,MSAA仍然很可怕,依靠全屏后处理对核心功能有不良影响。

问题

在开发一个移动VR应用中,有3个主要的考虑因素影响工程成果,这些因素或多或少约束着什么是可能的。

  1. 帧率
  2. 可视化分辨率
  3. 散热

帧率

帧率是我们比较熟悉的。在整场游戏中30-60帧是普遍可以接受的。在VR中,60帧是一个最低限度,而且大家有共识是一个好的VR体验帧率要在90-120帧之间。

当然,在移动设备上,我们可以做得最好。许多设备设置最大帧率是60帧,所以我使用60作为本文的其余部分的参照。

当一个VR体验低于60帧,效果是立竿见影的。头部跟踪不出现'保持'的显示时,用户会迷失方向,并且体验更可能出现模拟器问题。瞬时性能问题(如背景资料加载、操作系统的通知和垃圾回收)引起短暂但极度的不适,而持续的低帧率(30~60)随着时间的推移可能有不适的表现。许多用户无法自主确定一个体验是运行在45帧还是60帧;然而,在我们对低帧率的测试中,用户由于各种各样的除了帧率的特征更可能感到恶心。对于这个原因,在我看来,VR体验在30帧是危害极大的,低于30帧是毫无意义的。

将一个可视化的帧时间整合显示到测试版本对于调试是必不可少的。只要确保调试本身没有以这样的方式实现,从而导致GC分配或不必要的开销就可以。上图:帧率显示使用环形缓冲区和调用原GL的单绘制。在Unity中顶点调用不要使用GUI系统!

不幸的是,目前很少有可用的移动VR应用程序可以始终在60帧运行,并且桌面应用程序有严格的硬件要求。这些副作用导致VR的最佳做法过于保守。我看过不少文章说“VR用户的移动总是不好,而运动的某些类型如向视图方向的运动是有问题,设计良好的60帧跑步运动可以给绝大多数用户带来愉悦感。由于过山车的普及,VR应用程序可以足够被证明,运动是值得研究的。

关键点:低于60帧率可以让一个很好的体验变的令人作呕,甚至体验用户和开发者可能无法正确识别原因。

可视化分辨率

可视化分辨率是指如何让一个场景细节“可视”。在VR,这是一个物理屏幕分辨率、渲染缓冲区的分辨率、MSAA水平、物理镜头放大和各种后期处理效果(如透镜畸变)的组合。

我们需要多少分辨率?我读过很多不同的数字,但我对事物使用Michael Abrash近似,如每只眼4K×4K(90度视野)与当前的显示器相配,和16K×16K(180度视野)与视网膜分辨率相配。

显然,没有移动设备是可以做到,但是如果忽略一些,仍然有做到的。三星Galaxy S6推出每只眼2560×1440像素或1280×1440像素。在边缘进行一些损失计算,这大约是1024×1024或1K(是4K的16分之一)。

现在我敢肯定,Michael Abrash给的那些数字就是意味着4K×4K的MSAA打开,产生一个糟糕的16K可视分辨率,但原理是相同的,MSAA会帮助你。

关键点:抗混叠是非常重要的,即使昂贵。我喜欢在高端手机追求4x的MSAA。打破你的正向渲染吧。

简而言之,我知道推迟渲染的人有各种各样的后处理图像保真技术。就我个人而言,我认为,移动到后处理图像保真使一代游戏看起来很糟糕,但回到主题,这篇文章的重点是从移动传输中删除全屏渲染纹理,虽然他们是那么昂贵。本来的目的是后处理AA需要一个全屏渲染纹理。在Unity,正向渲染是非常快的。我最后一次检查UE4时并没有正向渲染模式,更糟糕的是,当我问及CG水平材质定制时我被讽刺的告知“你有源”–仿佛重新编译引擎对于垂死之人是一个可行的开发策略。这就是说,我对于如果史人正在为它努力也不会感到惊讶。

散热

散热是指设备的热输出。作为游戏开发者,我熟知在移动上的几个常见的性能瓶颈,以频率为顺序依次为:GPU填充率、CPU(通常绘制调用),CPU-GPU内存带宽,GPU的顶点处和糟糕的特定设备的驱动程序问题,就像无穷无尽的GPU处理。

大部分时间这些都是不相关的问题。即使是在CPU充分利用的情况下,开发者仍然可以继续添加对CPU的使用。然而,手机是小的,处理器大多是在同一个地方,这意味着热是一个全程问题。

为了保持手机远离融化,或爆炸,或融化和爆炸,当手机产生太多的热量时将减少处理器的权限,放缓一切。当你想到硬件工程参与后可以很可靠的做到,但帧率不可以,这就让人很惊讶。热调节不是设计用来平衡性能和热的,它的目的是为了节省你的手机。当它第一次开始,一个很好流畅的60帧通常会下降到40帧以下。当它实现了以后手机仍然是热的,它在相比较大的增量上减少权力。如果你的VR应用是散热有限的,这将不是一个很好的体验。

使热非常具有挑战性的原因是因为很难知道是什么是造成热,突然一切都是相关的。如果你可以减少每个函数调用的汇编指令,也许你可以假定每个指令的一些恒定的热因子,但这没有考虑到你的硬件效率或设备内部的物理结构,或应用程序的控制以外的因素,如后台进程,或者wifi等等。

为什么这对于VR是独特的?其他一些移动应用要求高分辨率3D渲染一个恒定的60帧4x MSAA–除此之外,VR应用渲染场景两次(每只眼一次),大多数SDK默认渲染一个高分辨率渲染纹理进行畸变。事实证明,渲染纹理在性能和产生的热量方面是非常费钱的。

在Gear VR发布的时候,约翰·卡马克提到了相关散热的问题。不幸的是,这不是一个可以很快消失的问题。

关键点:如果你的性能好几分钟,然后就莫名其妙地暴跌(以至于你的手机烫手),请检查日志中的热警告。

顶点位移镜头校正

当项目开始时,Cardboard Unity SDK的默认渲染方式是渲染成一个16位的渲染纹理不用MSAA。即使在这些设置下,许多低端设备在几分钟内就遇到了热限制。GearVR使用了一个类似的管道,也遇到了相同的局限性。无论使用什么SDK,在手机上绘制全分辨率渲染纹理是会占用性能的。

切换到顶点位移VR后,CDL渲染成32位全屏缓冲区(减少色带)–根据设备利用2x-4x MSAA,并且一般维持60帧。

此外,通过对“镜头空间”场景畸变,我们避免了变形的渲染对象的分辨率损失(或通过渲染一个更高分辨率的缓冲区而其相应渲染时间的浪费)。这是一种有效的像Nvidia公司最近发布的多分辨率的阴影,但它是免费的,并且它可以在一个6年的智能手机上运行。

上图:使用标准的渲染纹理技术渲染的121顶点平面。

下图:121顶点平面采用顶点位移,2x MSAA,没有渲染纹理。UV图像来自

http://www.pixelcg.com/blog/?p=146

从上面两个渲染中心进行双线性放大。

左:标准渲染纹理技术

右:顶点位移

如何使用

目前Unity的Cardboard SDK中有一个CG,其中包含一个名为CardboardDistortion.cginc文件。它包含了一个方法,这个方法用Brown–Conrady模型进行径向畸变校正将一个世界空间的顶点转换为反镜头畸变屏幕空间(‘镜头空间’)。

使用这个功能,看起来像是一个简单的固体颜色材质。请注意,我已经禁用了在桌面和在编辑器中使用条件编译的效果。在编辑器中执行反镜头畸变,使编辑的场景相当具有挑战性。

CGPROGRAM
#pragma vertex VertexProgram
#pragma fragment FragmentProgram
#include "CardboardDistortion.cginc"

struct VertexInput {
float4 position : POSITION;
};

struct VertexToFragment {
half4 position : SV_POSITION;
};

VertexToFragment VertexProgram (VertexInput vertex){
VertexToFragment output;

#if SHADER_API_MOBILE

///easy as that.
output.position = undistortVertex(vertex.position);
#else

///this is how a standard shader works
output.position = mul(UNITY_MATRIX_MVP, vertex.position);
#endif

return output;
};

fixed4 _Color;
fixed4 FragmentProgram (VertexToFragment fragment) : COLOR{
return _Color;
}
ENDCG

要求

这看起来很容易,你可能会想知道为什么每个人不在每个平台上这样做。为什么有人为渲染纹理而感到烦恼?为什么我们不把所有的东西都放在“镜头空间”。

镜头畸变是非线性的,因此,反透镜畸变也是非线性的。当我们对顶点着色处的顶点变形时,它们之间的线仍然是一条线,事实上,它应该是一个弯弧,当你通过一个弯曲的镜头看一条弧线时,它看起来像是一条线。当你通过一个弯曲的镜头看一条直线时,你会看到一个立方体与弯曲的边缘,整体的但可能不是你想要的。

上图:121个顶点的平面。下图:4个顶点的四边形。注意:非线性畸变是与平面被保留,但不与四边形。

这样做的结果是,如果顶点间隔相距甚远(在屏幕空间),我们将结束校正太多畸变,当我们转动我们的头时最终图像将显示为'经',这就毫不奇怪用户有愤怒的倾向了。

也许更糟的是,所有的顶点运动将增大对象之间的深度冲突问题。

幸运的是,解决方案很简单,我们需要添加足够的顶点镶嵌,这样失真将不再明显。事实证明,大多数高端移动设备能够在每帧的200000和800000个顶点之间渲染。不幸的是,我们仍然需要渲染场景两次,所以我们的顶点限制在100000和400000之间的顶点,此外,还取决于你想支持的设备。

你可能会认为,添加所有这些顶点会再次引起我们的热故障,但现代手机有非常密集的屏幕,1920×1080是相当普遍的。1920×1080约有2百万像素只有少量的透支,我们可以执行每帧1千万个着色器片段。如果我们需要渲染成一个单独的渲染目标为畸变校正,它增加了另外的2百万像素,加上上下文切换不可忽略不计的成本和大量的纹理内存访问。

一个好成果

因为我们必须大量增加场景的顶点数,我们可以利用这一点。我们可以在顶点程序中运行40万次计算照明,而不是在片段程序中运行约1000万次计算照明,这是计算的25倍降低(热切希望)。由于我们的顶点需要足够密集的非线性透镜畸变,照明也可能足够密集(这一般也是非线性)。

就像非线性畸变,顶点照明质量在很大程度上取决于顶点计数。左:使用顶点照明的4个顶点的四分之一是不能从点光源显示太多细节;右:一个具有121个顶点的平面是距离的一个合适近似。

这里请注意:我永远不会使用任何Unity的着色。我从不使用他们的天空盒。我只使用他们的照明引擎原型。在CDL的每个着色器编写在CG/ ShaderLab(不使用表面着色器),和片段程序或从顶点着色器中读取颜色值并且显示它,或读两颜色值,访问纹理,通过一种颜色增加纹理并添加另外一种颜色。保持片段程序非常简单,除去渲染纹理,减少透支使GPU多检查。

UI和2D

还有一个用来实现一个顶点位移管道,那就是处理二维元素,如用户界面。

既然我们在VR,UI已经是一个棘手的问题。为了维持正常的立体融合,UI需要在3D空间渲染,最好引用同一深度的3D对象(如网线需要对遮挡对象进行3D深度渲染)。

UI需要未失真和确保有足够数量的顶点,以及有意义的绘制UI元素,例如字母和纹理上镶嵌的是平面而不是四边形。镶嵌的确切数额将取决于屏幕上的UI元素的最大尺寸。

当然,任何一个被锁定的UI都会变形,但变形不会随着用户的头部旋转而改变,所以这种效果可能不太明显。

至少一般VR的接口是最好的处理背景,所以希望利用顶点位移镜头校正不会出现大的障碍。

优化

顶点位移的最小误差近似是在屏幕空间的顶点之间的距离的最小化,即为对象进一步远离相机,他们不需要太多的镶嵌,因此标准LoD技术是有用的。

这为优化提供了一个明确的机会,特别是对固定位置渲染。假如相机可以在一个场景中移动的情况下,优化变得更具挑战性。

最后的想法

坦率地说,我仍然感到震惊,这不是一个被广泛使用有极高硬件要求的发布桌面HMDs技术。基于镜头校正的顶点位移使智能手机渲染VR使 QHD(2560×1440)显示为60 帧高达4x MSAA。大多数现代的集成显卡至少应该能够集合多性能,而低到中等范围的专用显卡可以做得更好。我不希望在我的英特尔HD 5000上玩前线VR,但我不相信HMD硬件当前作品可以很容易的得到一个更广泛的受众,而不需要SLI的支持。

关于Brian Kehrer

Brian是一个独立的游戏开发人员和图形程序员。他是Cardboard Design Lab的ustwo(奇幻冒险游戏)首席开发员,在那里他花了很多时间研究着色器。

利用顶点位移进行VR畸变校正的更多相关文章

  1. [翻译]利用顶点位移的VR畸变校正

    文章英文原网址: http://www.gamasutra.com/blogs/BrianKehrer/20160125/264161/VR_Distortion_Correction_using_V ...

  2. 车牌定位与畸变校正(python3.7,opencv4.0)

    一.前言及思路简析 目前车牌识别系统在各小区门口随处可见,识别效果貌似都还可以.查阅资料后,发现整个过程又可以细化为车牌定位.畸变校正.车牌分割和内容识别四部分.本篇随笔主要介绍车牌定位及畸变校正两部 ...

  3. OpenCV畸变校正源代码分析

    图像算法中会经常用到摄像机的畸变校正,有必要总结分析OpenCV中畸变校正方法,其中包过普通针孔相机模型和鱼眼相机模型fisheye两种畸变校正方法. 普通相机模型畸变校正函数针对OpenCV中的cv ...

  4. OpenCV两种畸变校正模型源代码分析以及CUDA实现

    图像算法中会经常用到摄像机的畸变校正,有必要总结分析OpenCV中畸变校正方法,其中包括普通针孔相机模型和鱼眼相机模型fisheye两种畸变校正方法. 普通相机模型畸变校正函数针对OpenCV中的cv ...

  5. OpenCV畸变校正原理以及损失有效像素原理分析

    上一篇博客简要介绍了一下常用的张正友标定法的流程,其中获取了摄像机的内参矩阵K,和畸变系数D. 1.在普通相机cv模型中,畸变系数主要有下面几个:(k1; k2; p1; p2[; k3[; k4; ...

  6. 翻译:GLSL的顶点位移贴图

    翻译:GLSL的顶点位移贴图 翻译自: Vertex Displacement Mapping using GLSL 译者: FreeBlues 说明: 之所以选择这篇文档, 是因为现在但凡提到位移贴 ...

  7. 【OpenCV】摄像机标定+畸变校正

      摄像机标定 本文目的在于记录如何使用MATLAB做摄像机标定,并通过OpenCV进行校正后的显示. 首先关于校正的基本知识通过OpenCV官网的介绍即可简单了解: http://docs.open ...

  8. Matlab 摄像机标定+畸变校正

    博客转载自:http://blog.csdn.net/Loser__Wang/article/details/51811347 本文目的在于记录如何使用MATLAB做摄像机标定,并通过opencv进行 ...

  9. 利用matlab摄像机标定

    (1)输入图像 "Image names"键 Matlab的图形窗口显示出20幅靶标图像 (2) 提取角点 "Extract grid corners"键. 输 ...

随机推荐

  1. vue的Vuex

    网上也很多文章,但解释起来的确玄乎,小白们很难理解到位. 自问文笔没大神们好只是自己了解了掌握了Vuex用法以及主要思路 但要我解释起来也只能参考大神们的说法 Vuex就是一个全局变量,而这个全局变量 ...

  2. js对象按某个字段排序

    var arr = [ {name:'zopp',age:0}, {name:'gpp',age:18}, {name:'yjj',age:8} ]; function compare(propert ...

  3. ES6之Promise的基本用法

    之前多次看过阮一峰的ES6教程,对Promise也简单的理解过,但是,由于没在项目中运用过,所以记忆的并不深刻,昨天在进行项目的改良,有一个地方需要用到Promise 所以就这样写了: onload函 ...

  4. mybatis框架(4)---输入输出映射

    输入输出映射 通过parameterType制定输入参数类型 类型可以是简单类型(int String)也可以是POJO本身 或者包装类 1输入映射 关于输入简单类型和pojo本身的我就不写了,因为比 ...

  5. 在vue中使用Autoprefixed

    为了使我们的项目兼容各种浏览器,我们可能会在开发中写大量的前缀.即使有了IDE为我们提供了便捷的方式.但是仍然需要我们去花时间和精力.而这样会浪费我们很多的时间.为了在开发中提升团队的开发效率,并且同 ...

  6. TCP/IP 笔记 - 安全

    安全:可扩展身份认证协议.IP安全协议.传输层安全.DNS安全.域名密钥识别邮件 任何由用户或以用户账号执行却违背了用户本身意愿的软件被称为恶意软件 网络安全是一门十分广泛及有深度的学识,而本书旨在了 ...

  7. 一个用于分页的page类

    今天周一,趁工作轻松,自己就写了一个基于MySQl数据库的分页查询,做分页,最主要的是以下几点: 一:写sql语句:比如查询某张数据表的数据,sql语句为:select * from table li ...

  8. javaweb之Filter过滤器详解

    快速入门 1.新建一个类,实现Filter接口 2.实现doFilter()方法,打印一句话,来证明能够进行拦截 3.在web.xml中进行配置(参照Servlet配置) 4.访问一个页面,看看能不能 ...

  9. Apache-Flink深度解析-TableAPI

    您可能感兴趣的文章合集: Flink入门 Flink DataSet&DataSteam API Flink集群部署 Flink重启策略 Flink分布式缓存 Flink重启策略 Flink中 ...

  10. Python数据可视化的四种简易方法

    摘要: 本文讲述了热图.二维密度图.蜘蛛图.树形图这四种Python数据可视化方法. 数据可视化是任何数据科学或机器学习项目的一个重要组成部分.人们常常会从探索数据分析(EDA)开始,来深入了解数据, ...