说明
所谓圆环体,就是类似手镯一样的环体,也就是类似于甜甜圈的物体🍩
几何
圆环体有两个参数:大半径和小半径,我们还是将两个半径分别进行旋转,构造出纵横网格,然后把这些点连接起来,组成网格体。
如上图所示,我们可以很容易根据大半径得到点 B 的坐标,但是如何计算点 CDFG 组成的小圆呢?我们这里选择以切线方式进行计算:对点 B 的坐标进行求导,就可以得到点 B 处的切线向量,然后再把向量 BC 绕切线进行不同角度的旋转,就能得到点 DFG 的坐标。
也就是,首先我们要求出圆环的中心点的坐标,这个坐标只和大半径及其旋转角度相关 let center = SIMD3<Float>(cos(slice) * majorRadius, 0, sin(slice) * majorRadius) ,我们对其中的slice变量进行求导得到 let tangent = SIMD3<Float>(-sin(slice) * majorRadius, 0, cos(slice) * majorRadius)
我们希望得到的切线向量是归一化的向量,所以直接忽略掉大半径,就得到了长度为 1 的切线向量 tangentN = SIMD3<Float>(-sin(slice), 0, cos(slice))
导数小知识:cos'(x) = -sin(x), sin'(x) = cos(x)
接下来我们将该点处的水平法线(水平法线可以直接由中心点的坐标得到)进行旋转,就得到了真正的法线,最后我们将大半径上的中心点沿法线进行移动,就得到了圆环上的点。
代码
public static func generateTorus(minorRadius: Float, majorRadius: Float, minorResolution :Int = 24, majorResolution: Int = 24) throws -> MeshResource {
var descr = MeshDescriptor()
var meshPositions: [SIMD3<Float>] = []
var indices: [UInt32] = []
var normals: [SIMD3<Float>] = []
var textureMap: [SIMD2<Float>] = []
let slices = majorResolution > 2 ? majorResolution : 3
let angular = minorResolution > 2 ? minorResolution : 3
let slicesf = Float(slices)
let angularf = Float(angular)
let limit = Float.pi * 2.0
let sliceInc = limit / slicesf
let angularInc = limit / angularf
let perLoop = angular + 1
for s in 0...slices {
let sf = Float(s)
let slice = sf * sliceInc
let cosSlice = cos(slice)
let sinSlice = sin(slice)
let centerX = cosSlice * majorRadius
let centerZ = sinSlice * majorRadius
let center = SIMD3<Float>(centerX, 0, centerZ)
let tangentN = SIMD3<Float>(-sinSlice, 0, cosSlice)
let vectorN = SIMD3<Float>(cosSlice, 0, sinSlice)
for a in 0...angular {
let af = Float(a)
let angle = af * angularInc
let normal = simd_act(simd_quatf(angle: angle, axis: tangentN), vectorN)
meshPositions.append(center + normal * minorRadius)
normals.append(normal)
textureMap.append(SIMD2<Float>(af / angularf, sf / slicesf))
if (s != slices && a != angular) {
let index = a + s * perLoop
let tl = UInt32(index)
let tr = tl + 1
let bl = UInt32(index + perLoop)
let br = bl + 1
indices.append(contentsOf: [
tl, tr, bl,
tr, br, bl
])
}
}
}
descr.positions = MeshBuffers.Positions(meshPositions)
descr.normals = MeshBuffers.Normals(normals)
descr.textureCoordinates = MeshBuffers.TextureCoordinates(textureMap)
descr.primitives = .triangles(indices)
return try MeshResource.generate(from: [descr])
}