二、制作VR全景图播放器 (Google VR for Android)

原文地址:
http://blog.csdn.net/qq_24889075/article/details/52128463
http://www.jianshu.com/p/104251a3153d

相关阅读:
一、初识GVR (Google VR for Android):http://www.jianshu.com/p/09c0822b9d1e
三、制作VR视频播放器 (Google VR for Android):http://www.jianshu.com/p/82163453ed30

这篇看下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(192​​0×1080)。最大的兼容性和质量是头等大事,Google建议用户同时提供平面视觉1920x1080的视频和2048×2048处以上的立体视频。  

3、如何录制VR视频

生活中拍摄:

360度拍摄的照片和视频越来越方便和实惠。 VR视图可以使用由支持上述equirect-全景格式的任何摄像机产生的图像。对于有兴趣在快速入门用户来说,我们最喜欢的解决方案如下:

Cardboard Camera App:这个免费的Andr​​oid应用程序,允许用户快速捕捉立体图像360。

Ricoh Theta:一个非常流行的,用于捕获单360度的图像和视频相对廉价的解决方案。

CG(计算机动画)拍摄:

遥感影像数据的VR观点并没有从现实世界限于捕获。 CGI软件可以生成360图像和视频,一切从建筑到演练预演的电影。我们的一些最流行的捕获解决方案的列举如下:

360 Panorama Capture for Unity:一个免费的,易于使用的360捕获了Unity插件。

Unreal(虚幻):UE4的最新版本内置了360捕获解决方案。

Domemaster3D for Maya :从玛雅捕获单声道和立体声图像360免费的解决方案。

Renderman:开源库,用于捕捉360的内容。

Rendering Omnidirectional Stereo Content:一个有兴趣的人都在书写自己360捕获解决方案白皮书。

Android平台

这里官方有这Android平台的详细介绍,主要内容如下:

有这表明在官方SDK中的VR View 功能的两个示例应用程序:simplepanowidgetsimplevideowidget。这两个样品的是显示分别使用VrPanoramaViewVrVideoView嵌入全景图像和视频。

这里写图片描述

允许用户通过旋转他们的电话,看全景的不同部分。
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

正题来了~
看下我为了本次学习更改过的代码:
不了解Pair的请看这里: http://blog.csdn.net/qq_24889075/article/details/52127398


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做一款全景图的显示器(播放器?查看器?... 不知道叫什么合适):

  1. 导入google的库
  2. 在相应的布局文件中引入控件 com.google.vr.sdk.widgets.pano.VrPanoramaView
  3. 初始化控件
  4. 为VrPanoramaView设置options
  5. 找到图片的Bitmap
  6. 调用VrPanoramaView的loadImageFromBitmap方法
  7. 在onPause、onResume、onDestroy中做出相应处理

原文地址:
http://blog.csdn.net/qq_24889075/article/details/52128463
http://www.jianshu.com/p/104251a3153d

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,723评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,485评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,998评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,323评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,355评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,079评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,389评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,019评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,519评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,971评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,100评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,738评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,293评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,289评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,517评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,547评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,834评论 2 345

推荐阅读更多精彩内容