欢迎您访问 最编程 本站为您分享编程语言代码,编程技术文章!
您现在的位置是: 首页

S04E07: 圆锥网格生成

最编程 2024-07-05 21:00:10
...

说明

3D 图形学中的圆锥体,就是由两个面组成的空腔,中间是没有实体的。

几何

圆锥体由两部分组成:底部的圆形与上面的锥形,而上面的锥形其实也是一个圆形,只是把靠近中心的点,高度坐标向上拉一下就形成了锥形。

同样,底部圆形的贴图方式也有两种:环绕式,平铺式。另外,由于圆锥体由两部分组成,有时候我们希望对这两部分进行不同的贴图,也就是 splitFaces ,这就要求我们在创建三角形索引时,同时指定一下三角形面对应的材质索引,最后在 MeshDescriptor 中指定 materialsMeshDescriptor.Materials.perFace 即可。

惟一有点麻烦的是,圆锥体斜面的法线需要另外计算,我们可以先计算出 x 正方向的法线,然后绕中轴线进行旋转就可以得到所有点的法线。同时注意到这个向量长度本身就是 1,无需进行归一化处理。 所以,再对这个法线向量进行旋转后的坐标为 (coneNormY * cosAngle, coneNormX, coneNormY * sinAngle) 这个旋转操作不影响向量长度,也无需归一化处理。

代码

public static func generateCone(radius: Float, height: Float, angularResolution: Int = 24, radialResolution: Int = 1, verticalResolution: Int = 1, splitFaces: Bool = false, circleUV: Bool = true) throws -> MeshResource {
        var descr = MeshDescriptor()
        var meshPositions: [SIMD3<Float>] = []
        var indices: [UInt32] = []
        var normals: [SIMD3<Float>] = []
        var textureMap: [SIMD2<Float>] = []
        var materials: [UInt32] = []
        
        let vertical = verticalResolution > 0 ? verticalResolution : 1
        let angular = angularResolution > 2 ? angularResolution : 3
        let radial = radialResolution > 0 ? radialResolution : 1

        let verticalf = Float(vertical)
        let angularf = Float(angular)
        let radialf = Float(radial)

        let angularInc = (2.0 * .pi) / angularf
        let verticalInc = height / verticalf
        let radialInc = radius / radialf
        let radiusInc = radius / verticalf

        let yOffset = -0.5 * height
        let perLoop = angular + 1
        let verticesPerWall = perLoop * (vertical + 1)
        
        let hyp = sqrtf(radius * radius + height * height)
        let coneNormX = radius / hyp
        let coneNormY = height / hyp
        
        for v in 0...vertical {
            let vf = Float(v)
            let y = yOffset + vf * verticalInc
            let rad = radius - vf * radiusInc
            
            for a in 0...angular {
                let af = Float(a)
                let angle = af * angularInc
                
                let cosAngle = cos(angle)
                let sinAngle = sin(angle)
                
                let x = rad * cosAngle
                let z = rad * sinAngle
                
                let coneBottomNormal: SIMD3<Float> = [coneNormY * cosAngle, coneNormX, coneNormY * sinAngle]
                
                meshPositions.append(SIMD3<Float>(x, y, z))
                normals.append(normalize(coneBottomNormal))
                textureMap.append(SIMD2<Float>(1 - af / angularf, vf / verticalf))
                
                if (v != vertical && a != angular) {
                    let index = a + v * perLoop

                    let tl = UInt32(index)
                    let tr = tl + 1
                    let bl = UInt32(index + perLoop)
                    let br = bl + 1

                    indices.append(contentsOf: [tl, bl, tr,
                                                tr, bl, br
                    ])
                }
            }
        }
        if splitFaces {
            materials.append(contentsOf: Array(repeating: 0, count: angular * vertical * 2))
        }
        
        for r in 0...radial {
            let rf = Float(r)
            let rad = rf * radialInc
            
            for a in 0...angular {
                let af = Float(a)
                let angle = af * angularInc
                let x = rad * cos(angle)
                let y = rad * sin(angle)
                
                meshPositions.append(SIMD3<Float>(x, -height * 0.5, y))
                normals.append(SIMD3<Float>(0, -1, 0))
                if circleUV {
                    textureMap.append(SIMD2<Float>(af / angularf, 1 - rf / radialf))
                } else {
                    textureMap.append(SIMD2<Float>(x/radius/2+0.5, y/radius/2+0.5))
                }
                
                if (r != radial && a != angular) {
                    let index = verticesPerWall + a + r * perLoop;

                    let tl = UInt32(index)
                    let tr = tl + 1
                    let bl = UInt32(index + perLoop)
                    let br = bl + 1

                    indices.append(contentsOf: [tl, bl, tr,
                                                tr, bl, br
                    ])
                }
            }
        }
        if splitFaces {
            materials.append(contentsOf: Array(repeating: 1, count: angular * radial * 2))
        }
        
        descr.positions = MeshBuffers.Positions(meshPositions)
        descr.normals = MeshBuffers.Normals(normals)
        descr.textureCoordinates = MeshBuffers.TextureCoordinates(textureMap)
        descr.primitives = .triangles(indices)
        if !materials.isEmpty {
            descr.materials = MeshDescriptor.Materials.perFace(materials)
        }
    return try MeshResource.generate(from: [descr])
}

推荐阅读