如何在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
相关产品推荐
相关产品推荐

