GoogleVR for Unity--CardBoard視圖過小的解決方案

問題

google vr 的sdk現(xiàn)在還不能適配所有的機(jī)型。有一些特別的機(jī)型就會出現(xiàn)視圖過小的問題。如圖。視圖都縮小在了屏幕的下方,小小的一個。(github上的圖)

比如這樣

原因

設(shè)備沒有反饋一個正確的dpi值給sdk。沒有正確的值,sdk自然不能繪制出一個合適的視圖了。

簡練的解決方案

在unity中,找到BaseDevice腳本,有UpdateProfile()方法。里面就是定義了屏幕的各種屬性。其中,screen.width和screen.height就是屏幕的寬和高了(單位:米)。可以根據(jù)不同的屏幕做自定義。
當(dāng)然啦,這個方法里面還給很多其他的屬性賦值了,我們都可以在這里對這些屬性做操作。

 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];
       
       //重點(diǎn)在此。通過改這里的值,就可以修改視圖的大小了。注意。這里的單位是:米。
        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);
    }

好,簡練的解決方案到此為止,如果只是想解決問題的同學(xué)看到這里就夠了,可以直接滑到底部點(diǎn)個贊啦下面是我解決這個問題過程中的一些經(jīng)歷和收獲。


過程和收獲

在GvrViewerMain中,可以看到有一個Unity Editor Emulation Settings。這就unity的模擬設(shè)置。



年少無知英文不好的我,看到有手機(jī)型號設(shè)置,就直接點(diǎn)進(jìn)去GvrViewer腳本中看了。

在腳本中,很輕易地發(fā)現(xiàn),是GvrProfile這個類在控制著這些手機(jī)型號的設(shè)置。繼續(xù)點(diǎn)進(jìn)去看咯。


GvrProfile

GvrProfile這個類中,定義了所有關(guān)于屏幕的信息。所有的長度單位都是米。這會決定視圖放在手機(jī)上的哪個位置和方向。

我們看下源碼,看下到底有哪些屏幕信息

/// 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.手機(jī)的寬
  public float height;  // The short edge of the phone.手機(jī)的高
  public float border;  // Distance from bottom of the phone to the bottom edge of screen.
}

ps:視圖會盡量保持正方形,所以width和height相差太大,視圖會設(shè)配數(shù)值小的那個屬性,讓視圖顯得很小。

關(guān)于透鏡在視圖中如何放置的信息。也是以米為單位的。

/// Information about the lens placement in the viewer.  All distances are in meters.
[System.Serializable]
public struct Lenses {
  public float separation;     // Center to center.左右兩眼的視圖的中心點(diǎn)之間的距離
  public float offset;         // Offset of lens center from top or bottom of viewer.視圖中心點(diǎn)與底部的距離
  public float screenDistance; // Distance from lens center to the phone screen.視圖邊緣到視圖中心點(diǎn)的距離。說到底就是視圖的大小

  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.
}

嗯,,還有還有,關(guān)于透鏡的屬性沒認(rèn)真看這部分的代碼,所有就不翻譯了。

 /// 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編輯器中模擬運(yùn)行的時候看到效果。實(shí)際運(yùn)行的時候并不會有什么作用的。

所以我們回歸到GvrViewer。發(fā)現(xiàn)了如下代碼

用來提供數(shù)據(jù)的vr設(shè)備
 // The VR device that will be providing input data.
private static BaseVRDevice device;

我們發(fā)現(xiàn)這個BaseVRDevice中也自己存放著一個GvrProfile,估計(jì)他就是正主了。

在GvrViewer腳本中,我們發(fā)現(xiàn)他是通過

  device = BaseVRDevice.GetDevice();

來得到一個BaseVRDevice的實(shí)例的。GetDevice()方法具體的代碼如下。所以,想知道你的設(shè)備具體是使用哪個BaseVRDevice的實(shí)例?打個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;
  }

在下的設(shè)備使用的是AndroidDevices。發(fā)現(xiàn)這個類里面,沒做屏幕適配的方法喔。找他的父類GvrDevice。bingo,找到了UpdateProfile()方法。

  private void UpdateProfile() {
      //profileDate是一個底層的方法。我們通過調(diào)用這個方法,得到手機(jī)屏幕的數(shù)據(jù),然后再像自己的profile賦值,根據(jù)這個值來繪制視圖。
      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);
    }

然后怎么適配具體的屏幕,交給大家自由發(fā)揮了。

我的unity,googleVR學(xué)習(xí)總結(jié)目錄

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容