【Android App】实战项目之虚拟现实(VR)的全景相册(附源码和演示视频 可用于学习和大作业)

news2025/1/12 9:52:50

需要源码请点赞关注收藏后评论区留言私信~~~

不管是绘画还是摄影,都是把三维的物体投影到平面上,其实仍旧呈现二维的模拟画面。 随着科技的发展,传统的成像手段越来越凸显出局限性,缘由在于人们需要一种更逼真更接近现实的技术,从而更好地显示三维世界的环境信息,这便催生了增强现实AR和虚拟现实VR。 传统的摄影只能拍摄90度左右的风景,而新型的全景相机能够拍摄360度乃至720度(连同头顶和脚底在内)的场景,这种360/720度的相片即为全景照片。

一、需求描述

传统的照片只是某个视角观测到的平面截图,无法看到视角以外的场景。 现在有了VR技术,只要把全景相机拍摄的全景照片发到手机上,无论在哪里都能及时通过手机浏览全景照片,从而方便掌握最新的现场情况。 全景照片看似一张矩形图片,其实前后左右上下的景色全都囊括在内,但用户每次只能观看某个角度的截图。要想观看其它方向上的图画,就得想办法让全景照片转起来。

二、功能分析

全景照片之于观察者,便是将全景图贴在球体内侧,由此可见,二者的不同之处在于人是在球外还是球内

 浏览全景照片用到的技术

(1)通过手势的触摸与滑动,把全景照片相应地挪动观测角度。

(2)利用OpenGL ES 库,把平面的全景照片转换为曲面的实景快照。

(3)根据手势滑动在水平方向和垂直方向分别产生的角度变化,实时调整全景图的快照范围。

下面介绍主要代码模块之间的关系

(1)PanoramaActivity.java:这是全景相册的浏览器页面。

(2)PanoramaView.java:这是自定义全景视图的实现代码。

(3)PanoramaRender.java:这是全景图形的三维渲染器代码。

(4)PanoramaUtil.java:这是全景图片的顶点坐标工具类,用于计算全景图片的顶点列表,以及全景图片的纹理列表。 

具体到编码过程,主要有以下三项处理

1:实现全景照片的渲染器

2:计算手势触摸引发的角度变更

3:在活动页面加载全景照片的渲染器

三、效果展示 

演示视频如下 可以在下拉框中选择不同的相册进行展示,可以放缩或旋转 真正实现了身临其境的感觉

虚拟现实的全景相册

效果图如下 

 

 四、代码

部分代码如下 全部源码请点赞关注收藏后评论区留言~

package com.example.threed;

import android.os.Bundle;
import android.view.View;
import android.view.WindowManager;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Spinner;

import androidx.appcompat.app.AppCompatActivity;

import com.example.threed.panorama.PanoramaView;

public class PanoramaActivity extends AppCompatActivity {
    private final static String TAG = "PanoramaActivity";
    private PanoramaView pv_content; // 声明一个全景视图对象

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_panorama);
        getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); // 保持屏幕常亮
        pv_content = findViewById(R.id.pv_content);
        pv_content.initRender(resArray[0]); // 设置全景视图的全景图片
        initExampleSpinner(); // 初始化样例下拉框
    }

    // 初始化样例下拉框
    private void initExampleSpinner() {
        ArrayAdapter<String> exampleAdapter = new ArrayAdapter<>(this,
                R.layout.item_select, exampleArray);
        Spinner sp_example = findViewById(R.id.sp_example);
        sp_example.setPrompt("请选择全景照片例子");
        sp_example.setAdapter(exampleAdapter);
        sp_example.setOnItemSelectedListener(new ExampleSelectedListener());
        sp_example.setSelection(0);
    }

    private String[] exampleArray = {"现代客厅", "中式客厅", "故宫风光", "城市街景",
            "鸟瞰城市", "俯拍高校", "私人会所", "酒店大堂"};
    private int[] resArray = {R.drawable.panorama01, R.drawable.panorama02, R.drawable.panorama03, R.drawable.panorama04,
            R.drawable.panorama05, R.drawable.panorama06, R.drawable.panorama07, R.drawable.panorama08};

    class ExampleSelectedListener implements AdapterView.OnItemSelectedListener {
        public void onItemSelected(AdapterView<?> arg0, View arg1, final int arg2, long arg3) {
            pv_content.setDrawableId(resArray[arg2]); // 传入全景图片的资源编号
        }

        public void onNothingSelected(AdapterView<?> arg0) {}
    }

}

