VR+全景播放器+头控讲解-06

article/2025/8/22 3:35:12

VR+全景播放器+头控讲解-01-知识储备
VR+全景播放器+头控讲解-02-创建球体
VR+全景播放器+头控讲解-03-渲染视频
VR+全景播放器+头控讲解-04-滑动手势
VR+全景播放器+头控讲解-05-伸缩画面
VR+全景播放器+头控讲解-06-头控实现
VR+全景播放器+头控讲解-07-分屏技术

学习目标

掌握头控部分布局
如何检测头控按钮被选中
如何实现悬停动画

在UIView上面布局我们可以使用UIButton UIView UIImageView等,但是是在3D场景中,我们不能使用UIView,我们要使用平面几何当视图使用,下面具体介绍一下

分析

1594482-e7776042616a9cc9.png
243FA4BB-FAC5-4A93-A9DF-C3A1F447F010.png

提示几点

  1. 头控根节点可以放在根节点上中心或者球体中心都是可以的,但是考虑到后期我们要进行视频滤波,所以最好放到场景节点上
  2. 低头菜单出现,抬头菜单消失,这个要根据重力感应在X轴旋转决定的
  3. 当抬头时菜单总是出现在下方,一旦出现不会跟随照相机转动,当重力感应变化时,我们让头控节点的绕着y轴旋转

实现步骤

第一步.创建菜单节点

