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

如何在RealityKit中为检测平面添加重复纹理而非拉伸纹理?

解决ARKit平面检测中图片重复平铺的问题

要实现图片在检测到的平面上重复平铺,而非拉伸填充,需要从纹理采样模式配置和UV坐标调整两方面入手,以下是具体实现方案:

1. 创建支持重复采样的纹理采样器

默认纹理采样模式为.clampToEdge(拉伸填充),我们需要自定义MTLSamplerState,将S/T轴的采样模式设为.repeat,让纹理超出UV范围时重复显示:

private func createRepeatSampler() -> MTLSamplerState {
    let samplerDescriptor = MTLSamplerDescriptor()
    samplerDescriptor.addressModeS = .repeat // 横向重复
    samplerDescriptor.addressModeT = .repeat // 纵向重复
    samplerDescriptor.minFilter = .linear
    samplerDescriptor.magFilter = .linear
    
    guard let device = MTLCreateSystemDefaultDevice(),
          let samplerState = device.makeSamplerState(descriptor: samplerDescriptor) else {
        fatalError("无法创建重复采样器")
    }
    return samplerState
}

2. 配置材质并应用重复采样器

修改原有的材质创建逻辑,将自定义采样器赋值给材质,同时加载目标纹理:

fileprivate func didSetup() {
    self.planeGeometry = .generatePlane(width: planeAnchor.extent.x,
                                        depth: planeAnchor.extent.z)
    
    // 加载目标纹理
    guard let texture = try? TextureResource.load(named: "white_dot.png") else {
        fatalError("无法加载纹理文件")
    }
    
    // 创建并应用重复采样器
    let repeatSampler = createRepeatSampler()
    var material = SimpleMaterial()
    material.color = .init(tint: .white.withAlphaComponent(0.5), texture: texture)
    material.sampler = repeatSampler
    
    // 设置纹理重复密度(示例:每米重复4次)
    let repeatPerMeter: Float = 4.0
    updatePlaneUVs(width: planeAnchor.extent.x, depth: planeAnchor.extent.z, repeatPerMeter: repeatPerMeter)
    
    let model = ModelEntity(mesh: planeGeometry, materials: [material])
    model.position = [planeAnchor.center.x, 0, planeAnchor.center.z]
    self.addChild(model)
}

3. 调整平面网格的UV坐标

默认平面的UV范围是(0,0)到(1,1),无论平面多大都会拉伸纹理。我们需要根据平面尺寸和重复密度,缩放UV坐标范围,让纹理自然重复:

private func updatePlaneUVs(width: Float, depth: Float, repeatPerMeter: Float) {
    guard var meshDescriptor = planeGeometry?.generateMeshDescriptor() else { return }
    
    // 获取原UV数据并缩放
    var uvData = meshDescriptor.textureCoordinates[0].data as! [SIMD2<Float>]
    let uvScaleX = width * repeatPerMeter
    let uvScaleZ = depth * repeatPerMeter
    
    for i in 0..<uvData.count {
        uvData[i].x *= uvScaleX
        uvData[i].y *= uvScaleZ
    }
    
    // 更新网格UV数据并重新生成几何
    meshDescriptor.textureCoordinates[0] = .init(data: uvData)
    planeGeometry = .generate(from: meshDescriptor)
}

4. 平面更新时同步调整UV

在didUpdate方法中,当平面尺寸变化时,重新计算UV坐标,保持平铺效果:

fileprivate func didUpdate() {
    guard let planeGeometry = self.planeGeometry else { return }
    
    // 更新平面尺寸
    planeGeometry.width = planeAnchor.extent.x
    planeGeometry.depth = planeAnchor.extent.z
    
    // 重新调整UV
    let repeatPerMeter: Float = 4.0
    updatePlaneUVs(width: planeAnchor.extent.x, depth: planeAnchor.extent.z, repeatPerMeter: repeatPerMeter)
    
    // 更新模型位置
    if let model = children.first as? ModelEntity {
        model.position = [planeAnchor.center.x, 0, planeAnchor.center.z]
    }
}

关键说明

  • MTLSamplerAddressMode.repeat:控制纹理在UV超出0-1范围时重复显示,而非拉伸边缘。
  • UV坐标缩放:必须配合采样器的重复模式使用,否则即使采样器设为repeat,UV范围仍为0-1,纹理只会显示一次。通过缩放UV范围,可精准控制纹理的重复密度。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.26 22:42:49