util类

package com.example.threed.util;

import java.util.ArrayList;
import java.util.List;

public class PanoramaUtil {

    // 获取全景图片的顶点列表
    public static List<Float> getPanoramaVertexList(int perVertex, double perRadius) {
        List<Float> vertexList = new ArrayList<>();
        for (int i = 0; i < perVertex; i++) {
            for (int j = 0; j < perVertex; j++) {
                float x1 = (float) (Math.sin(i * perRadius / 2) * Math.cos(j * perRadius));
                float z1 = (float) (Math.sin(i * perRadius / 2) * Math.sin(j * perRadius));
                float y1 = (float) Math.cos(i * perRadius / 2);

                float x2 = (float) (Math.sin((i + 1) * perRadius / 2) * Math.cos(j * perRadius));
                float z2 = (float) (Math.sin((i + 1) * perRadius / 2) * Math.sin(j * perRadius));
                float y2 = (float) Math.cos((i + 1) * perRadius / 2);

                float x3 = (float) (Math.sin((i + 1) * perRadius / 2) * Math.cos((j + 1) * perRadius));
                float z3 = (float) (Math.sin((i + 1) * perRadius / 2) * Math.sin((j + 1) * perRadius));
                float y3 = (float) Math.cos((i + 1) * perRadius / 2);

                float x4 = (float) (Math.sin(i * perRadius / 2) * Math.cos((j + 1) * perRadius));
                float z4 = (float) (Math.sin(i * perRadius / 2) * Math.sin((j + 1) * perRadius));
                float y4 = (float) Math.cos(i * perRadius / 2);

                vertexList.add(x1);
                vertexList.add(y1);
                vertexList.add(z1);

                vertexList.add(x2);
                vertexList.add(y2);
                vertexList.add(z2);

                vertexList.add(x3);
                vertexList.add(y3);
                vertexList.add(z3);

                vertexList.add(x3);
                vertexList.add(y3);
                vertexList.add(z3);

                vertexList.add(x4);
                vertexList.add(y4);
                vertexList.add(z4);

                vertexList.add(x1);
                vertexList.add(y1);
                vertexList.add(z1);
            }
        }
        return vertexList;
    }

    // 获取全景图片的纹理列表
    public static List<Float> getPanoramaTextureList(int perVertex) {
        List<Float> textureList = new ArrayList<>();
        double perW = 1 / (float) perVertex;
        double perH = 1 / (float) (perVertex);
        for (int i = 0; i < perVertex; i++) {
            for (int j = 0; j < perVertex; j++) {
                float w1 = (float) (i * perH);
                float h1 = (float) (j * perW);

                float w2 = (float) ((i + 1) * perH);
                float h2 = (float) (j * perW);

                float w3 = (float) ((i + 1) * perH);
                float h3 = (float) ((j + 1) * perW);

                float w4 = (float) (i * perH);
                float h4 = (float) ((j + 1) * perW);

                textureList.add(h1);
                textureList.add(w1);

                textureList.add(h2);
                textureList.add(w2);

                textureList.add(h3);
                textureList.add(w3);

                textureList.add(h3);
                textureList.add(w3);

                textureList.add(h4);
                textureList.add(w4);

                textureList.add(h1);
                textureList.add(w1);
            }
        }
        return textureList;
    }

}

XML文件

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal" >

        <TextView
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:gravity="right"
            android:text="全景照片例子:"
            android:textColor="@color/black"
            android:textSize="17sp" />

        <Spinner
            android:id="@+id/sp_example"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:spinnerMode="dialog" />
    </LinearLayout>

    <com.example.threed.panorama.PanoramaView
        android:id="@+id/pv_content"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1" />

