27、增强现实中形状外观修改与交互实现

增强现实中形状外观修改与交互实现

1. 修改形状外观

1.1 基本方法概述

修改形状外观有多种方式,包括改变光照、透明度和纹理。光照会根据光源类型和位置使形状呈现不同效果;透明度决定形状是实心还是透明;纹理则是在形状表面应用图形图像,例如让形状看起来像由砖块或沙子构成。通过这些修改,能让形状更具视觉吸引力。

1.2 应用纹理

1.2.1 创建项目

创建一个新的增强现实应用项目,命名为 ARAppearance,并确保内容技术使用 SceneKit。

1.2.2 准备纹理图像

在互联网上搜索“公共领域纹理图像”,下载以 .png 或 .jpg 格式存储的纹理图像,然后将其拖放到导航窗格中。

1.2.3 修改代码

修改 ViewController.swift 文件,代码如下:

import UIKit
import SceneKit
import ARKit
class ViewController: UIViewController, ARSCNViewDelegate {
    @IBOutlet var sceneView: ARSCNView!
    override func viewDidLoad() {
        super.viewDidLoad()
        // Set the view's delegate
        sceneView.delegate = self
        // Show statistics such as fps and timing information
        sceneView.showsStatistics = true
        sceneView.debugOptions = [ARSCNDebugOptions.showWorldOrigin, 
        ARSCNDebugOptions.showFeaturePoints]
        let box = SCNBox(width: 0.1, height: 0.1, length: 0.2, 
        chamferRadius: 0.01)
        let node = SCNNode()
        let material = SCNMaterial()
        material.diffuse.contents = UIImage(named: "stone.jpg")
        box.materials = [material]
        node.geometry = box
        node.position = SCNVector3(0, 0, -0.3)
        sceneView.scene.rootNode.addChildNode(node)
    }
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        // Create a session configuration
        let configuration = ARWorldTrackingConfiguration()
        // Run the view's session
        sceneView.session.run(configuration)
    }
    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        // Pause the view's session
        sceneView.session.pause()
    }
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Release any cached data, images, etc that aren't in use.
    }
    // MARK: - ARSCNViewDelegate
    /*
    // Override to create and configure nodes for anchors added to the 
    view's session.
    func renderer(_ renderer: SCNSceneRenderer, nodeFor anchor: ARAnchor) 
    -> SCNNode? {
        let node = SCNNode()
        return node
    }
    */
    func session(_ session: ARSession, didFailWithError error: Error) {
        // Present an error message to the user
    }
    func sessionWasInterrupted(_ session: ARSession) {
        // Inform the user that the session has been interrupted, for 
        example, by presenting an overlay
    }
    func sessionInterruptionEnded(_ session: ARSession) {
        // Reset tracking and/or remove existing anchors if consistent 
        tracking is required
    }
}

这段代码定义了一个 SCNBox 几何形状和一个 SCNMaterial 数组,将“stone.jpg”图形文件作为其第一个材质,使形状周围呈现石头图像。

1.3 改变透明度

在 viewDidLoad 函数中添加一行代码来定义透明度值,使整个 viewDidLoad 函数如下:

override func viewDidLoad() {
    super.viewDidLoad()
    // Set the view's delegate
    sceneView.delegate = self
    // Show statistics such as fps and timing information
    sceneView.showsStatistics = true
    sceneView.debugOptions = [ARSCNDebugOptions.showWorldOrigin, 
    ARSCNDebugOptions.showFeaturePoints]
    let box = SCNBox(width: 0.1, height: 0.1, length: 0.2, 
    chamferRadius: 0.01)
    let node = SCNNode()
    let material = SCNMaterial()
    material.diffuse.contents = UIImage(named: "stone.jpg")
    material.transparency = 0.7
    box.materials = [material]
    node.geometry = box
    node.position = SCNVector3(0, 0, -0.3)
    sceneView.scene.rootNode.addChildNode(node)
}

