GoogleVR for Unity--CardBoard视图过小的解决方案

问题

google vr 的sdk现在还不能适配所有的机型。有一些特别的机型就会出现视图过小的问题。如图。视图都缩小在了屏幕的下方,小小的一个。(github上的图)

GoogleVR for Unity--CardBoard视图过小的解决方案_第1张图片
比如这样

原因

设备没有反馈一个正确的dpi值给sdk。没有正确的值,sdk自然不能绘制出一个合适的视图了。

简练的解决方案

在unity中,找到BaseDevice脚本,有UpdateProfile()方法。里面就是定义了屏幕的各种属性。其中,screen.width和screen.height就是屏幕的宽和高了(单位:米)。可以根据不同的屏幕做自定义。
当然啦,这个方法里面还给很多其他的属性赋值了,我们都可以在这里对这些属性做操作。

 private void UpdateProfile()
    {
       GetProfile(profileData);
       GvrProfile.Viewer device = new GvrProfile.Viewer();
       GvrProfile.Screen screen = new GvrProfile.Screen();
       device.maxFOV.outer = profileData[0];
       device.maxFOV.upper = profileData[1];
       device.maxFOV.inner = profileData[2];
       device.maxFOV.lower = profileData[3];
       
       //重点在此。通过改这里的值,就可以修改视图的大小了。注意。这里的单位是:米。
        screen.width = 0.133f;
        screen.height = 0.074f;
       
        screen.border = profileData[6];
        device.lenses.separation = profileData[7];
        device.lenses.offset = profileData[8];
        device.lenses.screenDistance = profileData[9];
        device.lenses.alignment = (int)profileData[10];
        device.distortion.Coef = new [] { profileData[11], profileData[12] };
        Profile.screen = screen;
        Profile.viewer = device;

        float[] rect = new float[4];
        Profile.GetLeftEyeNoLensTanAngles(rect);
        float maxRadius = GvrProfile.GetMaxRadius(rect);
        Profile.viewer.inverse = GvrProfile.ApproximateInverse(
        Profile.viewer.distortion, maxRadius);
    }

好,简练的解决方案到此为止,如果只是想解决问题的同学看到这里就够了,可以直接滑到底部点个赞啦下面是我解决这个问题过程中的一些经历和收获。


过程和收获

在GvrViewerMain中,可以看到有一个Unity Editor Emulation Settings。这就unity的模拟设置。


GoogleVR for Unity--CardBoard视图过小的解决方案_第2张图片

年少无知英文不好的我,看到有手机型号设置,就直接点进去GvrViewer脚本中看了。

在脚本中,很轻易地发现,是GvrProfile这个类在控制着这些手机型号的设置。继续点进去看咯。


GoogleVR for Unity--CardBoard视图过小的解决方案_第3张图片
GvrProfile

GvrProfile这个类中,定义了所有关于屏幕的信息。所有的长度单位都是米。这会决定视图放在手机上的哪个位置和方向。

我们看下源码,看下到底有哪些屏幕信息

/// Information about the screen.  All distances are in meters, measured relative to how
/// the phone is expected to be seated in the viewer, i.e. landscape orientation.
[System.Serializable]
public struct Screen {

  public float width;   // The long edge of the phone.手机的宽
  public float height;  // The short edge of the phone.手机的高
  public float border;  // Distance from bottom of the phone to the bottom edge of screen.
}

ps:视图会尽量保持正方形,所以width和height相差太大,视图会设配数值小的那个属性,让视图显得很小。

关于透镜在视图中如何放置的信息。也是以米为单位的。

/// Information about the lens placement in the viewer.  All distances are in meters.
[System.Serializable]
public struct Lenses {
  public float separation;     // Center to center.左右两眼的视图的中心点之间的距离
  public float offset;         // Offset of lens center from top or bottom of viewer.视图中心点与底部的距离
  public float screenDistance; // Distance from lens center to the phone screen.视图边缘到视图中心点的距离。说到底就是视图的大小

  public int alignment;  // Determines whether lenses are placed relative to top, bottom or
                         // center.  It is actually a signum (-1, 0, +1) relating the scale of
                         // the offset's coordinates to the device coordinates.
                         //视图的align。。靠中,靠上,靠下咯,就是下面定义的三个变量。

  public const int AlignTop = -1;    // Offset is measured down from top of device.
  public const int AlignCenter = 0;  // Center alignment ignores offset, hence scale is zero.
  public const int AlignBottom = 1;  // Offset is measured up from bottom of device.
}

嗯,,还有还有,关于透镜的属性没认真看这部分的代码,所有就不翻译了。

 /// Information about the viewing angles through the lenses.  All angles in degrees, measured