</LinearLayout>

创作不易 觉得有帮助请点赞关注收藏~~~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/45498.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

基于有偏距离权值(Weighted cubic O-MOMS with warping)三次O-MOMS插值理论的图像超分辨重构研究-附Matlab程序

⭕⭕ 目 录 ⭕⭕✳️ 一、图像超分辨率重构原理✳️ 二、三次O-MOMS插值重构理论与实验分析✳️ 2.1 三次O-MOMS(Cubic O-MOMS)插值理论与实验验证✳️ 2.2 有偏距离三次O-MOMS插值重构理论与实验验证✳️ 2.3 权重三次O-MOMS插值理论与实验验证✳️ 2.4 有偏距离权值三次O-MOM…

[附源码]Python计算机毕业设计Django茶叶销售微信小程序

项目运行 环境配置&#xff1a; Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术&#xff1a; django python Vue 等等组成&#xff0c;B/S模式 pychram管理等等。 环境需要 1.运行环境&#xff1a;最好是python3.7.7&#xff0c;…

Hive 查看和修改 tez 容器的资源

1. 查看当前的配置 1.1 进入 AppMaster 或 History 进入运行中&#xff0c;或者运行完作业对应的 “Tracking URL”。以下示例是已经运行完的 job。 1.2 进入 tez-ui 进入 tez-ui 之后&#xff0c;点击 Configurations 1.3 查看配置 输入要查询的配置项&#xff0c;点击…

随smart登陆欧洲,亿咖通科技踏上出海新征程

随着全新smart精灵#1正式登陆欧洲&#xff0c;全球移动出行科技公司亿咖通科技同步向欧洲车主揭晓其搭载于新车上的下一代智能座舱系统&#xff0c;并正式将其出海战略向前推进关键一步&#xff0c;成为中国智能化出海的一座崭新里程碑。 全新smart精灵#1预计将于2022年底开始&…

π220N31兼容代替TI ISO1540DR 低功耗 3.0kVrms 双向I2C 隔离器

π220N31兼容代替TI ISO1540DR 低功耗 3.0kVrms 双向I2C 隔离器&#xff0c;I2C隔离器输入和输出采用二氧化硅(SiO2)介质隔离&#xff0c;可阻断高电压并防止噪声电流进入控制侧&#xff0c;避免电路干扰和损坏敏感器件。与光电耦合器相比&#xff0c;在功能、性能、尺寸和功耗…

2022年全国最新消防设施操作员模拟真题题库及答案

百分百题库提供消防设施操作员考试试题、消防设施操作员考试预测题、消防设施操作员考试真题、消防设施操作员证考试题库等,提供在线做题刷题&#xff0c;在线模拟考试&#xff0c;助你考试轻松过关。 124、消防电梯设置应符合下列哪些( )要求 A.消防电梯载重量不应小于800kg …

麦芽糖-阿奇霉素 maltose-Azithromycin

麦芽糖-阿奇霉素 maltose-Azithromycin 中文名称&#xff1a;麦芽糖-阿奇霉素 英文名称&#xff1a;maltose-Azithromycin 别称&#xff1a;阿奇霉素修饰麦芽糖&#xff0c;阿奇霉素-麦芽糖 PEG接枝修饰麦芽糖 麦芽糖-聚乙二醇-阿奇霉素 Azithromycin-PEG-maltose 阿…

@企业主们看过来,用华为云CDN给你的网页加个速

企业主们看过来&#xff0c;用华为云CDN给你的网页加个速 前段时间参加秋招的时候&#xff0c;被问到了一个问题&#xff0c;CND是干啥的&#xff0c;什么是CND&#xff0c;面试官问我这个问题的时候&#xff0c;我暗窃喜这不是我的强项吗&#xff01;&#xff01;&#xff01;…

【C++面向对象程序设计】CH5 继承与派生(续)——虚基类

目录 前言 一、虚基类的作用 二、虚基类的初始化 三、例【5.9】在【例5.8】中在teacher类和student类之上增加一个共同的基类person&#xff0c;人员的一些基本数据放在person中 四、多层多重继承用虚基类 五、虚基类的构造函数 六、多重继承如何工作 七、虚拟继承 八…

