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

在ARKit中用Swift为动态生成的多边形SCNGeometry添加UIImage材质

你猜的完全没错!动态生成的多边形之所以能正常显示纯色材质,却无法正确渲染图片材质,核心原因就是缺少UV纹理坐标的映射——纯色不需要知道每个顶点对应纹理的位置,但图片材质必须依赖UV坐标来确定纹理上的哪个点对应3D模型的顶点。当然可以动态生成UV映射,下面我来一步步帮你修改代码实现:

为什么需要UV坐标?

UV坐标是二维的(u, v),取值范围通常是0到1,对应纹理图片的左上角(0,0)到右下角(1,1)。它的作用是告诉SceneKit:3D模型的每个顶点,应该采样纹理图片的哪个位置的像素。没有UV坐标,SceneKit不知道如何把图片贴到多边形上,自然就无法正确显示。

动态生成UV坐标的实现

根据你的多边形形状,你可以选择不同的UV生成逻辑。下面给出一个通用的示例:假设我们把顶点的X/Z坐标归一化到0-1范围,作为UV坐标(如果是垂直平面,换成X/Y即可)。

修改后的完整代码如下:

// 假设你的vertices是[SCNVector3]类型
let source = SCNGeometrySource(vertices: vertices)

// --- 新增UV坐标生成部分 ---
// 先计算顶点X、Z轴的极值,用于坐标归一化
var minX = Float.infinity, maxX = -Float.infinity
var minZ = Float.infinity, maxZ = -Float.infinity
for vertex in vertices {
    minX = min(minX, vertex.x)
    maxX = max(maxX, vertex.x)
    minZ = min(minZ, vertex.z)
    maxZ = max(maxZ, vertex.z)
}
// 处理极端情况(所有顶点重合),避免除以0
let xRange = maxX - minX > 0 ? maxX - minX : 1
let zRange = maxZ - minZ > 0 ? maxZ - minZ : 1

// 为每个顶点生成UV坐标
var uvCoordinates = [CGPoint]()
for vertex in vertices {
    // 归一化X到0-1作为u,归一化Z到0-1作为v
    let u = CGFloat((vertex.x - minX) / xRange)
    let v = CGFloat((vertex.z - minZ) / zRange)
    uvCoordinates.append(CGPoint(x: u, y: v))
}
// 创建UV对应的Geometry Source
let uvSource = SCNGeometrySource(textureCoordinates: uvCoordinates)
// --- UV部分结束 ---

// 你的索引数据生成逻辑保持不变
var data = [Int32]()
for (index, _) in vertices.enumerated() {
    data.append(Int32(index))
}
data.insert(Int32(vertices.count), at: 0)
let indexData = Data(bytes: data, count: data.count * MemoryLayout<Int32>.size)
let element = SCNGeometryElement(data: indexData, primitiveType: .polygon, primitiveCount: 1, bytesPerIndex: MemoryLayout<Int32>.size)

// 现在把顶点Source和UV Source一起传入Geometry初始化
geometry = SCNGeometry(sources: [source, uvSource], elements: [element])

// 材质设置保持不变
if let material = geometry?.firstMaterial {
    material.diffuse.contents = UIImage(named: "Diffuse")
    material.isDoubleSided = true
    // 可选:调整纹理拉伸模式,默认是重复,也可以设为.clamp避免边缘重复
    // material.diffuse.wrapS = .clamp
    // material.diffuse.wrapT = .clamp
}

额外说明

  • 如果你的多边形是规则形状(比如三角形、四边形),也可以直接给每个顶点指定固定的UV值(比如[(0,0), (1,0), (1,1), (0,1)]),这样纹理映射会更精准。
  • UV坐标的范围不限于0-1,如果超出这个范围,SceneKit会根据材质的wrapS和wrapT属性决定是重复纹理还是拉伸边缘。

内容的提问来源于stack exchange,提问作者Heiko Plömer

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 04:17:10