+(SCNNode*)createBackgroundNode{/// 创建一个背景色SCNNode* bgNode = [SCNNode node];bgNode.position = SCNVector3Make(0, CONTROL_DISTANCE/2, -CONTROL_DISTANCE);bgNode.geometry.firstMaterial.cullMode = SCNCullModeBack;bgNode.rotation = SCNVector4Make(1, 0, 0, atan(1/2.0));return bgNode;}
+(SCNNode*)createShadowNode{SCNNode *shadowNode = [SCNNode node];shadowNode.geometry= [SCNPlane planeWithWidth:MENU_MAX_LENGTH height:BUTTON_WIDHT+BUTTON_WIDHT/2.0];shadowNode.geometry.firstMaterial.diffuse.contents = BACKGROUND_MENU_SHOW_NAME;shadowNode.position = SCNVector3Make(0, 0, 0);shadowNode.geometry.firstMaterial.cullMode = SCNCullModeBack;return shadowNode;
}+(SCNNode*)createPlayNode{SCNNode* playNode = [SCNNode node];playNode.geometry = [SCNPlane planeWithWidth:BUTTON_WIDHT*0.8 height:BUTTON_WIDHT*0.8];playNode.geometry.firstMaterial.diffuse.contents = ICON_PLAY_NAME;playNode.position = SCNVector3Make(-20, 0, 2);playNode.geometry.firstMaterial.cullMode = SCNCullModeBack;return playNode;
}
+(SCNNode*)createPreviousNode{/// 第六步-创建播放上一个视频的节点SCNNode* previousNode = [SCNNode node];previousNode.geometry = [SCNPlane planeWithWidth:BUTTON_WIDHT*0.8 height:BUTTON_WIDHT*0.8];previousNode.geometry.firstMaterial.diffuse.contents = ICON_PREVIOUS_NAME;previousNode.position = SCNVector3Make(-60, 0, 2);previousNode.geometry.firstMaterial.cullMode = SCNCullModeBack;return previousNode;
}
+(SCNNode*)createAfterNode{SCNNode *nextNode = [SCNNode node];nextNode.geometry  = [SCNPlane planeWithWidth:BUTTON_WIDHT*0.8 height:BUTTON_WIDHT*0.8];nextNode.geometry.firstMaterial.diffuse.contents = ICON_AFTER_NAME;nextNode.position = SCNVector3Make(20, 0, 2);nextNode.geometry.firstMaterial.cullMode = SCNCullModeBack;return nextNode;
}
+(SCNNode*)createMoreNode{SCNNode* moreNode = [SCNNode node];moreNode.geometry = [SCNPlane planeWithWidth:BUTTON_WIDHT*0.8 height:BUTTON_WIDHT*0.8];moreNode.geometry.firstMaterial.diffuse.contents = ICON_MENU_HIDDEN_NAME;moreNode.position = SCNVector3Make(60, 0, 2);moreNode.geometry.firstMaterial.cullMode = SCNCullModeBack;return moreNode;
}
+(SCNNode*)createHighVoiceNode{SCNNode* voiceHighNode = [SCNNode node];voiceHighNode.geometry = [SCNPlane planeWithWidth:BUTTON_WIDHT*0.8 height:BUTTON_WIDHT*0.8];voiceHighNode.geometry.firstMaterial.diffuse.contents = ICON_VOICE_HIGH_NAME;voiceHighNode.geometry.firstMaterial.cullMode = SCNCullModeBack;return voiceHighNode;
}
+(SCNNode*)createLowVoiceNode{SCNNode* voiceLowNode = [SCNNode node];voiceLowNode.geometry = [SCNPlane planeWithWidth:BUTTON_WIDHT*0.8 height:BUTTON_WIDHT*0.8];voiceLowNode.geometry.firstMaterial.diffuse.contents = ICON_VOICE_LOW_NAME;voiceLowNode.geometry.firstMaterial.cullMode = SCNCullModeBack;return voiceLowNode;
}

创建的方法基本一致,调节一下位置即可!

第二步 添加到先把功能按钮节点添加到头控背景节点上,然后将背景节点添加到头控根节点上去

[self.scene.rootNode addChildNode:self.controlNode];
[self.controlNode addChildNode:self.bgNode];
[self.bgNode addChildNode:self.shadowNode];
[self.bgNode addChildNode:self.playOrPauseNode];
[self.bgNode addChildNode:self.proviousNode];
[self.bgNode addChildNode:self.AfterNode];
[self.bgNode addChildNode:self.showMoreNode];
[self.bgNode addChildNode:self.lowVoiceNode];
[self.bgNode addChildNode:self.highVoiceNode];
[self.scene.rootNode addChildNode:self.eyeNode];

第三步 添加点控节点,将其放在添加到照相机节点上去,这样照相机转动的时候,它就能跟着转动,效果就是一直在屏幕中央

 [self.eyeNode addChildNode:self.dotNode];

第四步 抬头小时低头出现

-(void)controlEyeNodeInVR:(SCNVector3)vector{
// 向上抬头 并且local
if ( vector.x>0.8 &&self.bgNode.hidden==false){self.bgNode.hidden = true;self.dotNode.hidden = true;
}else if ( vector.x<0.8 &&self.bgNode.hidden){self.bgNode.hidden = false;self.dotNode.hidden = false;SCNVector3 eulerAngles = self.controlNode.eulerAngles;// eulerAngles.y = -vector.z;eulerAngles.z = vector.z;self.controlNode.eulerAngles = eulerAngles;}
}

第五步 如何检测点控射线和头控按钮相交
思路:

先将按钮转换到照相机节点上,点控射线是否和按钮区域相交,就是相当于头控坐标 x在范围 [-button.width/2,button.width/2]内, y在 [-button.height/2.button.heigth/2.0]内

/// 下面就举一个例子,其它的都是类似的
SCNVector3 proviousPosition = [self.bgNode convertPosition:self.proviousNode.position toNode:self.self.eyeNode];
if (proviousPosition.x > -BUTTON_WIDHT/2 && proviousPosition.x < BUTTON_WIDHT/2 && proviousPosition.y > -BUTTON_WIDHT/2 && proviousPosition.y < BUTTON_WIDHT/2 ){if (!self.controlDotIn.inPreviousNode&&!self.proviousNode.hidden){// 第一次进入 还没有离开// 开始执行悬停动画[self startAnimation];}_controlDotIn.inPreviousNode = YES;return;
}else if(_controlDotIn.inPreviousNode){// 检测到离开按钮return;
}

悬停动画的实现

第一步 创建动画行为

-(void)createAnimation{
/// 创建动画节点
self.animationNode = [SCNNode node];
self.animationNode.geometry = [SCNPlane planeWithWidth:CONTROL_DISTANCE/5.0 height:CONTROL_DISTANCE/5.0];
self.animationNode.hidden = true;// 控制点上增加动画节点
[self.dotNode addChildNode:self.animationNode];
__weak XJRenderView* weakSelf = self;
self.animationAction  = [SCNAction customActionWithDuration:3 actionBlock:^(SCNNode * _Nonnull node, CGFloat elapsedTime) {int time = (int) (elapsedTime *(weakSelf.gif.count-1)/3.0);node.geometry.firstMaterial.diffuse.contents = weakSelf.gif[time];if(time == weakSelf.gif.count-1){[weakSelf animationDidStop];}
}];
}
-(NSArray *)gif{if(!_gif){NSMutableArray *gif= [NSMutableArray arrayWithCapacity:GIF_LENGTH];for(int i = 0 ; i< GIF_LENGTH ;i++){NSString *name = [NSString stringWithFormat:GIF_PREFIX_NAME_FORMAT,i];[gif addObject:name];}_gif = gif;}return _gif;
}

第二步 在执行的节点上执行动画行为

-(void)startAnimation{[self.animationNode runAction:self.animationAction];
}

本节讲解完毕
SceneKit 中文教程


http://chatgpt.dhexx.cn/article/8bPKvB8k.shtml

相关文章

mxreality.js 免费开源的全景图/全景视频/VR 直播播放器介绍

[2018-10-20 重要更新]支持VR直播功能支持全景视频poster支持全景图和视频和场景之间随意切换全景模式切换回默认主视角播放列表 优点&#xff1a; 1、全景图支持全景模式和VR模式 2、支持网页端全景图补天功能&#xff0c;有效去除顶部和底部拼接留下的痕迹、做出真正完美的…

Unity3D制作极简版VR全景视频播放器

自从Unity5.6.4还是2017的版本开始&#xff0c;官方提供了兼容移动端和Windows端的视频播放器控件——Video Player&#xff0c;下面介绍如何使用这个控件&#xff0c;制作VR播放器。 1、新建空白场景&#xff0c;新建球体Sphere&#xff0c;Camera放置球心位置&#xff1b; …

基于threejs,完成一个简单的全景图播放器

直接上代码 CameraControls.js相机控制器 import * as THREE from three;function CameraControls(object, domElement, cb, update) {this.object object;this.domElement domElement ! undefined ? domElement : document;this.enabled true;this.lookSpeed 0.1;this.…

使用SceneKit编写VR全景播放器

最近用SceneKit做了全景看房的功能&#xff0c;现总结下如何实现的。 先看下最终的效果&#xff1a; gif1.gif VR图片全景播放器有以下功能: 360度手势滑动&#xff0c;缩放陀螺仪分屏&#xff08;VR眼镜&#xff09;热点hotpot头控/eyepick 手势滑动&#xff0c;缩放&#…

AVProVideo☀️五、播放全景视频

🎊 商务合作:https://skode.cn/file/businesscard/wechat.jpg 🎥 本文由 星河造梦坊公司官方 原创! 🏅 如果你有技术问题或项目开发,都可以加上方的联系方式,和我聊一聊你的故事🧡 文章目录 🟥 360球形全景视频🟧 360立方体全景视频🟨 360天空盒全景视频🟩…

[OpenGL]从零开始写一个Android平台下的全景视频播放器——3.1 全景视频是如何实现的

Github项目地址 为了方便没有准备好梯子的同学&#xff0c;我把项目在CSDN上打包下载&#xff0c;不过更新会慢一些 回到目录 恭喜Martin同学获得由CSDN颁发的“更新慢慢慢”荣誉称号 全景视频有很多种类&#xff0c;例如Sphere全景&#xff0c;Skybox&#xff08;Cubemap&…

VR全景播放器 AvPro Video

最近项目需要&#xff0c;使用Unity开发VR全景播放器&#xff0c;包括PC端和VR一体机端&#xff08;Android&#xff09;。Unity5.6开始支持VideoPlayer&#xff0c;使用自带的VideoPlayer&#xff0c;很顺利把播放器完成了&#xff0c;使用了很长时间&#xff0c;一直没什么性…

FFmpeg 开发(07):FFmpeg + OpenGLES 实现 3D 全景播放器

该文章首发于微信公众号:字节流动 FFmpeg 开发系列连载: FFmpeg 开发(01):FFmpeg 编译和集成 FFmpeg 开发(02):FFmpeg + ANativeWindow 实现视频解码播放 FFmpeg 开发(03):FFmpeg + OpenSLES 实现音频解码播放 FFmpeg 开发(04):FFmpeg + OpenGLES 实现音频可视化播放 FFm…

html全景直播播放器,Insta360 Player(全景视频播放器) V2.3.6 官方版

Insta360 Player是一款性能强劲且专业的全景视频播放器&#xff0c;它支持播放Insta360全景相机拍摄的全景视频和图片&#xff0c;并且支持本地视频的播放&#xff0c;操作非常的简单&#xff0c;有需要的用户可以下载来使用&#xff0c;此软件支持播放 Insta360全景相机产生的…

全景播放器,免安装支持全景视频

全景图片播放器&#xff0c;同时支持全景视频&#xff0c;可直接拖入页面查看。免安装全景播放器,文件不要大于20M Hpano 3D全景播放器是一款免安装可以720度互动浏览的全景图片查看器&#xff0c;通过拖球形全景图片或视频文件进行预览&#xff0c;720度的全方位图片查看器让你…

android 简单的exoplayer全景播放器

全景播放器网上一搜也是一堆一堆的&#xff0c;还有google推出的vrsdk&#xff0c;所以也没啥好说的&#xff0c;就简单记录一下 实现全景主要用到的还是opengles&#xff0c;只要用的开源播放器有setSurface(Surface surface)这个函数&#xff0c;就可以不改播放器源码实现 …

全景播放器

在github上发现的&#xff0c;绿色免安装&#xff0c;完全免费&#xff0c;只有一个界面三个按键&#xff0c;超级简单。里面带来几个全景视频和图片的demo&#xff0c;想体验全景的可以直接打开看看。想换片的时候按键盘的空格键既可以返回主界面。视角查看用鼠标左键点击拖动…

OpenGL ES_手把手教你打造VR全景播放器

OpenGL ES _ 入门_01OpenGL ES _ 入门_02OpenGL ES _ 入门_03OpenGL ES _ 入门_04OpenGL ES _ 入门_05OpenGL ES _ 入门练习_01OpenGL ES _ 入门练习_02OpenGL ES _ 入门练习_03OpenGL ES _ 入门练习_04OpenGL ES _ 入门练习_05OpenGL ES _ 入门练习_06OpenGL ES _ 着色器 _ 介…

播放全景视频【一】:用unity Video Player视频播放器来播放360全景视频

先上图为敬 本文测试环境&#xff1a; Win10 Unity 2020.3.40 Pico G2 4k VR一体机 一、使用Video Player心理负担比较小 使用Unity自带的【视频播放器&#xff08;Video Player&#xff09;】来播放360全景视频的【好处】&#xff1a; 1、控制逻辑与2D视频相同&#xff0c…

利用FFmpeg和OpenGL ES 实现 3D 全景播放器

前言 我们已经利用 FFmpeg OpenGLES OpenSLES 实现了一个多媒体播放器&#xff0c;本文将基于此播放器实现一个酷炫的 3D 全景播放器。 全景播放器原理 全景视频是由多台摄像机在一个位置同时向四面八方拍摄&#xff0c;最后经过后期拼接处理生成的。 用普通的多媒体播放器播…

航空客运订票系统(C语言,软件用的DEV)

这两天整理之前的作业代码&#xff0c;把自己一点一点敲出来的系统又看了一下&#xff0c;挑几个发出来供大家参考。想要源码、报告可以找我啦&#xff0c;代码的注释之前写的都是非常详细的&#xff01; 但是不是无偿的啦&#xff08;不坑&#xff0c;一杯奶茶喽&#xff0c;不…

数据结构_航空客运订票系统(C实现)

文章目录 总述代码粗糙版修理版 还可以修正的点:余票不足时仍然提示还剩下几张(而不是直接拒绝该用户的订票操作)对于购票者的id 不单单是说约定一个可以不重复的主键(命名规则),而且还要辅以必要的检查违约功能 总述 在这里插入代码片 1&#xff0e; 问题描述&#xff1a;(题…

航空机票订票系统

项目介绍 主要功能是使订票系统可以录入航班情况&#xff0c;查询某个航线的情况、办理订票、办理退票、修改航班信息、查询订票信息等。完成此系统&#xff0c;需要综合运用数据结构课程中学到的几种典型数据结构&#xff0c;以及程序设计语言&#xff08;C语言&#xff09;&…

【数据结构应用】航空客运订票系统

目录 前言 一、作业要求介绍 二、各个函数的实现 1.头文件总结需要的功能 &#xff08;1&#xff09;结构体的定义 &#xff08;2&#xff09;各个功能的函数 2.各个函数的具体实现 &#xff08;1&#xff09;初始化 &#xff08;2&#xff09;打印航班信息表 &#xff08;4&…

Java实现航空机票订票系统

1、要求&#xff1a; &#xff08;1&#xff09;设计每条航线所涉及的信息&#xff0c;如终点站名、航班号、飞机号、飞机周日&#xff08;星期几&#xff09;、乘员定额、余票量、订定票的客户名单&#xff08;包括姓名、订票量、舱位等级1&#xff0c;2或3&#xff09;等&…