当前位置: 首页 > news >正文

iOS 使用 SceneKit 实现全景图

新项目为购车App涉及到全景图功能,经过搜索和咨询得到全景图的实现原理为从球内看球上的图片这一原理

思路就是在SceneKit的场景里放置一个球体,把全景图贴在球体表明,然后把相机放置在球体中心,通过旋转相机的角度得到全景图旋转的效果。

现将demo放在下方以供参考

import UIKit
import SceneKitclass FullImageViewController: BaseViewController {// 全景图lazy var sceneView = {let sv = SCNView()sv.scene = SCNScene.init()return sv}()// 相机lazy var cameraNode = {let node = SCNNode()node.camera = SCNCamera.init()node.eulerAngles = currentEulerAnglesnode.position = SCNVector3Make(0, 0, 0)node.camera?.fieldOfView = 90return node}()// 球体lazy var sphere = {let sp = SCNSphere()// 半径sp.radius = 50// 只渲染一面,从球体里面看,外面就不用渲染了sp.firstMaterial?.isDoubleSided = true// 剔除外面sp.firstMaterial?.cullMode = .frontreturn sp}()// 当前相机的旋转角度(用于手势持续累加角度)private var currentEulerAngles = SCNVector3(0, Float.pi, 0)override func viewDidLoad() {super.viewDidLoad()sceneView.frame = CGRectMake(0, 90, view.frame.width, view.frame.width)view.addSubview(sceneView)// 把全景图“贴”到球体上sphere.firstMaterial?.diffuse.contents = flipImageLeftRight(UIImage.init(named: "full_image")!)// 球体Node,位置放到场景原点let sphereNode = SCNNode.init(geometry: sphere)sphereNode.position = SCNVector3Make(0, 0, 0)sceneView.scene?.rootNode.addChildNode(sphereNode)// 相机Node,位置放到场景原点sceneView.scene?.rootNode.addChildNode(cameraNode)sceneView.allowsCameraControl = false// 添加拖动手势识别器,用于控制视角旋转let panGesture = UIPanGestureRecognizer(target: self, action: #selector(handlePan(_:)))sceneView.addGestureRecognizer(panGesture)}// 手势@objc func handlePan(_ gesture: UIPanGestureRecognizer) {let translation = gesture.translation(in: sceneView)//视角向右(正方向),向左(负方向)let deltaYaw = Float(translation.x) * (Float.pi / 180) * 0.5//视角往上(正方向), 视角往下(负方向)let deltaPitch = Float(translation.y) * (Float.pi / 180) * 0.5if gesture.state == .changed {var newX = currentEulerAngles.x + deltaPitchlet newY = currentEulerAngles.y + deltaYaw// 限制上下角度在 ±90° 内,防止翻转newX = max(min(newX, Float.pi / 2), -Float.pi / 2)cameraNode.eulerAngles = SCNVector3(newX, newY, 0)}if gesture.state == .ended {currentEulerAngles = cameraNode.eulerAngles}}// 图片翻转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}}

import UIKit

import SceneKit

class FullImageViewController: BaseViewController {

    // 全景图

    lazy var sceneView = {

        let sv = SCNView()

        sv.scene = SCNScene.init()

        return sv

    }()

    // 相机

    lazy var cameraNode = {

        let node = SCNNode()

        node.camera = SCNCamera.init()

        node.eulerAngles = currentEulerAngles

        node.position = SCNVector3Make(0, 0, 0)

        node.camera?.fieldOfView = 90

        return node

    }()

    // 球体

    lazy var sphere = {

        let sp = SCNSphere()

        // 半径

        sp.radius = 50

        // 只渲染一面,从球体里面看,外面就不用渲染了

        sp.firstMaterial?.isDoubleSided = true

        // 剔除外面

        sp.firstMaterial?.cullMode = .front

        return sp

    }()

    // 当前相机的旋转角度(用于手势持续累加角度)

    private var currentEulerAngles = SCNVector3(0, Float.pi, 0)

    override func viewDidLoad() {

        super.viewDidLoad()

        sceneView.frame = CGRectMake(0, 90, view.frame.width, view.frame.width)

        view.addSubview(sceneView)

        // 把全景图“贴”到球体上

        sphere.firstMaterial?.diffuse.contents = flipImageLeftRight(UIImage.init(named: "full_image")!)

        // 球体Node,位置放到场景原点

        let sphereNode = SCNNode.init(geometry: sphere)

        sphereNode.position = SCNVector3Make(0, 0, 0)

        sceneView.scene?.rootNode.addChildNode(sphereNode)

        // 相机Node,位置放到场景原点

        sceneView.scene?.rootNode.addChildNode(cameraNode)

        sceneView.allowsCameraControl = false

        // 添加拖动手势识别器,用于控制视角旋转

        let panGesture = UIPanGestureRecognizer(target: self, action: #selector(handlePan(_:)))

        sceneView.addGestureRecognizer(panGesture)

    }

    // 手势

    @objc func handlePan(_ gesture: UIPanGestureRecognizer) {

        let translation = gesture.translation(in: sceneView)

        //视角向右(正方向),向左(负方向)

        let deltaYaw = Float(translation.x) * (Float.pi / 180) * 0.5

        //视角往上(正方向), 视角往下(负方向)

        let deltaPitch = Float(translation.y) * (Float.pi / 180) * 0.5

        if gesture.state == .changed {

            var newX = currentEulerAngles.x + deltaPitch

            let newY = currentEulerAngles.y + deltaYaw

            // 限制上下角度在 ±90° 内,防止翻转

            newX = max(min(newX, Float.pi / 2), -Float.pi / 2)

            cameraNode.eulerAngles = SCNVector3(newX, newY, 0)

        }

        if gesture.state == .ended {

            currentEulerAngles = cameraNode.eulerAngles

        }

    }

    // 图片翻转

    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

    }

}

参考文献:iOS 使用 SceneKit 实现全景图项目里碰到了展示全景图的需求,以前没做过,google了一下已经有不少现成的库 - 掘金

http://www.lryc.cn/news/576594.html

相关文章:

  • MCPA2APPT:基于 A2A+MCP+ADK 的多智能体流式并发高质量 PPT 智能生成系统
  • 微处理原理与应用篇---STM32寄存器控制GPIO
  • Unity2D 街机风太空射击游戏 学习记录 #16 道具父类提取 旋涡道具
  • FPGA内部资源介绍
  • Python爬虫实战:研究sanitize库相关技术
  • 笔记07:网表的输出与导入
  • SQL关键字三分钟入门:RANK() —— 窗口函数
  • Java AI 新纪元:Spring AI 与 Spring AI Alibaba 的崛起
  • JavaScript正则表达式之正向先行断言(Positive Lookahead)深度解析
  • 第8章-财务数据
  • 某音Web端消息体ProtoBuf结构解析
  • TCP 在高速网络下的大数据量传输优化:拥塞控制、效率保障与协议演进​
  • Linux更改国内镜像源
  • InnoDB的undo日志涉及的页结构
  • C语言二级指针与多级指针
  • 国内公司把数据湖做成了数据库
  • uni-app项目实战笔记27--uniapp搜索页面的实现
  • 手势-handpose的pipeline介绍
  • nt!IoSynchronousPageWrite函数分析之atapi!IdeReadWrite----非常重要
  • 视频序列中的帧间匹配技术 FrameMatcher 详解
  • 智能制造——56页2025 智慧工厂解决方案【附全文阅读】
  • zookeeper Curator(3):Watch事件监听
  • 从单体架构到微服务:微服务架构演进与实践
  • 从台式电脑硬件架构看前后端分离开发模式
  • Spring Boot 3 多数据源改造全流程:Druid、HikariCP 与 dynamic-datasource 实战总结
  • 内网横向-工作流
  • 典型工程应用三
  • [rootme:ctf all the day]Ubuntu 8.04week wp
  • python 项目利用uv管理python包依赖
  • phpstudy 可以按照mysql 数据库