【深入浅出Java并发编程指南】「难点 - 核心 - 遗漏」线程状态流转及生命周期的技术指南(知识点串烧)

前提介绍 本章主要介绍相关线程声明周期的转换机制以及声明周期的流转关系以及相关AQS的实现和相关的基本原理&#xff0c;配合这相关官方文档的中英文互译的介绍。 线程状态流转及生命周期 当线程被创建并启动以后&#xff0c;它既不是一启动就进入了执行状态&#xff0c;也不…

17. 电话号码的字母组合

17. 电话号码的字母组合 给定一个仅包含数字 2-9 的字符串&#xff0c;返回所有它能表示的字母组合。答案可以按 任意顺序 返回。 给出数字到字母的映射如下&#xff08;与电话按键相同&#xff09;。注意 1 不对应任何字母。 示例 1&#xff1a; 输入&#xff1a;digits …

R语言分布滞后非线性模型(DLNM)研究发病率,死亡率和空气污染示例

全文下载链接&#xff1a;http://tecdat.cn/?p21317本文提供了运行分布滞后非线性模型的示例&#xff0c;同时描述了预测变量和结果之间的非线性和滞后效应&#xff0c;这种相互关系被定义为暴露-滞后-反应关联&#xff08;点击文末“阅读原文”获取完整代码数据&#xff09;。…

C语言文件操作

目录序言文件程序文件&数据文件程序文件数据文件文本文件&二进制文件文件名操作初阶打开&关闭文件fopen读写文件fputc & fgetc文件缓冲区文件指针操作进阶打开方式"w"(只写)"r"(只读)"a"(追加)文件的顺序读写fgets & fputsf…

Mac安装rabbitmq延迟队列插件

Mac安装rabbitmq延迟队列插件我是通过brew安装的rabbitmq&#xff0c;没有安装Homebrew的需要安装一下查看我们rabbitmq版本&#xff0c;我这里的版本是3.11.3&#xff0c;我们下载的插件大版本必须是3.11 brew info rabbitmq下载rabbitmq_delayed_message_exchange插件&#…

虹科分享 | 终端安全防护 | 网络安全术语列表(终篇)

如果你的工作或者生活与网络安全有关&#xff0c;你就知道它使用了自己独特的、不断发展的语言。术语和缩略语受到网络安全专家的喜爱。因此&#xff0c;我们创建了一个全面的网络安全词汇表&#xff0c;解释了常用的网络安全术语、短语和技术。我们设计此列表是为了揭开安全专…

春夏秋冬-第12届蓝桥杯Scratch选拔赛真题精选

[导读]&#xff1a;超平老师计划推出Scratch蓝桥杯真题解析100讲&#xff0c;这是超平老师解读Scratch蓝桥真题系列的第89讲。 蓝桥杯选拔赛每一届都要举行4~5次&#xff0c;和省赛、国赛相比&#xff0c;题目要简单不少&#xff0c;再加上篇幅有限&#xff0c;因此我精挑细选…

[附源码]计算机毕业设计springboot车险销售管理系统论文

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

MySQL软件常见操作

1登录MySQL 登录&#xff0c;如果你配置了环境变量就可以winr&#xff0c;在运行框输入cmd&#xff0c;输入登录命令 第一种&#xff1a;直接输入密码 mysql -uroot -p(你的密码没有有括号) 第二种不直接输入密码 mysql -uroot -p 前面两种都是localhost登录 下面是完整版 m…

流媒体传输 - RTSP 协议认证过程

Rtsp 认证 主要分为两种&#xff1a; 基本认证 &#xff08;Basic authentication&#xff09;和 摘要认证 &#xff08;Digest authentication&#xff09; 基本认证是 HTTP 1.0 提出的认证方案&#xff0c;其消息传输不经过加密转换因此存在严重的安全隐患。 摘要认证是 H…

[附源码]计算机毕业设计springboot房屋租赁信息系统

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…