Metal中程序化设置纹理坐标导致非对称四边形纹理变形问题
非矩形四边形纹理坐标映射变形解决方案
我在项目中通过程序化方式创建顶点,填充到以下Vertex结构体:
struct Vertex{ var position: SIMD3<Float> var color: SIMD4<Float> var textCoors: SIMD2<Float> }
我的对象是由两个三角形组成的简单平面,通过textureRip函数生成纹理坐标(参数pI为平面索引,l为平面的角点索引)。该函数在处理正方形或矩形平面时能实现纹理无缝贴合,但当四边形角点坐标不对称时(正方形、矩形是四边形的特例),会出现纹理变形。我不想采用将平面拆分为多个小方块的方法,希望找到更优解决方案。
原textureRip函数代码:
func textureRip(pI: Int, l: Int, vertex: [[SIMD3<Float>]]) -> SIMD2<Float> { var textCoor0 = SIMD2<Float>() // We have this ratio because metalview coordinate system 2x2 unit size // from -1 to 1 for both x and y. Texture coordinate sistem is 1 unit wide and // unit height. (0 to 1). So metal view twice as much bigger. let ratio:Float = 2 // We are adding 1 to metal view values because its values begins // from -1 so we are shifting its coordinate system to texture coordinate // system along wiht ratio. let x0 = (vertex[pI][0].x + 1) / ratio let x = (vertex[pI][1].x + 1) / ratio // We are using "1 -" because texture coordinate sistem origin is // at the top left corner and metal view coordinate system at center. // in that case texture look upside down so we are turning it using "1-". let y0 = 1 - (vertex[pI][0].y + 1) / ratio let y = 1 - (vertex[pI][2].y + 1) / ratio if l == 0 { textCoor0 = SIMD2<Float>(x0, y0) } else if l == 1 { textCoor0 = SIMD2<Float>(x, y0) } else if l == 2 { textCoor0 = SIMD2<Float>(x, y) } else { textCoor0 = SIMD2<Float>(x0, y) } return textCoor0 }
问题根源
原函数基于轴对齐的X/Y坐标计算纹理UV,仅适用于矩形平面。当四边形为任意形状时,这种轴对齐的映射会强制将非正交的四边形顶点对应到正交的纹理坐标系,必然导致拉伸变形。
优化方案:基于局部坐标系的纹理映射
为每个四边形建立独立的局部UV坐标系,通过顶点间的向量投影计算纹理坐标,适配任意平面四边形的映射需求。
修改后的函数代码:
func textureRip(pI: Int, l: Int, vertex: [[SIMD3<Float>]]) -> SIMD2<Float> { let quadVertices = vertex[pI] guard quadVertices.count >= 4 else { return .zero } // 取四边形的三个顶点定义局部UV基准 let v0 = quadVertices[0] let v1 = quadVertices[1] let v2 = quadVertices[2] // 计算局部U/V轴向量(对应纹理的水平/垂直方向) let uAxis = v1 - v0 let vAxis = v2 - v0 // 计算轴向量的模长平方,用于投影归一化 let uLengthSq = dot(uAxis, uAxis) let vLengthSq = dot(vAxis, vAxis) // 处理当前角点的向量偏移 let currentVertex = quadVertices[l] let delta = currentVertex - v0 // 将偏移向量投影到U/V轴,得到归一化的UV值 let u = dot(delta, uAxis) / uLengthSq let v = dot(delta, vAxis) / vLengthSq // 适配Metal纹理坐标系:翻转Y轴(纹理原点在左上角) let textureV = 1.0 - v return SIMD2<Float>(u, textureV) }
方案说明
- 局部坐标系构建:通过四边形的前三个顶点定义U/V轴,确保纹理方向与四边形的边对齐。
- 向量投影计算:将每个顶点相对于基准顶点的偏移,投影到U/V轴上,得到的UV值能自然贴合四边形的形状。
- 坐标系适配:保留原函数的Y轴翻转逻辑,适配Metal的纹理坐标系要求。
- 兼容性:对于矩形平面,该函数的计算结果与原函数一致;对于任意平面四边形,能避免纹理拉伸变形。
内容的提问来源于stack exchange,提问作者Hope
相关产品推荐
相关产品推荐

