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

news/2025/3/19 6:37:05/

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

不管是绘画还是摄影,都是把三维的物体投影到平面上,其实仍旧呈现二维的模拟画面。 随着科技的发展,传统的成像手段越来越凸显出局限性,缘由在于人们需要一种更逼真更接近现实的技术,从而更好地显示三维世界的环境信息,这便催生了增强现实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; // 声明一个全景视图对象@Overrideprotected 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" ><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:orientation="horizontal" ><TextViewandroid:layout_width="0dp"android:layout_height="wrap_content"android:layout_weight="1"android:gravity="right"android:text="全景照片例子:"android:textColor="@color/black"android:textSize="17sp" /><Spinnerandroid: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.PanoramaViewandroid:id="@+id/pv_content"android:layout_width="match_parent"android:layout_height="0dp"android:layout_weight="1" /></LinearLayout>

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


http://www.ppmy.cn/news/80.html

相关文章

Vilatile底层逻辑总结

#### 增加volatile 使用JIT优化的时候 禁止出现语句重排 #### volatile是Java虚拟机提供的轻量级同步机制。 - 保证可见性 - 不保证原子性&#xff08;整体流程成功 整体流程失败&#xff09;如果要保证原子性-加 synchronized 或者直接使用 Automic 原子类 - 禁止指令重排&am…

运算符与分支、循环语句

一、运算符 1. 算术运算符 加 减 -乘 *除 /取余 % &#xff08;开发中经常作为某个数字是否被整除&#xff09; 2. 赋值运算符 对变量进行赋值的运算符。 -*/% 3. 一元运算符 JavaScript的运算符可以根据所需表达式的个数&#xff0c;分为一元运算符、二元运算符、三元运…

【visual studio】visual studio 2022 无法 复制黏贴

visual studio 2022 cannot copy paste 其他网友也有反馈到微软&#xff1a;VS 2022 Copy and Paste form feature Broken?Copy paste still not fixed in Visual studio 2022表现是突然就无法复制和黏贴了其他的app 就没有这个问题每次都是重启电脑解决。 2022年11月fix 今…

基于FPGA+MPU+MCU全自动血细胞分析仪解决方案

全自动血细胞分析仪是医院临床检验应用非常广泛的仪器之一&#xff0c;用来检测红细胞、血红蛋白、白细胞、血小板等项目。是基于电子技术和自动化技术的全自动智能设备&#xff0c;功能齐全&#xff0c;操作简单&#xff0c;依托相关计算机系统在数据处理和数据分析等方面具有…

xss-labs/level11

首先输入 <script>alert(xss)</script> 丝毫不差 没有出现回弹现象 根本就不出人意料好吧 接着来看一下源代码好吧 能够看得出来第一个输出点由于htmlspecialchars转义操作 所以上述代码根本行不通 这一关比上一关又多了一个隐藏表单 一共是有四个隐藏表单 从…

计算机毕业设计springboot驾校学员管理系统w42sj源码+系统+程序+lw文档+部署

计算机毕业设计springboot驾校学员管理系统w42sj源码系统程序lw文档部署 计算机毕业设计springboot驾校学员管理系统w42sj源码系统程序lw文档部署本源码技术栈&#xff1a; 项目架构&#xff1a;B/S架构 开发语言&#xff1a;Java语言 开发软件&#xff1a;idea eclipse 前…

【数据结构】实验 7 图

目录 【实验目的】 【实验预习】 【实验内容】 1.编写程序&#xff0c;实现图的邻接矩阵存储及图的深度优先搜索和广度优先搜索 2.编写程序&#xff0c;实现图的邻接表存储和拓扑排序算法 3.编写程序&#xff0c;实现带权图的存储、图的最小生成树及单源最短路径算法 …

C语言习题练习11--指针

1.代码结果 #include <stdio.h> int main() {int arr[] {1,2,3,4,5};short *p (short*)arr;int i 0;for(i0; i<4; i){*(pi) 0;}for(i0; i<5; i){printf("%d ", arr[i]);}return 0; } 正常&#xff1a;0001--00 02--00 03--00 04--00 05 数组内部是倒…