如何用代码在RealityKit中给3D模型叠加可自定义尺寸的非拉伸图片?
在RealityKit中实现自定义尺寸与位置的图片贴纸(类似Decal功能)
核心实现思路
放弃裁切模型的方案,直接创建独立的平面ModelEntity作为贴纸载体:
- 给平面设置带目标图片的材质,通过匹配平面尺寸与图片比例避免拉伸
- 调整平面的位置、缩放和旋转,让它精准贴合到模型的任意区域
- 将贴纸设为模型的子实体,跟随模型同步移动
代码示例
1. 创建带自定义图片的贴纸实体
import RealityKit import UIKit func createImageSticker(image: UIImage, size: SIMD3<Float>) -> ModelEntity { // 把UIImage转成RealityKit可识别的纹理资源 guard let textureResource = try? TextureResource.load([image]) else { fatalError("加载图片纹理失败") } // 配置材质,保证图片不拉伸 var material = SimpleMaterial() material.baseColor = .init(texture: .init(textureResource)) material.tintColor = .white.withAlphaComponent(1.0) material.isMetallic = false material.roughness = 1.0 // 创建指定尺寸的平面网格 let mesh = MeshResource.generatePlane(width: size.x, depth: size.z) let stickerEntity = ModelEntity(mesh: mesh, materials: [material]) // 调整贴纸朝向(默认Z轴向外,这里转90度让正面朝向相机方向,可按需修改) stickerEntity.transform.rotation = simd_quatf(angle: .pi / 2, axis: [1, 0, 0]) return stickerEntity }
2. 将贴纸绑定到目标模型的指定位置
func addStickerToModel(targetModel: Entity, sticker: ModelEntity, position: SIMD3<Float>, rotation: simd_quatf? = nil) { // 把贴纸设为模型的子实体,跟随模型移动 targetModel.addChild(sticker) // 设置贴纸在模型局部坐标系中的位置 sticker.transform.position = position // 可选:如果模型表面倾斜,自定义贴纸旋转角度贴合表面 if let customRotation = rotation { sticker.transform.rotation = customRotation } }
3. 完整使用场景(ARView中加载模型并添加贴纸)
class ViewController: UIViewController { @IBOutlet var arView: ARView! override func viewDidLoad() { super.viewDidLoad() // 加载目标模型(这里用内置立方体示例,替换成你的模型即可) let targetModel = ModelEntity(mesh: MeshResource.generateBox(size: 0.5)) let anchor = AnchorEntity(plane: .horizontal) anchor.addChild(targetModel) arView.scene.addAnchor(anchor) // 加载贴纸图片,创建指定尺寸的贴纸 guard let stickerImage = UIImage(named: "sticker.png") else { return } // 尺寸对应宽0.2米、高0.15米,尽量匹配图片宽高比避免拉伸 let stickerSize = SIMD3<Float>(0.2, 0, 0.15) let sticker = createImageSticker(image: stickerImage, size: stickerSize) // 将贴纸放在立方体正面中心(立方体中心在(0,0,0),正面Z轴方向,所以位置是(0, 0, 0.25)) let stickerPosition = SIMD3<Float>(0, 0, 0.25) addStickerToModel(targetModel: targetModel, sticker: sticker, position: stickerPosition) // 后续可随时调整贴纸属性 // 比如放大贴纸:sticker.transform.scale = SIMD3<Float>(1.2, 1, 1.2) // 比如移动位置:sticker.transform.position = SIMD3<Float>(0.1, 0.1, 0.25) } }
关键细节提示
- 避免图片拉伸:创建平面时,
width和depth的比例要和图片的宽高比一致;也可以通过material.baseColor.texture?.scale = SIMD2<Float>(0.5, 0.5)调整纹理缩放比例 - 贴合非平面区域:如果要贴在模型的曲面或斜面上,可以通过射线检测获取模型表面的法线,自动计算贴纸的旋转角度,实现精准贴合
- DecalComponent替代方案:RealityKit 2.0+支持
DecalComponent,但它依赖模型的UV映射,灵活性不如自定义平面实体,示例代码如下:
func addDecalToModel(targetModel: ModelEntity, image: UIImage) { guard let texture = try? TextureResource.load([image]) else { return } var material = SimpleMaterial() material.baseColor = .init(texture: .init(texture)) let decal = DecalComponent(materials: [material], size: SIMD2<Float>(0.2, 0.15)) targetModel.components.set(decal) // 可通过调整targetModel的transform或DecalComponent的offset改变贴花位置 }
内容的提问来源于stack exchange,提问作者user26604425
相关产品推荐
相关产品推荐

