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

基于RealityKit的墙面表面积检测技术实现问询

结合RealityKit与SwiftUI实现墙面测量与选中功能方案

入门学习路径

  • 优先啃Apple官方RealityKit文档,重点掌握AnchorEntity、ModelEntity、ARView核心类,以及ARWorldTrackingConfiguration的平面检测配置逻辑。
  • 观看WWDC相关Session:WWDC20的「RealityKit Essentials」、WWDC21的「Build apps with RealityKit 2」,这些是官方出品的实操指南,能快速建立AR开发的基础认知。
  • 先从最小Demo练手:先实现「在AR场景中放置3D模型」,再逐步添加平面检测、手势交互,最后过渡到距离测量和选中效果的开发。

可修改的核心功能示例代码

以下代码实现了基础的AR视图集成、手动放置角落标记、距离计算、选中标记变色的功能,你可以根据需求扩展:

import SwiftUI
import RealityKit
import ARKit

struct ARWallMeasuringView: View {
    @State private var arView = ARView(frame: .zero)
    @State private var placedMarkers: [AnchorEntity] = []
    @State private var selectedMarker: AnchorEntity?
    
    var body: some View {
        ZStack {
            ARViewWrapper(arView: $arView, markers: $placedMarkers, selectedMarker: $selectedMarker)
            VStack {
                Spacer()
                HStack(spacing: 40) {
                    Button(action: placeCornerMarker) {
                        Image(systemName: "plus.circle.fill")
                            .font(.system(size: 40))
                            .foregroundColor(.white)
                            .shadow(radius: 5)
                    }
                    Button(action: calculateWallArea) {
                        Image(systemName: "calculator.fill")
                            .font(.system(size: 40))
                            .foregroundColor(.white)
                            .shadow(radius: 5)
                    }
                }
                .padding(.bottom, 30)
            }
        }
    }
    
    // 放置角落标记点
    private func placeCornerMarker() {
        guard let raycastResult = arView.session.raycast(
            arView.makeRaycastQuery(from: arView.center, allowing: .estimatedPlane, alignment: .vertical)
        ).first else { return }
        
        let markerAnchor = AnchorEntity(raycastResult: raycastResult)
        let markerModel = ModelEntity(
            mesh: MeshResource.generateSphere(radius: 0.02),
            materials: [SimpleMaterial(color: .red, isMetallic: true)]
        )
        markerAnchor.addChild(markerModel)
        arView.scene.addAnchor(markerAnchor)
        placedMarkers.append(markerAnchor)
    }
    
    // 计算墙面表面积(简化版:取前两个标记点的距离作为宽度,从平面锚点获取高度)
    private func calculateWallArea() {
        guard placedMarkers.count >= 2 else {
            print("至少需要放置2个标记点")
            return
        }
        
        let marker1 = placedMarkers[0]
        let marker2 = placedMarkers[1]
        let width = simd_distance(marker1.position, marker2.position)
        
        guard let planeAnchor = marker1.anchor as? ARPlaneAnchor else {
            print("未检测到平面锚点")
            return
        }
        let height = planeAnchor.extent.y
        let area = width * height
        
        print("墙面表面积:\(String(format: "%.2f", area)) 平方米")
    }
}

// SwiftUI与ARView的桥接容器
struct ARViewWrapper: UIViewRepresentable {
    @Binding var arView: ARView
    @Binding var markers: [AnchorEntity]
    @Binding var selectedMarker: AnchorEntity?
    
    func makeUIView(context: Context) -> ARView {
        let config = ARWorldTrackingConfiguration()
        config.planeDetection = [.vertical]  // 重点检测垂直墙面
        arView.session.run(config)
        
        // 添加点击选中手势
        let tapGesture = UITapGestureRecognizer(target: context.coordinator, action: #selector(Coordinator.handleTap))
        arView.addGestureRecognizer(tapGesture)
        
        return arView
    }
    
    func updateUIView(_ uiView: ARView, context: Context) {}
    
    func makeCoordinator() -> Coordinator {
        Coordinator(self)
    }
    
    class Coordinator: NSObject {
        var parent: ARViewWrapper
        
        init(_ parent: ARViewWrapper) {
            self.parent = parent
        }
        
        @objc func handleTap(_ gesture: UITapGestureRecognizer) {
            let location = gesture.location(in: parent.arView)
            guard let hitEntity = parent.arView.entity(at: location) else { return }
            
            // 找到标记点对应的锚实体
            guard let markerAnchor = hitEntity.anchor as? AnchorEntity else { return }
            
            if parent.selectedMarker == markerAnchor {
                // 取消选中,恢复红色
                if let model = markerAnchor.children.first as? ModelEntity {
                    model.model?.materials = [SimpleMaterial(color: .red, isMetallic: true)]
                }
                parent.selectedMarker = nil
            } else {
                // 重置之前选中的标记点颜色
                if let prevSelected = parent.selectedMarker, let prevModel = prevSelected.children.first as? ModelEntity {
                    prevModel.model?.materials = [SimpleMaterial(color: .red, isMetallic: true)]
                }
                // 设置当前选中标记为蓝色
                if let model = markerAnchor.children.first as? ModelEntity {
                    model.model?.materials = [SimpleMaterial(color: .blue, isMetallic: true)]
                }
                parent.selectedMarker = markerAnchor
            }
        }
    }
}

功能优化方向

  1. 自动识别墙面角落:替代手动放置标记,通过ARPlaneAnchor的boundaryVertices属性获取墙面的四个角落坐标,自动生成标记点。
  2. 精准面积计算:扩展为支持四个标记点,计算矩形的长和宽,避免当前简化版的误差。
  3. 选中视觉增强:给选中的墙面添加蓝色轮廓(用OutlineMaterial),而不仅仅是标记点变色,更贴近文本选中的视觉效果。
  4. 错误提示:添加弹窗或Toast提示用户操作错误(比如未检测到墙面、标记点数量不足等),提升用户体验。

内容的提问来源于stack exchange,提问作者Thea Young

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.24 06:15:43