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

