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

如何用代码在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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.20 10:13:19