上述代码在材质数组中定义了两个特性:一是在盒子周围显示“stone.jpg”图像,二是将透明度定义为 0.7,使盒子呈现半透明效果。

1.4 改变光照

1.4.1 创建光源步骤

创建光源需要以下步骤:
1. 定义一个 SCNLight 对象。
2. 定义 SCNLight 类型。
3. 将 SCNLight 对象分配给一个 SCNNode。
4. 定义 SCNNode 的位置。
5. 将 SCNNode 添加到场景中。

1.4.2 代码示例
override func viewDidLoad() {
    super.viewDidLoad()
    // Set the view's delegate
    sceneView.delegate = self
    // Show statistics such as fps and timing information
    sceneView.showsStatistics = true
    sceneView.debugOptions = [ARSCNDebugOptions.showWorldOrigin, 
    ARSCNDebugOptions.showFeaturePoints]
    let box = SCNBox(width: 0.1, height: 0.1, length: 0.1, 
    chamferRadius: 0.01)
    let node = SCNNode()
    let material = SCNMaterial()
    // material.diffuse.contents = UIImage(named: "stone.jpg")
    // material.transparency = 0.7
    let spotLight = SCNLight()
    spotLight.type = .directional
    let spotNode = SCNNode()
    spotNode.light = spotLight
    spotNode.position = SCNVector3(0, 0.2, 0)
    material.diffuse.contents = UIColor.orange
    box.materials = [material]
    node.geometry = box
    node.position = SCNVector3(0, 0, -0.3)
    sceneView.scene.rootNode.addChildNode(node)
    sceneView.scene.rootNode.addChildNode(spotNode)
}

运行此项目,会创建一个橙色盒子,光源位于原点上方 0.2 米处,由于光照类型为定向光,它只会照亮盒子的正面。

1.4.3 不同光照类型效果

若将光照类型从定向光改为泛光(omni),代码如下:

//spotLight.type = .directional  // illuminates only the front of the box
spotLight.type = .omni  // illuminates the front and top of the box

运行项目后,泛光会照亮盒子的正面和顶部。不同光照类型和光源位置会产生不同的视觉效果,可以通过实验来探索。

1.5 光照类型总结

光照类型 效果
ambient 环境光,均匀照亮场景
directional 定向光,只照亮特定方向
IES 基于 IES 文件的光照
probe 光照探针,用于反射和光照估计
spot 聚光灯,照亮特定区域

1.6 光照创建流程

graph LR
    A[定义 SCNLight 对象] --> B[定义 SCNLight 类型]
    B --> C[分配 SCNLight 对象到 SCNNode]
    C --> D[定义 SCNNode 位置]
    D --> E[添加 SCNNode 到场景]

2. 与增强现实对象交互

2.1 交互概述

在现实场景中显示虚拟对象很有趣,但我们通常希望做更多事情,比如让虚拟对象在屏幕上移动,并让用户通过触摸手势(如点击或滑动)与它们进行交互。这样可以使增强现实应用更具视觉吸引力和响应性。

2.2 创建项目

创建一个新的增强现实应用项目,命名为 ARGesture。该项目包含 AppDelegate.swift 文件、ViewController.swift 文件、Main.storyboard 文件,以及一个 art.scnassets 文件夹,其中包含 ship.scn 对象和 texture.png 文件。

2.3 准备工作

2.3.1 导入框架

在 ViewController.swift 文件顶部添加以下行以导入 GLKit 框架:

import GLKit

此时,ViewController.swift 文件应导入 GLKit、UIKit、SceneKit 和 ARKit 框架,共四条导入语句。

2.3.2 创建节点

创建一个 SCNNode 对象作为属性,以便在多个函数中访问,ViewController.swift 文件顶部应如下所示:

