项目里碰到了展示全景图的需求,以前没做过,google了一下已经有不少现成的库可以拿来用了,不过有前辈提到可以用SceneKit来实现,刚好去年做AR的时候用过SceneKit,一下子来了思路,在这里记录一下。

思路

思路就是在SceneKit的场景里放置一个球体,把全景图贴在球体表明,然后把相机放置在球体中心不就得了。

实现

实现起来很简单,往 ViewController 放一个 SCNView,一个相机,一个球体就好。

class PanoramaVC: UIViewController {let sceneView = SCNView()// 相机Nodelet cameraNode = SCNNode()// 球体let sphere = SCNSphere()...override func viewDidLoad() {super.viewDidLoad()sceneView.scene = SCNScene.init()sceneView.frame = self.view.frameself.addSubView(sceneView)sphere.radius = 50// 只渲染一面,从球体里面看,外面就不用渲染了sphere.firstMaterial?.isDoubleSided = false// 剔除外面sphere.firstMaterial?.cullMode = .front// 把全景图“贴”到球体上sphere.firstMaterial?.diffuse.contents = UIImage.init(named: "全景图名")// 球体Node,位置放到场景原点let sphereNode = SCNNode.init(geometry: sphere)sphereNode.position = SCNVector3Make(0, 0, 0)sceneView.scene?.rootNode.addChildNode(sphereNode)// 相机Node,位置放到场景原点cameraNode.camera = SCNCamera.init()cameraNode.position = SCNVector3Make(0, 0, 0)sceneView.scene?.rootNode.addChildNode(cameraNode)}
}
复制代码

到这里就成功了。但是这样有个问题:全景图左右是反的。这是因为全景图是从球体外面“贴”到球体上的,所以从球体内部看到的全景图是左右反向的。那我就把全图片给它左右反向就好啦。使用下面这个函数可以把图片左右反向。

func flipImageLeftRight(_ image: UIImage) -> UIImage? {UIGraphicsBeginImageContextWithOptions(image.size, false, image.scale)let context = UIGraphicsGetCurrentContext()!context.translateBy(x: image.size.width, y: image.size.height)context.scaleBy(x: -image.scale, y: -image.scale)context.draw(image.cgImage!, in: CGRect(origin:CGPoint.zero, size: image.size))let newImage = UIGraphicsGetImageFromCurrentImageContext()UIGraphicsEndImageContext()return newImage
}
复制代码

此时把左右反向的图片“贴”到球体上

sphere.firstMaterial?.diffuse.contents = flipImageLeftRight(UIImage.init(named: "全景图名"))
复制代码

全景图长这样:

快看下效果吧(实际还是很流畅的,gif 效果有卡顿):

悬浮广告

除了全景图,还有个需求就是在全景图适当的位置显示广告图标,点击后会出现一张海报。要求广告图标在全景图视角变化的时候跟着动。

这个其实也不难,只要知道需要放置广告的位置坐标,在全景图视角变化的同时移动图标让图标和目标位置同步就好。

但是我怎么知道全景图视角怎样变化呢(其实是相机Node旋转角度在变化)?我想通过相机旋转时监听旋转角度但是没找到监听方法,如果有谁知道望告知,十分感谢!

那我干脆自己控制相机旋转角度。使用这句代码禁止通过 sceneView 来控制相机旋转:

sceneView.allowsCameraControl = false
复制代码

然后监听滑动手势来控制相机和广告图标旋转:

//相机旋转角度(单位是弧度,转一圈是 2pi)
var rotateAngle = CGPoint.zero
...override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {if touches.count == 1 {let touch = touches.first// 手指在x、y方向滑动距离let offsetX = (touch?.location(in: self.view).x)! - (touch?.previousLocation(in: self.view).x)!let offsetY = (touch?.location(in: self.view).y)! - (touch?.previousLocation(in: self.view).y)!// 手指滑动的距离和相机旋转弧度比值约等于800(这个值是我测试得到的可能不精确)rotateAngle.x += offsetX / 800rotateAngle.y += offsetY / 800// 把rotateAngle.x按2?取模rotateAngle.x = rotateAngle.x.truncatingRemainder(dividingBy: CGFloat.pi * 2)// 控制全景图上下旋转不颠倒if rotateAngle.y > 1 {rotateAngle.y = 1}if rotateAngle.y < -1 {rotateAngle.y = -1}// 旋转相机rotateCamera()// 旋转广告rotateAdvertisement()}
}/// 旋转相机
func rotateCamera() {let rotation = SCNAction.rotateTo(x: rotateAngle.y, y: rotateAngle.x, z: 0, duration: 0)cameraNode.runAction(rotation)
}
复制代码

要旋转广告图标就要先添加一个广告图标,这里使用一个 UIButton 作为示例。我想把广告图标放在右边的狮子头上,而狮子头的位置的x、y坐标与图片宽高比值为0.570和0.456。

...
var adPosition = CGPoint.init(x: 0.570, y: 0.456)
var adButton = UIButton()func addAdversisement() {adButton.setTitle("我是广告图标", for: .normal)adButton.backgroundColor = UIColor.init(white: 1, alpha: 0.5)adButton.layer.cornerRadius = 10adButton.frame = CGRect.init(origin: CGPoint.zero, size: CGSize.init(width: 80, height: 40))self.view.addSubview(adButton)// 把图标放在屏幕上对应的位置adButton.center = positionInScreenOfAd(position: adPosition)
}/// 从广告图标在图片上的位置计算出在屏幕上的位置
///
/// - Parameter position: 图片上的位置
/// - Returns: 屏幕上的位置
func positionInScreenOfAd(position: CGPoint) -> CGPoint {//广告点相对于图片原点的偏移角度let diffToOrigin = CGPoint.init(x: position.x * CGFloat.pi, y: (position.y - 0.5) * CGFloat.pi)//转动过程中,广告点相对于屏幕中心点的偏移角度var offsetAngle = CGPoint.init(x: diffToOrigin.x + rotateAngle.x / 2, y: diffToOrigin.y + rotateAngle.y)offsetAngle.x = offsetAngle.x.truncatingRemainder(dividingBy: CGFloat.pi)//在显示范围内,就按角度比例显示在屏幕上let viewSize = self.view.frame.size// 全景图显示在屏幕上的范围为 -0.20453 < x < 0.20453, -0.53996 < y < 0.53996(单位为弧度),超出范围就不显示,其中 x 值正负要分开处理//(这个值是我测试得到的可能不精确)if abs(offsetAngle.y) > 0.53996 {return CGPoint.zero}if abs(offsetAngle.x) < 0.20453 {return CGPoint.init(x: viewSize.width / 2 * (1 + offsetAngle.x / 0.20453), y: viewSize.height / 2 * (1 + offsetAngle.y / 0.53996))}if abs(offsetAngle.x) >= CGFloat.pi - 0.20453 && abs(offsetAngle.x) < CGFloat.pi {return CGPoint.init(x: viewSize.width / 2 * (1 + (abs(offsetAngle.x) - CGFloat.pi) / 0.20453), y: viewSize.height / 2 * (1 + offsetAngle.y / 0.53996))}return CGPoint.zero
}
复制代码

接下来就是旋转广告图标:

/// 旋转广告
func rotateAdvertisement() {// 实时计算旋转过程中广告图标在屏幕上的位置let newP = positionInScreenOfAd(position: adPosition)// newP == CGPoint.zero 表示图标位置在屏幕之外adButton.isHidden = newP == CGPoint.zeroadButton.center = positionInScreenOfAd(position: adPosition)
}
复制代码

到这里就可以看到广告图标随全景图滑动了:

大功告成!

iOS 使用 SceneKit 实现全景图相关推荐

  1. iOS 10 SceneKit 新特性 – SceneKit 制作 3D 场景框架

    来源:scauos(@大朕东) 链接:http://www.jianshu.com/p/b30785bb6c97 开头语: 今天的主题是探索iOS10 SceneKit的新功能,你可以观看今年WWDC ...

  2. iOS SpriteKit/SceneKit/Metal浅析

    [SpriteKit] 在iOS7中内置了新的SpriteKit框架,该框架主要用来开发2D游戏.是开发iOS和OS X 下的2D游戏引擎,可以使用OC或者Swift来进行开发.目前已经支持的内容包括 ...

  3. iOS基于SceneKit的3D汽车改色

    基于SceneKit的汽车改色,需要修改模型文件可以去 3D Models for Professionals :: TurboSquid自行下载,免费资源还挺多的. 先上个效果图吧: 核心代码: l ...

  4. [SceneKit专题]20-仿水果忍者小游戏Geometry-Fighter

    说明 本系列文章是对<3D Apple Games by Tutorials>一书的学习记录和体会 此书对应的代码地址 SceneKit系列文章目录 更多iOS相关知识查看github上W ...

  5. base64转html文件,图片转换成Base64编码集成到html文件

    首先为什么要这么做?  原因很简单这样可以减少与服务器的请求,当然对于一些浏览器并不支持,如IE8.通常用在手机版网站中,具体转化方法如下: 1.在线打开Base64的编码器将图片编码成Base64 ...

  6. 鱼眼图片转化android,iOS简单实现全景图小行星和鱼眼模式

    基于SceneKit,先导入SceneKit.framework 首先说一下本人对全景图的理解,所谓全景图,就是一个球体,在球体表面贴上图片,在不同位置看就会产生不同的效果. 比如如果把摄像机放在球心 ...

  7. iOS开发之SceneKit框架--SCNView.h

    1.SCNView 在macOS中,SCNView是NSView的子类,在iOS和tvOS中,SCNView是UIView的子类.SCNView用于显示SceneKit的3D场景,而需要设置场景的相关 ...

  8. 利用OpenGL实现IOS上VR全景图

    VR做为近几年较火的一个新技术,获得了许多大公司的关注,如Google.微软.Facebook都先后进入这个领域,希望能够在VR上能抢占先机,获得市场.2016又被称为VR元年,借此势头VR获得了巨大 ...

  9. ios 3D引擎 SceneKit 开发(2) --贴图篇

    hello ,大家好,我是Roc.Tian,最近一直在研究苹果自家的3D 引擎 SceneKit ,适当写写博客,总结一下,与大家分享一下,也希望跟大家交流,共同进步. 今天简单说一下 SceneKi ...

最新文章

  1. MIT科学家首次发现只对歌唱有反应的神经元,对,只能人声带伴奏的那种歌
  2. Reveal使用心法
  3. DataWorks 功能实践 — 生产开发环境隔离
  4. 删除未使用的引用 | Visual Studio 2019(16.10)新功能试用
  5. d.php xfso_centos平台基于snort、barnyard2以及base的IDS(入侵检测系统)的搭建与测试及所遇问题汇总...
  6. 选择操作、投影操作、交操作
  7. 赛尔笔记 | 对比学习简述
  8. SPOJ SUBST1 New Distinct Substrings(后缀数组 本质不同子串个数)题解
  9. 摆动式运输机运动分析_三河燕郊人民医院设备资讯(第28期):SW—3702精子分析仪...
  10. python 优点_python语言有什么优势
  11. 设置linearlayout最大高度_桥式、门式起重机-安全防护装置30条设置要求(六)- 第1~10条...
  12. 微信emoji表情数据如何添加到json中
  13. notepad++ paste data vertically
  14. SSIS Execute SQL Task assign output 的两种方法
  15. python启动浏览器崩溃
  16. 电子印章怎么验证真假?
  17. html svg波浪,CSS实现svg图片水纹波浪流动效果
  18. 贷前审批策略的6个搭建思路
  19. 计算机模拟考试系统(excel2003),用Excel建立模拟考试系统(下)
  20. 滑动轨迹 曲线 python_Python 模拟真实运动轨迹,轻松完成长跑和打卡

热门文章

  1. 方框滤波(Box filtering)
  2. SQL查询结果加序列号
  3. Qt官方示例-虚拟键盘使用
  4. Java设计模式的作用
  5. 六、Quartz-配置详解
  6. NoSQL入门------关于NoSQL
  7. Windows版本下安装使用Grafana教程
  8. Unity报错:Assertion failed on expression: ‘IsMatrixValid(matrix)‘...的解决办法
  9. 非线性规划----经济调度(Python实现)
  10. 理解矩阵 from孟岩--流星小屋