二、VR全景圖顯示器開發 ---- Android VR視訊/Google VR for Android /VR Pano/VR Video
這篇看下SimpleVrPanorama這個栗子
SimpleVrPanorama
其實這篇應該寫SimpleVrPanorama和simplevideowidget 兩個,但是由於篇幅過長就分開寫了
演示
用AS錄的沒有觸控點顯示,先湊合看吧
介紹
官方在這裡介紹了VR view 、支援平臺等。我挑幾個相對重要的介紹一下:
1、影象規格
VR檢視影象可以儲存為PNG,JPEG或GIF。Google建議使用JPEG改進壓縮。 為了獲得最大的相容性和效能,影象尺寸應該是2的倍數(例如,2048或4096)。 單個影象應為2:1縱橫比(例如4096×2048)。 立體影象應為1:1縱橫比(例如4096×4096)。
如圖:
2、 視訊規格
VR view視訊應該被儲存為H264編碼的mp4檔案。
單個視訊應是2:1縱橫比。
立體視訊應是1:1縱橫比。
一些較舊的裝置不能解碼的視訊最大不能超過超過1080(1920×1080)。最大的相容性和質量是頭等大事,Google建議使用者同時提供平面視覺1920x1080的視訊和2048×2048處以上的立體視訊。
3、如何錄製VR視訊
生活中拍攝:
360度拍攝的照片和視訊越來越方便和實惠。 VR檢視可以使用由支援上述equirect-全景格式的任何攝像機產生的影象。對於有興趣在快速入門使用者來說,我們最喜歡的解決方案如下:
Ricoh Theta:一個非常流行的,用於捕獲單360度的影象和視訊相對廉價的解決方案。
CG(計算機動畫)拍攝:
遙感影像資料的VR觀點並沒有從現實世界限於捕獲。 CGI軟體可以生成360影象和視訊,一切從建築到演練預演的電影。我們的一些最流行的捕獲解決方案的列舉如下:
Unreal(虛幻):UE4的最新版本內建了360捕獲解決方案。
Renderman:開源庫,用於捕捉360的內容。
Android平臺
在這裡官方有這Android平臺的詳細介紹,主要內容如下:
有這表明在官方SDK中的VR View 功能的兩個示例應用程式:simplepanowidget
允許使用者通過旋轉他們的電話,看全景的不同部分。
simplevideowidget示例還允許使用者暫停(點選 VR View就暫停了。 VR View也就是視訊那個區域),可以使用進度條改變進度。允許使用者更改模式,分別是全屏模式和紙板模式。
全屏模式:
紙板模式:
程式碼分析
(^_^ 為了方便學習與理解,基於官方Demo的程式碼進行了修改 )
前言
這個栗子中需要注意幾個知識點:
VrPanoramaView //Google提供給我們現實全景圖片的View
Options //VrPanoramaView 所需的設定
VrPanoramaEventListener//為 VrPanoramaView 設定監聽
loadImageFromBitmap//載入圖片的主要方法
AndroidManifest
<!--Demo需要的兩個許可權-->
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<application android:label="SimpleVrPanoramaActivity"
android:largeHeap="true"
android:theme="@android:style/Theme.Holo.Light">
<!-- 這裡原本有個 launchMode ,方便閱讀 把這個和Activity中相關程式碼(onNewIntent) 刪掉了 後面部介-->
<activity android:name=".SimpleVrPanoramaActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
<category android:name="com.google.intent.category.CARDBOARD" />
</intent-filter>
</activity>
</application>
build.gradle
dependencies {
compile project(':libraries-common')
compile project(':libraries-commonwidget')
compile project(':libraries-panowidget')
}
佈局檔案
只有一個主要標籤
<com.google.vr.sdk.widgets.pano.VrPanoramaView
android:id="@+id/pano_view"
android:layout_width="match_parent"
android:layout_height="250dip"
android:layout_margin="5dip"
android:scrollbars="@null"/>
SimpleVrPanoramaActivity
package com.google.vr.sdk.samples.simplepanowidget;
import android.app.Activity;
import android.graphics.BitmapFactory;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
import android.util.Pair;
import com.google.vr.sdk.widgets.pano.VrPanoramaEventListener;
import com.google.vr.sdk.widgets.pano.VrPanoramaView;
import com.google.vr.sdk.widgets.pano.VrPanoramaView.Options;
import java.io.IOException;
import java.io.InputStream;
public class SimpleVrPanoramaActivity extends Activity {
private static final String TAG = "VrPanorama";
private VrPanoramaView panoWidgetView;//上面說的Google提供給我們現實全景圖片的View
private String fileUri = "first.jpg";//assets資料夾下的檔名
private Options panoOptions = new Options();//VrPanoramaView需要的設定
private ImageLoaderTask backgroundImageLoaderTask;//非同步載入圖片
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main_layout);//佈局上面貼了
panoWidgetView = (VrPanoramaView) findViewById(R.id.pano_view);//初始化VrPanoramaView
panoWidgetView.setEventListener(new ActivityEventListener());//為VrPanoramaView新增監聽
//如果有任務在執行則停止它
if (backgroundImageLoaderTask != null) {
backgroundImageLoaderTask.cancel(true);
}
//設定inputType 為TYPE_STEREO_OVER_UNDER. 在後面會介紹TYPE_STEREO_OVER_UNDER的,暫時當做一個圖片的顯示型別就行
panoOptions.inputType = Options.TYPE_STEREO_OVER_UNDER;
//建立一個任務
backgroundImageLoaderTask = new ImageLoaderTask();
//執行任務。將圖片名(根據專案實際情況傳吧)和設定傳入
backgroundImageLoaderTask.execute(Pair.create(fileUri, panoOptions));
}
//非同步任務
class ImageLoaderTask extends AsyncTask<Pair<String, Options>, Void, Boolean> {
@Override
protected Boolean doInBackground(Pair<String, Options>... fileInformation) {//真正寫專案根據情況新增條件判斷吧
InputStream istr = null;
try {
istr = getAssets().open(fileInformation[0].first);//獲取圖片的輸入流
} catch (IOException e) {
Log.e(TAG, "Could not decode default bitmap: " + e);
return false;
}
Bitmap bitmap = BitmapFactory.decodeStream(istr);//建立bitmap
panoWidgetView.loadImageFromBitmap(bitmap, fileInformation[0].second);//引數一為圖片的bitmap,引數二為 VrPanoramaView 所需要的設定
try {
istr.close();//關閉InputStream
} catch (IOException e) {
Log.e(TAG, "Could not close input stream: " + e);
}
return true;
}
}
private class ActivityEventListener extends VrPanoramaEventListener {
@Override
public void onLoadSuccess() {//圖片載入成功
Log.e(TAG, "onLoadSuccess");
}
@Override
public void onLoadError(String errorMessage) {//圖片載入失敗
Log.e(TAG, "Error loading pano: " + errorMessage);
}
@Override
public void onClick() {//當我們點選了VrPanoramaView 時候出發
super.onClick();
Log.e(TAG, "onClick");
}
@Override
public void onDisplayModeChanged(int newDisplayMode) {//改變顯示模式時候出發(全屏模式和紙板模式)
super.onDisplayModeChanged(newDisplayMode);
Log.e(TAG, "onDisplayModeChanged");
}
}
@Override
protected void onPause() {
panoWidgetView.pauseRendering();//暫停3D渲染和跟蹤
super.onPause();
}
@Override
protected void onResume() {
super.onResume();
panoWidgetView.resumeRendering();//恢復3D渲染和跟蹤
}
@Override
protected void onDestroy() {
panoWidgetView.shutdown();//關閉渲染下並釋放相關的記憶體
if (backgroundImageLoaderTask != null) {
backgroundImageLoaderTask.cancel(true);//停止非同步任務
}
super.onDestroy();
}
}
看完了有木有感覺炒雞簡單啊?現在你已經掌握瞭如何使用 VrPanoramaView 了吧。
用 VrPanoramaView 的確簡單,但是侷限性特別大,後面有機會 會詳細說的。
再介紹下程式碼中沒提到的兩個方法:
setFullscreenButtonEnabled (false); //隱藏全屏模式按鈕
setVrModeButtonEnabled(false); //隱藏VR模式按鈕
Options
接下來看看剛剛的VrPanoramaView.Options吧,上文中 是這麼設定的
panoOptions.inputType = Options.TYPE_STEREO_OVER_UNDER;
那麼為什麼要這樣設定呢?先看官方對Options中標籤的介紹:
public static final int TYPE_MONO = 1;
影象被預期以覆蓋沿著其水平軸360度,而垂直範圍是根據影象的寬高比來計算。例如,如果一個1000x250畫素的影象,給出所述全景將覆蓋360x90度與垂直範圍是-45至+45度。
public static final int TYPE_STEREO_OVER_UNDER = 2;
包含兩個大小相等的投影 全景圖垂直疊加。頂部影象被顯示給左眼、底部影象被顯示給右眼。//看下圖你就懂了
影象將覆蓋沿水平軸360度,而垂直範圍是根據影象的寬高比來計算。例如,如果一個1000x500畫素的影象中給出(即1000x250畫素每個眼睛),全景將覆蓋360x90度與垂直範圍是-45至+45度。
我要顯示的圖片是下圖這樣的,所以就要設定為 ‘TYPE_STEREO_OVER_UNDER’
那麼什麼樣的圖片設定為 ‘TYPE_MONO’ 呢?
請看:
不知道有沒有眼神好的同學發現這個問題:TYPE_STEREO_OVER_UNDER型別的圖片每次切換模式時候 圖片中間都會有一條垂直於水平線的分割線(很淺 很淺 然後逐漸消失),TYPE_MONO 就沒有 ^_^
Options類中的程式碼也十分簡單
public static class Options {
private static final int TYPE_START_MARKER = 0;//起始標記
public static final int TYPE_MONO = 1;
public static final int TYPE_STEREO_OVER_UNDER = 2;
private static final int TYPE_END_MARKER = 3;//結束標記
public int inputType = 1;//預設為一
public Options() {
}
void validate() {
if(this.inputType <= 0 || this.inputType >= 3) {//標記錯誤處理
String var10000 = VrPanoramaView.TAG;
int var1 = this.inputType;
Log.e(var10000, (new StringBuilder(38)).append("Invalid Options.inputType: ").append(var1).toString());
this.inputType = 1;
}
}
}
調皮的你如果在loadImageFromBitmap(bitmap,options)方法中 將options不小心設定為null了,也沒關係。我在原始碼中我發現下面的程式碼,感覺挺溫馨的
public void loadImageFromBitmap(Bitmap bitmap, VrPanoramaView.Options options) {
//有木有那裡暖暖的 ^_^
if(options == null) {
options = new VrPanoramaView.Options();
} else {
options.validate();
}
//重點不在這裡 可以無視它
this.renderer.loadImageFromBitmap(bitmap, options, this.eventListener);
}
至此com.google.vr.sdk.widgets.common包、com.google.vr.sdk.widgets.pano包和com.google.vr.sdk.widgets.video包(程式碼下一篇介紹) 的主要內容都介紹完了,總結下吧
總結
總結下如何在Android裝置上用Google的SDK做一款全景圖的顯示器(播放器?檢視器?… 不知道叫什麼合適):
- 匯入google的庫
- 在相應的佈局檔案中引入控制元件 com.google.vr.sdk.widgets.pano.VrPanoramaView
- 初始化控制元件
- 為VrPanoramaView設定options
- 找到圖片的Bitmap
- 呼叫VrPanoramaView的loadImageFromBitmap方法
- 在onPause、onResume、onDestroy中做出相應處理