/// away from the optical axis, i.e. angles are all positive.  It is assumed that left and right

/// eye FOVs are mirror images, so that both have the same inner and outer angles.  Angles do not

/// need to account for the limits due to screen size.
[System.Serializable]
public struct MaxFOV {
  public float outer;  // Towards the side of the screen.
  public float inner;  // Towards the center line of the screen.
  public float upper;  // Towards the top of the screen.
  public float lower;  // Towards the bottom of the screen.
}

/// Information on how the lens distorts light rays.  Also used for the (approximate) inverse
/// distortion.  Assumes a radially symmetric pincushion/barrel distortion model.
[System.Serializable]
public struct Distortion {
  private float[] coef;
  public float[] Coef {
    get {
      return coef;
    }
    set {
      if (value != null) {
        coef = (float[])value.Clone();
      } else {
        coef = null;
      }
    }
  }

  public float distort(float r) {
    float r2 = r * r;
    float ret = 0;
    for (int j=coef.Length-1; j>=0; j--) {
      ret = r2 * (ret + coef[j]);
    }
    return (ret + 1) * r;
  }

  public float distortInv(float radius) {
    // Secant method.
    float r0 = 0;
    float r1 = 1;
    float dr0 = radius - distort(r0);
    while (Mathf.Abs(r1 - r0) > 0.0001f) {
      float dr1 = radius - distort(r1);
      float r2 = r1 - dr1 * ((r1 - r0) / (dr1 - dr0));
      r0 = r1;
      r1 = r2;
      dr0 = dr1;
    }
    return r1;
  }
}

看完这里,大家就会觉得好面熟喔,是的,这些属性就是我们在解决方案中,在androiddevice中修改的属性。不过,在直接在这里改的话,就这可以在unity编辑器中模拟运行的时候看到效果。实际运行的时候并不会有什么作用的。

所以我们回归到GvrViewer。发现了如下代码

用来提供数据的vr设备
 // The VR device that will be providing input data.
private static BaseVRDevice device;

我们发现这个BaseVRDevice中也自己存放着一个GvrProfile,估计他就是正主了。

在GvrViewer脚本中,我们发现他是通过

  device = BaseVRDevice.GetDevice();

来得到一个BaseVRDevice的实例的。GetDevice()方法具体的代码如下。所以,想知道你的设备具体是使用哪个BaseVRDevice的实例?打个log看看呗。

  public static BaseVRDevice GetDevice() {
    if (device == null) {
#if UNITY_EDITOR
      device = new EditorDevice();
#elif ANDROID_DEVICE
  #if UNITY_HAS_GOOGLEVR
      device = new UnityVRDevice();
  #else
      device = new AndroidDevice();
  #endif  // UNITY_HAS_GOOGLEVR
#elif IPHONE_DEVICE
      device = new iOSDevice();
#else
      throw new InvalidOperationException("Unsupported device.");
#endif  // UNITY_EDITOR
    }
    return device;
  }

在下的设备使用的是AndroidDevices。发现这个类里面,没做屏幕适配的方法喔。找他的父类GvrDevice。bingo,找到了UpdateProfile()方法。

  private void UpdateProfile() {
      //profileDate是一个底层的方法。我们通过调用这个方法,得到手机屏幕的数据,然后再像自己的profile赋值,根据这个值来绘制视图。
      GetProfile(profileData);
      
      GvrProfile.Viewer device = new GvrProfile.Viewer();
      GvrProfile.Screen screen = new GvrProfile.Screen();
      device.maxFOV.outer = profileData[0];
      device.maxFOV.upper = profileData[1];
      device.maxFOV.inner = profileData[2];
      device.maxFOV.lower = profileData[3];
      screen.width = profileData[4];
      screen.height = profileData[5];
      screen.border = profileData[6];
      device.lenses.separation = profileData[7];
      device.lenses.offset = profileData[8];
      device.lenses.screenDistance = profileData[9];
      device.lenses.alignment = (int)profileData[10];
      device.distortion.Coef = new [] { profileData[11], profileData[12] };
      Profile.screen = screen;
      Profile.viewer = device;

      float[] rect = new float[4];
      Profile.GetLeftEyeNoLensTanAngles(rect);
      float maxRadius = GvrProfile.GetMaxRadius(rect);
      Profile.viewer.inverse = GvrProfile.ApproximateInverse(
          Profile.viewer.distortion, maxRadius);
    }

然后怎么适配具体的屏幕,交给大家自由发挥了。

我的unity,googleVR学习总结目录

你可能感兴趣的:(GoogleVR for Unity--CardBoard视图过小的解决方案)