S04E02:矩形平面网格生成

503 阅读2分钟

说明

一般情况下,在 3D 中创建一个平面几何体,都是由两个三角形组成一个四边形。对于普通的显示来说,这已经足够了,但有时候我们希望可以有更多的顶点,以便能完成一些变形动画。这时就需要创建细分的平面网格。

计算

首先,我们要知道水平和竖直的点的个数,计算这些点的坐标,并加入顶点数组中。其实顶点坐标的计算和 UV 是类似的,都是计算水平竖直的百分比,只是顶点需要再将百分比转为 -0.5~+0.5 范围并乘以对应宽高,而 UV 并不需要。

// x_v 为水平方向第几个点,vertices.0 为水平总点数,width 为物理宽度
meshPositions.append([
                    (Float(x_v) / Float(vertices.0 - 1) - 0.5) * width,
                    0,
                    (0.5 - Float(y_v) / Float(vertices.1 - 1)) * depth
                ])

接下来,是 UV 的计算。UV 坐标的范围是 0~1,这一点很好计算。

需要说明的是,Mesh 中 UV 的原点在左下角,x 轴向右为正, y 轴向上为正。而当我们使用 ModelDebugOptionsComponent(visualizationMode: .textureCoordinates) 对 UV 可视化时,会发现左上角为原点,y 轴向下为正。这可能是因为 RealityKit 为了兼容 UIKit 坐标做了 y 轴翻转,以便更好和 UIImage 的贴图对应起来。

最后,还有三角形索引 indices 的计算,它是用来说明,哪三个点构成一个三角形。我们可以一次添加 6 个点,也就是两个三角形,刚好构成一个四边形网格。注意三角形顶点的顺序,它们决定三角形的正面朝向哪里。

代码

extension MeshResource {
    /// Creates a new plane mesh with the specified values. 
    /// - Parameters:
    ///   - width: Width of the output plane
    ///   - depth: Depth of the output plane
    ///   - vertices: Vertex count in the x and z axis
    /// - Returns: A plane mesh
    public static func generateDetailedPlane(
        width: Float, depth: Float, vertices: (Int, Int)
    ) throws -> MeshResource {
        var descr = MeshDescriptor()
        var meshPositions: [SIMD3<Float>] = []
        var indices: [UInt32] = []
        var textureMap: [SIMD2<Float>] = []
        for x_v in 0..<(vertices.0) {
            let vertexCounts = meshPositions.count
            for y_v in 0..<(vertices.1) {
                meshPositions.append([
                    (Float(x_v) / Float(vertices.0 - 1) - 0.5) * width,
                    0,
                    (0.5 - Float(y_v) / Float(vertices.1 - 1)) * depth
                ])
                textureMap.append([Float(x_v) / Float(vertices.0 - 1), Float(y_v) / Float(vertices.1 - 1)])
                if x_v > 0 && y_v > 0 {
                    indices.append(
                        contentsOf: [
                            vertexCounts - vertices.1, vertexCounts, vertexCounts - vertices.1 + 1,
                            vertexCounts - vertices.1 + 1, vertexCounts, vertexCounts + 1
                        ].map { UInt32($0 + y_v - 1) })
                }
            }
        }
        descr.primitives = .triangles(indices)
        descr.positions = MeshBuffer(meshPositions)
        descr.textureCoordinates = MeshBuffers.TextureCoordinates(textureMap)
        return try .generate(from: [descr])
    }
}