如何在RealityKit中绘制可灵活调整尺寸的3D圆环?
在RealityKit中生成可自定义参数的矩形截面3D圆环
方案一:手动构建自定义网格(推荐,完全可控)
这种方式直接在RealityKit中生成顶点数据构建网格,能完全适配从数米到百米的尺寸范围,且参数调整灵活,不会出现缩放失真问题。
核心思路
通过计算矩形截面沿环形路径扫掠后的顶点、法线和索引数据,生成自定义MeshResource,再封装为ModelEntity加入场景。可调整的核心参数包括:
circleRadius:圆环中心到矩形截面中心的距离sectionWidth:矩形截面的径向宽度sectionHeight:矩形截面的轴向高度segmentCount:圆环分段数(数值越大,模型越平滑)
代码实现
import RealityKit func createRectangularTorus(circleRadius: Float, sectionWidth: Float, sectionHeight: Float, segmentCount: Int) -> MeshResource { var vertices: [MeshResource.Position] = [] var normals: [MeshResource.Normal] = [] var indices: [UInt32] = [] let halfWidth = sectionWidth / 2 let halfHeight = sectionHeight / 2 let angleStep = 2 * Float.pi / Float(segmentCount) // 遍历每个分段生成顶点与法线 for i in 0..<segmentCount { let angle = Float(i) * angleStep let nextAngle = Float(i + 1) * angleStep // 当前分段的8个顶点(顶面4个+底面4个) let p0 = SIMD3<Float>((circleRadius + halfWidth) * cos(angle), halfHeight, (circleRadius + halfWidth) * sin(angle)) let p1 = SIMD3<Float>((circleRadius - halfWidth) * cos(angle), halfHeight, (circleRadius - halfWidth) * sin(angle)) let p2 = SIMD3<Float>((circleRadius - halfWidth) * cos(nextAngle), halfHeight, (circleRadius - halfWidth) * sin(nextAngle)) let p3 = SIMD3<Float>((circleRadius + halfWidth) * cos(nextAngle), halfHeight, (circleRadius + halfWidth) * sin(nextAngle)) let p4 = SIMD3<Float>(p0.x, -halfHeight, p0.z) let p5 = SIMD3<Float>(p1.x, -halfHeight, p1.z) let p6 = SIMD3<Float>(p2.x, -halfHeight, p2.z) let p7 = SIMD3<Float>(p3.x, -halfHeight, p3.z) vertices.append(contentsOf: [p0, p1, p2, p3, p4, p5, p6, p7]) // 生成法线(径向、顶面、底面) let radialNormal0 = normalize(SIMD3<Float>(p0.x, 0, p0.z)) let radialNormal1 = normalize(SIMD3<Float>(p1.x, 0, p1.z)) let topNormal = SIMD3<Float>(0, 1, 0) let bottomNormal = SIMD3<Float>(0, -1, 0) normals.append(contentsOf: [ radialNormal0, radialNormal1, radialNormal1, radialNormal0, topNormal, topNormal, topNormal, topNormal, bottomNormal, bottomNormal, bottomNormal, bottomNormal, radialNormal0, radialNormal1, radialNormal1, radialNormal0 ]) // 生成三角面索引 let baseIndex = UInt32(i * 8) // 顶面三角面 indices.append(contentsOf: [baseIndex, baseIndex+1, baseIndex+2, baseIndex+2, baseIndex+3, baseIndex]) // 底面三角面 indices.append(contentsOf: [baseIndex+4, baseIndex+7, baseIndex+6, baseIndex+6, baseIndex+5, baseIndex+4]) // 外侧三角面 indices.append(contentsOf: [baseIndex, baseIndex+3, baseIndex+7, baseIndex+7, baseIndex+4, baseIndex]) // 内侧三角面 indices.append(contentsOf: [baseIndex+1, baseIndex+5, baseIndex+6, baseIndex+6, baseIndex+2, baseIndex+1]) } // 构建MeshDescriptor并生成MeshResource let descriptor = MeshDescriptor() descriptor.positions = MeshBuffers.Positions(vertices) descriptor.normals = MeshBuffers.Normals(normals) descriptor.primitives = .triangles(indices) return try! MeshResource.generate(from: [descriptor]) }
使用示例
// 创建圆环实体 let torusMesh = createRectangularTorus( circleRadius: 50.0, // 50米半径的圆环 sectionWidth: 3.0, // 3米宽的截面 sectionHeight: 1.5, // 1.5米高的截面 segmentCount: 64 // 64分段保证平滑度 ) let torusEntity = ModelEntity(mesh: torusMesh, materials: [SimpleMaterial(color: .darkGray, isMetallic: true)]) // 添加到RealityKit场景(与现有USDZ模型共存) arView.scene.addAnchor(torusEntity)
方案二:SceneKit网格转RealityKit(备选)
若你熟悉SceneKit的路径拉伸逻辑,可先通过SCNGeometry生成模型,再转换为RealityKit兼容的MeshResource,适合快速原型开发。
代码实现
import SceneKit import RealityKit // 1. 用SceneKit生成矩形截面环形网格 let profilePath = UIBezierPath(rect: CGRect(x: -1.5, y: -0.75, width: 3, height: 1.5)) // 3x1.5米的矩形截面 let sweepPath = UIBezierPath(arcCenter: .zero, radius: 50, startAngle: 0, endAngle: 2 * .pi, clockwise: true) // 50米半径的环形路径 let scnGeometry = SCNGeometry(path: sweepPath, profile: profilePath) // 2. 转换为RealityKit的MeshResource guard let scnData = scnGeometry.data() else { fatalError("Failed to extract SCNGeometry data") } let realityMesh = try! MeshResource(data: scnData, options: nil) // 3. 创建实体并加入场景 let torusEntity = ModelEntity(mesh: realityMesh, materials: [SimpleMaterial(color: .blue, isMetallic: false)]) arView.scene.addAnchor(torusEntity)
注意事项
- 转换后的网格性能略低于手动生成的自定义网格,频繁调整参数时推荐用方案一
- 若需要碰撞检测,可给
ModelEntity添加CollisionComponent,直接使用生成的MeshResource作为碰撞形状
内容的提问来源于stack exchange,提问作者lobax
相关产品推荐
相关产品推荐

