当前位置: 首页 > 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了一下已经有不少现成的库 - 掘金

相关文章:

  • 华为云Flexus+DeepSeek征文 | 华为云ModelArts Studio实战指南:创建高效的AingDesk知识库问答助手
  • HarmonyOS NEXT仓颉开发语言实战案例:图片预览器
  • PyQtNode Editor 第三篇创建节点(节点的定义)
  • 圆周石墨密封流体温度场MATLAB分析(微分求积法求解二维能量方程)
  • python中学物理实验模拟:瞬间推力与摩擦力作用下的物体运动
  • AC-DC-AC间接变频电源设计方案(工频50Hz→20KHz)
  • 【大模型】Query 改写常见Prompt 模板
  • Idea 项目远程开发 Remote Development
  • git使用详解和示例
  • 数字孪生技术引领UI前端设计新革命:实时交互与模拟预测
  • 使用cmake+vs2022编译win环境下grpc(不建议拉取最新版本grpc(注意本文时间是2025/6/28))
  • 【软考--软件设计师】10.2 关系型数据库
  • 实变与泛函题解-心得笔记【16】
  • Dask心得与笔记【2】
  • 《卷积神经网络到Vision Transformer:计算机视觉的十年架构革命》
  • LeetCode--38.外观数列
  • docker部署后端服务的脚本
  • 华为交换机SSH登录报错--Key exchange failed.
  • Java-Scanner类
  • 深入解析Java 内部类