You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在ARKit中旋转或缩放3D模型SCN文件?附渲染代码

在ARKit中对SCN格式3D模型实现旋转与缩放

Hey,看了你这段加载SCN模型的ARKit代码,想给模型加旋转和缩放功能其实挺简单的,咱们一步步来实现:

第一步:先获取目标模型的SCNNode

你现在直接把整个场景赋值给了sceneView,但要操作具体的模型,得先拿到对应的SCNNode节点。修改你的viewDidLoad方法,加载场景后定位到模型节点:

override func viewDidLoad() {
    super.viewDidLoad()
    
    // 加载SCN场景,增加空值判断避免崩溃
    guard let scene = SCNScene(named: "duck.scn", inDirectory: "models.scnassets/furniture") else {
        fatalError("无法加载模型文件,请检查路径或文件名是否正确")
    }
    sceneView.scene = scene
    
    // 获取模型节点(这里的"duck"要和你SCN文件里的节点名称完全一致)
    guard let duckNode = scene.rootNode.childNode(withName: "duck", recursively: true) else {
        fatalError("找不到模型节点,请检查SCN文件中的节点名称")
    }
    // 可选:设置初始缩放,比如缩小到原尺寸的一半
    duckNode.scale = SCNVector3(0.5, 0.5, 0.5)
}

第二步:实现缩放操作

有两种常见方式:代码直接设置固定缩放比例,或者通过捏合手势让用户交互缩放。

方式1:代码直接设置缩放

直接修改节点的scale属性,SCNVector3的三个值分别对应X/Y/Z轴的缩放比例,比如:

// 缩小到原尺寸的一半
duckNode.scale = SCNVector3(0.5, 0.5, 0.5)
// 放大到原尺寸的2倍
duckNode.scale = SCNVector3(2.0, 2.0, 2.0)

方式2:捏合手势实现交互缩放

给sceneView添加捏合手势识别器,让用户可以通过双指捏合来缩放模型:

首先在viewDidLoad里添加手势:

// 添加捏合手势
let pinchGesture = UIPinchGestureRecognizer(target: self, action: #selector(handlePinch(_:)))
sceneView.addGestureRecognizer(pinchGesture)

然后实现手势处理方法:

@objc private func handlePinch(_ gesture: UIPinchGestureRecognizer) {
    guard let duckNode = sceneView.scene.rootNode.childNode(withName: "duck", recursively: true) else { return }
    
    // 根据手势的缩放比例调整节点尺寸
    let scaleFactor = gesture.scale
    duckNode.scale = SCNVector3(
        duckNode.scale.x * scaleFactor,
        duckNode.scale.y * scaleFactor,
        duckNode.scale.z * scaleFactor
    )
    // 重置手势的scale值,避免累积导致缩放速度失控
    gesture.scale = 1.0
}

第三步:实现旋转操作

同样有两种方式:代码固定旋转角度,或者通过旋转手势让用户交互控制。

方式1:代码直接设置旋转

可以通过eulerAngles(欧拉角)或rotation属性来设置旋转,单位都是弧度(比如π/2对应90度):

// 方式1:欧拉角绕Y轴旋转90度(Y轴通常是垂直方向)
duckNode.eulerAngles.y += .pi/2

// 方式2:rotation属性,格式为SCNVector4(x轴分量, y轴分量, z轴分量, 旋转弧度)
// 下面的代码也是绕Y轴旋转90度
duckNode.rotation = SCNVector4(0, 1, 0, .pi/2)

方式2:旋转手势实现交互旋转

给sceneView添加旋转手势识别器,用户可以通过双指旋转来控制模型方向:

先在viewDidLoad里添加手势:

// 添加旋转手势
let rotationGesture = UIRotationGestureRecognizer(target: self, action: #selector(handleRotation(_:)))
sceneView.addGestureRecognizer(rotationGesture)

然后实现手势处理方法:

@objc private func handleRotation(_ gesture: UIRotationGestureRecognizer) {
    guard let duckNode = sceneView.scene.rootNode.childNode(withName: "duck", recursively: true) else { return }
    
    // 绕Y轴旋转,若需要绕X或Z轴,修改eulerAngles的x或z属性即可
    duckNode.eulerAngles.y += gesture.rotation
    // 重置手势的rotation值,避免累积导致旋转速度失控
    gesture.rotation = 0
}

完整整合后的代码

把上面的功能整合到你的SimpleViewController里,完整代码如下:

import UIKit
import ARKit

class SimpleViewController: UIViewController {
    @IBOutlet var sceneView: ARSCNView!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        // 加载SCN场景
        guard let scene = SCNScene(named: "duck.scn", inDirectory: "models.scnassets/furniture") else {
            fatalError("无法加载模型文件,请检查路径是否正确")
        }
        sceneView.scene = scene
        
        // 初始化模型节点(可选:设置初始缩放)
        if let duckNode = scene.rootNode.childNode(withName: "duck", recursively: true) {
            duckNode.scale = SCNVector3(0.5, 0.5, 0.5)
        }
        
        // 添加所有手势识别器
        setupGestureRecognizers()
    }
    
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        let configuration = ARWorldTrackingConfiguration()
        sceneView.session.run(configuration)
    }
    
    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        sceneView.session.pause()
    }
    
    private func setupGestureRecognizers() {
        // 捏合缩放手势
        let pinchGesture = UIPinchGestureRecognizer(target: self, action: #selector(handlePinch(_:)))
        sceneView.addGestureRecognizer(pinchGesture)
        
        // 旋转手势
        let rotationGesture = UIRotationGestureRecognizer(target: self, action: #selector(handleRotation(_:)))
        sceneView.addGestureRecognizer(rotationGesture)
    }
    
    @objc private func handlePinch(_ gesture: UIPinchGestureRecognizer) {
        guard let duckNode = sceneView.scene.rootNode.childNode(withName: "duck", recursively: true) else { return }
        
        let scaleFactor = gesture.scale
        duckNode.scale = SCNVector3(
            duckNode.scale.x * scaleFactor,
            duckNode.scale.y * scaleFactor,
            duckNode.scale.z * scaleFactor
        )
        gesture.scale = 1.0
    }
    
    @objc private func handleRotation(_ gesture: UIRotationGestureRecognizer) {
        guard let duckNode = sceneView.scene.rootNode.childNode(withName: "duck", recursively: true) else { return }
        
        duckNode.eulerAngles.y += gesture.rotation
        gesture.rotation = 0
    }
}

几个注意点

  • 确保你获取节点时用的名称(比如*"duck"*)和SCN文件里的节点名称完全一致,不然会找不到节点。你可以在Xcode的Scene编辑器里查看节点的名称。
  • 如果你的模型由多个子节点组成,建议把所有子节点放到一个父节点下,这样操作父节点就能统一控制整个模型的缩放和旋转。
  • 手势处理里一定要重置gesture.scale和gesture.rotation,不然每次手势触发都会累积之前的数值,导致缩放/旋转速度越来越快,体验很差。

内容的提问来源于stack exchange,提问作者S.M_Emamian

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.25 07:28:54