原文地址:http://blog.csdn.net/mao_xiao_feng/article/details/52599298
上篇为大家介绍了如何实现虚拟视觉,链接如下:
写给VR手游开发小白的教程:(六)Cardboard如何实现沉浸式VR体验之构造双眼
本篇分为五部分,为大家讲述虚拟视觉的引入带来的两个基本问题以及Cardboard的解决方案
一、VR设备透镜的作用
上篇已经说过,要实现FOV的外部匹配,需要使得我们双眼的视野正好覆盖手机屏幕,这意味着手机会离我们的眼睛很近,当我们长时间注视一个近距离物体时无疑会产生视觉上的疲劳,久而久之,产生近视或者是对身体更大的危害。
这是虚拟视觉带来的很显而易见的问题,如何解决?目前所有的VR设备都采用了一个方法:增加镜头。
关于VR透镜的设计涉及到了光学方面的原理,提供两篇好的文章:
http://www.elecfans.com/vr/433366_3.html
http://ivr.baidu.com/js/573d9236814e2.html
综上所述,透镜的作用大约可以概括为2个:1、防止视觉疲劳2、增大视场角(FOV)
现在多数的VR眼镜,都采用了菲涅尔透镜,它与普通的透镜其实没什么区别,只不过它将内部的材质移去,使得整个镜片更加轻薄。
二、畸变问题的产生
畸变是因为相机镜头在成像时,视场不同区域的放大率不同而产生的变形。
下图所示,畸变分为桶形畸变和枕形畸变。
桶形畸变的产生原因实际上是因为中间部分的放大率比边缘部分大,导致图像产生了向内弯曲的现象(如下图中黑色圈中的线明显比绿色圈中的线要长)。
反之枕形畸变则是中间部分的放大率比边缘部分小而导致的。
光学上可以通过改进透镜的工艺来改善畸变,比如采用非球面镜片,但是我们的VR设备的价格注定了我们观察的图像必然会发生畸变(果然是便宜没好货-.-),所以处理图像畸变也是我们面临的一个难题。
三、Unity中的扭曲矫正(RadialUndistortionEffect.cs)
Cardboard使用了RadialUndistortionEffect.cs这个脚本来做扭曲矫正:
using UnityEngine;
using System.Collections;
// Applies the inverse of the lens distortion to the image. The image is "undistorted" so
// that when viewed through the lenses (which redistort), the image looks normal. In the case
// of Cardboard, the lenses apply a pincushion distortion, so this effect applies a barrel
// distortion to counteract that.
[RequireComponent(typeof(Camera))]
public class RadialUndistortionEffect : MonoBehaviour {
#if UNITY_EDITOR
private StereoController controller;
#endif
private Material material;
void Awake() {
if (!SystemInfo.supportsRenderTextures) {
Debug.Log("Radial Undistortion disabled: render textures not supported.");
return;
}
Shader shader = Shader.Find("Cardboard/Radial Undistortion");
if (shader == null) {
Debug.Log("Radial Undistortion disabled: shader not found.");
return;
}
material = new Material(shader);
}
#if UNITY_EDITOR
void Start() {
var eye = GetComponent<CardboardEye>();
if (eye != null) {
controller = eye.Controller;
}
}
#endif
void OnRenderImage(RenderTexture source, RenderTexture dest) {
// Check if we found our shader, and that native distortion correction is OFF (except maybe in
// the editor, since native is not available here).
bool disabled = material == null || !Cardboard.SDK.UseDistortionEffect;
#if UNITY_EDITOR
bool mainCamera = controller != null && controller.GetComponent<Camera>().tag == "MainCamera";
disabled |= !mainCamera || !Cardboard.SDK.simulateDistortionCorrection;
#endif
if (disabled) {
// Pass through, no effect.
Graphics.Blit(source, dest);
} else {
// Undistort the image.
Graphics.Blit(source, dest, material);
}
}
}
来自CODE的代码片RadialUndistortionEffect.cs
Applies the inverse of the lens distortion to the image. The image is "undistorted" so that when viewed through the lenses (which redistort), the image looks normal. In the case of Cardboard, the lenses apply a pincushion distortion, so this effect applies a barrel distortion to counteract that.
应用了透镜扭曲的反效果。图像是“反扭曲”的以至于当我们透过透镜观察的时候(经过了扭曲),图像会看起来正常一些。在Cardboard里,透镜产生了枕形畸变,因此可以使用一个桶形畸变的图像效果来消除畸变的影响。
这句注释很清楚的告诉了我们如何去消除畸变,那就是产生一个已经畸变了的图像去抵消透镜产生的畸变,如果透镜产生了枕形畸变,那我们直接生成已经桶形畸变了的图像,反之也同理。
如何产生桶形畸变的图像?这主要涉及到Unity中shader的概念,shader主要用于生成一系列图像效果,称作着色器程序,它主要运行在GPU上,编写这样的代码段,需要一定的功力,也需要对Unity和其渲染机制有很高的理解。(博主没有编写过shader,没法去解释其实现了。。)
最后,通过该函数Graphics.Blit(source, dest, material);将我们的源RenderTexture经过material处理后转换成目标RenderTexture。
可以做一个实验,在运行的时候,如果将脚本RadialUndistortionEffect.cs关闭,则会出现下图的现象。
注意!!!这种方式只适用于Unity Editor,如果要在手机当中产生失真矫正效果,需要调用的是手机对应的图形处理程序。
可惜的是所有Android或者iOS的方法在Unity中只能以库的形式调用,以android为例,我们通常会在eclipse或者Android Studio中打包成类库,然后在Unity当中去做引用,基于这一点,我们并不能看到Android产生畸变的图像的具体实现过程,因为它们已经被封装起来了。
四、FOV的内部匹配问题
我们在上篇已经说过了FOV的外部匹配,通常人的视场角为60度左右,在60度的范围内我们要做到正好覆盖图像输出的viewport位置,任何图像小于视场或者大于视场的现象均会导致沉浸感降低。这是FOV的外部匹配问题。
现在我们不仅有现实中的视觉,别忘了在虚拟世界中,我们也有视觉,对应的当然也有FOV。这里所说的FOV内部匹配,就是现实视觉的FOV与虚拟视觉的FOV之间的匹配。
换言之,就是保证虚拟视觉的视场角保持在一个对于人来说舒适的范围,60度左右。
那么为什么要做匹配呢?我们完全可以在虚拟世界中获得更大或者是更窄的FOV,从实现的角度考虑,这没有问题,但是人体习惯于以60度左右的FOV观察世界,并且我们想要观察超出人体视场的物体时,会习惯性的转头,这就像是一种本能,大脑已经习惯于这样的FOV,现在当我们被迫接受不匹配的FOV时,大脑对于新的改变表现出不适,从而产生强烈的眩晕感。
从某些方面来看,FOV内部匹配甚至比外部匹配更加重要。
五、FOV内部匹配的解决方案(StereoController.cs)
StereoController这个类主要用来对双眼(Cardboard Eye)进行控制和调整,它绑定在mono Camera上,对两个子物体stereo Camera进行控制。
Controls a pair of CardboardEye objects that will render the stereo view of the camera this script is attached to.
这个脚本控制一对用来产生立体画面的摄像机物体。
这个脚本下有几个重要的属性:
/***********************************************************************************************************************************/
stereoMultiplier:
Adjusts the level of stereopsis for this stereo rig. Note that this parameter is not the virtual size of the head -- use a scale on the head game object for that. Instead, it is a control on eye vergence, or rather, how cross-eyed or not the stereo rig is. Set to 0 to turn off stereo in this rig independently of any others.
通过本控制器调整立体度等级。注意这个参数不是虚拟头部的大小--使用scale来调整头部物体,而是对眼睛离散度的控制,这个离散度也可以理解为视线怎样倾斜。设置为0的时候,关闭立体效果。
上两张图大家就能发现这个参数的功能了:
这是一张stereoMultiplier设置为1时候的图
这是stereoMultiplier设置为0时候的图
两者的差别,大家可以仔细的观察一下(必须要很仔细很仔细的观察,才能发现不同 :-D)
如果还是找不到不同,好吧,我们用两张图来表达差异:
平常,我们的眼睛观察物体,是这样的。
现在,stereoMultiplier设为0,是这样的。
第一种方式,同时考虑了视线的倾斜,而第二种方式只是将画面平视输出,很显然,如果想要更高的立体感,这个参数设为1更好。
/***********************************************************************************************************************************/
matchMonoFOV:
这个参数,就是上述的FOV内部匹配
The stereo cameras by default use the actual optical FOV of the Cardboard device,because otherwise the match between head motion and scene motion is broken, which impacts the virtual reality effect. However, in some cases it is desirable to adjust the FOV anyway, for special effects or artistic reasons. But in no case should the FOV be allowed to remain very different from the true optical FOV for very long, or users will experience discomfort.This value determines how much to match the mono camera's field of view. This is a fraction: 0 means no matching, 1 means full matching, and values in between are compromises. Reasons for not matching 100% would include preserving some VR-ness,and that due to the lens distortion the edges of the view are not as easily seen as when the phone is not in VR-mode.Another use for this variable is to preserve scene composition against differences in the optical FOV of various Cardboard models. In all cases, this value simply lets the mono camera have some control over the scene in VR mode, like it does in non-VR mode.
立体摄像机默认使用Cardboard设备实际的FOV,不然会导致头部运动和场景运动的匹配关系破裂(这会影响沉浸感),然而,在一定情况下,也可以调整FOV(为了特效或者艺术感),但是长时间使用与实际视觉FOV不同的FOV是不允许的,这会让用户不舒服。这个值决定了mono Camera获得的FOV的匹配度。0意味着不匹配,1意味着完全匹配,中间的值折中。不进行100%匹配的原因包括,保留VR的特性,VR模式下图像的边缘不易被非VR模式看到,不同类型的Cardboard进行兼容等。这个值通常让mono Camera在VR模式下对于场景有一些控制。
/***********************************************************************************************************************************/
matchByZoom:
Determines the method by which the stereo cameras' FOVs are matched to the mono camera's FOV (assuming matchMonoFOV is not 0). The default is to move the stereo cameras (matchByZoom = 0), with the option to instead do a simple camera zoom (matchByZoom = 1). In-between values yield a mix of the two behaviors.It is not recommended to use simple zooming for typical scene composition, as it conflicts with the VR need to match the user's head motion with the corresponding scene motion. This should be reserved for special effects such as when the player views the scene through a telescope or other magnifier (and thus the player knows that VR is going to be affected), or similar situations.Note that matching by moving the eyes requires that the centerOfInterest object be non-null, or there will be no effect.
决定stereo cameras的FOV和mono camera的FOV进行匹配的方式(前提是matchMonoFOV不是0)。默认是移动stereocameras(matchByZoom = 0),还有一种方式是简单的对camera的视野进行缩放(matchByZoom = 1),中间的值,表示两种方式的混合,不建议使用简单缩放,因为它和头部移动与场景移动的匹配相冲突。同样缩放效果可以被保留用作特效,比如玩家通过望远镜观看场景。注意移动匹配需要兴趣中心点物体非空,否则会无效。
/***********************************************************************************************************************************/
centerOfInterest:
Matching the mono camera's field of view in stereo by moving the eyes requires a designated "center of interest". This is either a point in space (an empty gameobject) you place in the scene as a sort of "3D cursor", or an actual scene entity which the player is likely to be focussed on.The FOV adjustment is done by moving the eyes toward or away from the COI so that it appears to have the same size on screen as it would in the mono camera. This is disabled if the COI is null.
为了移动stereo camera匹配FOV,我们需要一个被设计好的“兴趣中心点”。这可以是一个空物体,也可以是非空物体,甚至是场景中玩家可能会关注的实际物体,FOV调整根据距离兴趣中心点的距离以至于看起来与在mono camera中有同样的尺寸。
radiusOfInterest:
在兴趣中心为非空物体时,可以定义半径大小。
综上,可以看出Cardboard使用基于mono camera来调整每个stereo camera的方法,并且设置了兴趣中心点,这个点相当于我们双眼视线的中心交点,除了作为基准外,一些游戏为了吸引玩家注意也会在这个点上放置重要的物体。