说明
有时我们需要圆形的平面网格,并且需要控制贴图的形状,本文讲述如何生成一个圆形的平面。
几何
其实简单地说,圆形网格就是将方形网格进行变形而已:将原来按 x, y 方向水平竖直的网格,变成了沿径向(半径方向 0~R)、切向(环向角度 0~360)排列而已。
这样一来,贴图的方向也有了两种选择:
- 将贴图 UV 方向也按径向、切向扭曲排列。如下图左侧;
- 将贴图 UV 按实际坐标位置线性排列。如下图右侧;
代码
public static func generateCirclePlane(radius: Float, angularResolution: Int, radialResolution: Int, circleUV: Bool = true) throws -> MeshResource {
var descr = MeshDescriptor()
var meshPositions: [SIMD3<Float>] = []
var indices: [UInt32] = []
var textureMap: [SIMD2<Float>] = []
let radial = radialResolution > 0 ? radialResolution : 1
let angular = angularResolution > 2 ? angularResolution : 3;
let radialf = Float(radial)
let angularf = Float(angular)
let radialInc = radius / radialf
let angularInc = (2.0 * Float.pi) / angularf
let perLoop = angular + 1
for r in 0...radial {
let rf = Float(r)
let rad = rf * radialInc
let rFactor = rf / radialf
for a in 0...angular {
let af = Float(a)
let angle = af * angularInc
let ca = cos(angle)
let sa = sin(angle)
meshPositions.append(SIMD3<Float>(rad * ca, 0, rad * sa))
if circleUV {
textureMap.append(SIMD2<Float>(rFactor, 1 - af / angularf))
} else {
textureMap.append(SIMD2<Float>((ca*rFactor)/2+0.5, 0.5-(sa*rFactor)/2))
}
if (r != radial && a != angular) {
let index = UInt32(a + r * perLoop)
let tl = index
let tr = tl + 1
let bl = index + UInt32(perLoop)
let br = bl + 1
indices.append(contentsOf: [tr,bl,tl,
br,bl,tr])
}
}
}
descr.primitives = .triangles(indices)
descr.positions = MeshBuffer(meshPositions)
descr.textureCoordinates = MeshBuffers.TextureCoordinates(textureMap)
return try .generate(from: [descr])
}
补充
可能会有人注意到,在圆形的中心,会有很多个点重叠在一起。那么这些重叠的点,能不能合并成一个点呢?
其实判断能不能合并的规则很简单:除了位置相同,它们的法线和 UV 是否也相同,如果都相同则可以合并,只要有一个不同就不适合合并。
如果使用默认的环形贴图模式,显示 UV 坐标并不相同,所以无法简单合并。