import UIKit
import SceneKit
import ARKit
import GLKit
class ViewController: UIViewController, ARSCNViewDelegate {
    @IBOutlet var sceneView: ARSCNView!
    let node = SCNNode()

2.4 创建几何形状并应用纹理

2.4.1 存储图形图像

为避免因文件名拼写错误或文件移动导致 Xcode 找不到文件,可将图形图像存储在 Assets.xcassets 文件夹中,并赋予描述性名称。具体操作步骤如下:
1. 点击导航窗格中的 Assets.xcassets 文件夹打开面板。
2. 点击面板左下角的 + 图标,选择“New Image Set”。
3. 点击 AppIcon 集下的 Image 并按 Return 键,输入描述性名称“Texture”并按 Return 键。
4. 将 texture.png 从导航窗格拖放到 1x 虚线框中。

2.4.2 定义几何形状和应用纹理

在 viewDidLoad 函数中添加以下代码:

override func viewDidLoad() {
    super.viewDidLoad()
    // Set the view's delegate
    sceneView.delegate = self
    // Show statistics such as fps and timing information
    sceneView.showsStatistics = true
    sceneView.debugOptions = [ARSCNDebugOptions.showWorldOrigin, 
    ARSCNDebugOptions.showFeaturePoints]
    node.geometry = SCNPyramid(width: 0.15, height: 0.2, length: 0.1)
    node.geometry?.firstMaterial?.diffuse.contents = imageLiteral(resourceName: "Texture")
    node.position = SCNVector3(0, -0.2, 0)
    sceneView.scene.rootNode.addChildNode(node)
}

上述代码定义了一个金字塔形状,并将 texture.png 图形图像应用到其表面。

2.5 处理触摸手势

2.5.1 添加手势识别器

在 viewDidLoad 函数中添加以下代码以检测右滑动手势:

let swipeGesture = UISwipeGestureRecognizer(target: self, action: #selector(handleSwipe))
sceneView.addGestureRecognizer(swipeGesture)

完整的 viewDidLoad 函数如下:

override func viewDidLoad() {
    super.viewDidLoad()
    // Set the view's delegate
    sceneView.delegate = self
    // Show statistics such as fps and timing information
    sceneView.showsStatistics = true
    sceneView.debugOptions = [ARSCNDebugOptions.showWorldOrigin, 
    ARSCNDebugOptions.showFeaturePoints]
    node.geometry = SCNPyramid(width: 0.15, height: 0.2, length: 0.1)
    node.geometry?.firstMaterial?.diffuse.contents = imageLiteral(resourceName: "Texture")
    let swipeGesture = UISwipeGestureRecognizer(target: self, action: #selector(handleSwipe))
    sceneView.addGestureRecognizer(swipeGesture)
    node.position = SCNVector3(0, -0.2, 0)
    sceneView.scene.rootNode.addChildNode(node)
}
2.5.2 实现手势处理函数

创建 handleSwipe 函数来处理右滑动手势:

@objc func handleSwipe(sender: UISwipeGestureRecognizer) {
    let swipeArea = sender.view as! SCNView
    let touchCoordinates = sender.location(in: swipeArea)
    let touchedShape = swipeArea.hitTest(touchCoordinates, options: nil)
    if (sender.direction == .right) && (touchedShape.isEmpty != true) {
        print ("Right swipe")
        let degrees: Float = 45
        let radians = GLKMathDegreesToRadians(degrees)
        let action = SCNAction.rotateBy(x: 0, y: CGFloat(radians), z: 0, duration: 5)
        node.runAction(action)
    }
}

该函数获取用户滑动的坐标,检查滑动手势是否为向右且在金字塔边界内。如果是,则将金字塔旋转 45 度,旋转持续 5 秒。

2.6 让对象持续旋转

如果希望金字塔持续旋转,可将 viewDidLoad 函数中的最后两行代码替换为:

let forever = SCNAction.repeatForever(action)
node.runAction(forever)

完整的 ViewController.swift 文件如下:

import UIKit
import SceneKit
import ARKit
import GLKit
class ViewController: UIViewController, ARSCNViewDelegate {
    @IBOutlet var sceneView: ARSCNView!
    let node = SCNNode()
    override func viewDidLoad() {
        super.viewDidLoad()
        // Set the view's delegate
        sceneView.delegate = self
        // Show statistics such as fps and timing information
        sceneView.showsStatistics = true
        sceneView.debugOptions = [ARSCNDebugOptions.showWorldOrigin, 
        ARSCNDebugOptions.showFeaturePoints]
        node.geometry = SCNPyramid(width: 0.15, height: 0.2, length: 0.1)
        node.geometry?.firstMaterial?.diffuse.contents = imageLiteral(resourceName: "Texture")
        let swipeGesture = UISwipeGestureRecognizer(target: self, action: #selector(handleSwipe))
        sceneView.addGestureRecognizer(swipeGesture)
        node.position = SCNVector3(0, -0.2, 0)
        sceneView.scene.rootNode.addChildNode(node)
    }
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        // Create a session configuration
        let configuration = ARWorldTrackingConfiguration()
        // Run the view's session
        sceneView.session.run(configuration)
    }
    @objc func handleSwipe(sender: UISwipeGestureRecognizer) {
        let swipeArea = sender.view as! SCNView
        let touchCoordinates = sender.location(in: swipeArea)
        let touchedShape = swipeArea.hitTest(touchCoordinates, options: nil)
        if (sender.direction == .right) && (touchedShape.isEmpty != true) {
            print ("Right swipe")
            let degrees: Float = 45
            let radians = GLKMathDegreesToRadians(degrees)
            let action = SCNAction.rotateBy(x: 0, y: CGFloat(radians), z: 0, duration: 5)
            //let forever = SCNAction.repeatForever(action)
            node.runAction(action)   //node.runAction(forever)
        }
    }
    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        // Pause the view's session
        sceneView.session.pause()
    }
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Release any cached data, images, etc that aren't in use.
    }
}

2.7 交互操作流程

graph LR
    A[创建项目] --> B[导入 GLKit 框架]
    B --> C[创建 SCNNode 对象]
    C --> D[存储图形图像]
    D --> E[定义几何形状并应用纹理]
    E --> F[添加手势识别器]
    F --> G[实现手势处理函数]
    G --> H[可选:让对象持续旋转]

2.8 交互功能总结

功能 实现步骤
创建项目 创建 ARGesture 项目,包含相关文件和资源
准备工作 导入 GLKit 框架,创建 SCNNode 对象
应用纹理 存储图形图像,定义几何形状并应用纹理
处理手势 添加手势识别器,实现手势处理函数
持续旋转 可选,使用 SCNAction.repeatForever 让对象持续旋转
【四旋翼无人机】具备螺旋桨倾斜机构的全驱动四旋翼无人机:建模控制研究(Matlab代码、Simulink仿真实现)内容概要:本文围绕具备螺旋桨倾斜机构的全驱动四旋翼无人机展开研究,重点探讨其系统建模控制策略,结合Matlab代码Simulink仿真实现。文章详细分析了无人机的动力学模型,特别是引入螺旋桨倾斜机构后带来的全驱动特性,使其在姿态位置控制上具备更强的机动性自由度。研究涵盖了非线性系统建模、控制器设计(如PID、MPC、非线性控制等)、仿真验证及动态响应分析,旨在提升无人机在复杂环境下的稳定性和控制精度。同时,文中提供的Matlab/Simulink资源便于读者复现实验并进一步优化控制算法。; 适合人群:具备一定控制理论基础和Matlab/Simulink仿真经验的研究生、科研人员及无人机控制系统开发工程师,尤其适合从事飞行器建模先进控制算法研究的专业人员。; 使用场景及目标:①用于全驱动四旋翼无人机的动力学建模仿真平台搭建;②研究先进控制算法(如模型预测控制、非线性控制)在无人机系统中的应用;③支持科研论文复现、课程设计或毕业课题开发,推动无人机高机动控制技术的研究进展。; 阅读建议:建议读者结合文档提供的Matlab代码Simulink模型,逐步实现建模控制算法,重点关注坐标系定义、力矩分配逻辑及控制闭环的设计细节,同时可通过修改参数和添加扰动来验证系统的鲁棒性适应性。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值