如何在RealityKit中通过MeshDescriptor实现网格实例化

编程语言 2026-07-11

我正在尝试在RealityKit中实现网格实例化——在一个绘制调用中将同一个网格渲染多次(可能的情况)。

为了简化起见,我在Reality Composer Pro创建了一个立方体。

我尝试使用 MeshInstancesComponent —— 它确实能生成合并后的网格 —— 但因为阴影投射出现了一些奇怪的问题而没有奏效。而 MeshDescriptor,则没有这个阴影问题。

因此,为了访问底层顶点数据,我有以下代码,是改自 这里:

extension MeshResource.Contents {
    func forEachVertex(_ callback: (SIMD3<Float>, UInt32?, SIMD2<Float>?, SIMD3<Float>, SIMD3<Float>, Int, SIMD3<Float>) -> Void) {
        for instance in self.instances {
            guard let model = self.models[instance.model] else { continue }
            let instanceToModel = instance.transform
            for part in model.parts {
                var vertexes: [SIMD4<Float>] = []
                var indices: [UInt32] = []
                var texCoords: [SIMD2<Float>] = []
                var norms: [SIMD4<Float>] = []
                var tangents: [SIMD4<Float>] = []
                var bitangents: [SIMD4<Float>] = []
                var materialIndexes = [Int]()

                for position in part.positions {
                    vertexes.append(instanceToModel * SIMD4<Float>(position, 1.0))
                }

                materialIndexes.append(part.materialIndex)

                if let allIndices = part.triangleIndices {
                    for index in allIndices {
                        indices.append(index)
                    }
                }

                if let allTex = part.textureCoordinates {
                    for coord in allTex {
                        texCoords.append(coord)
                    }
                }

                if let n = part.normals {
                    for norm in n {
                        norms.append(SIMD4<Float>(norm, 1.0))
                    }
                }

                if let t = part.tangents {
                    for tan in t {
                        tangents.append(SIMD4<Float>(tan, 1.0))
                    }
                }


                if let b = part.bitangents {
                    for tan in b {
                        bitangents.append(SIMD4<Float>(tan, 1.0))
                    }
                }


                for i in 0..<vertexes.count {
                    let v = vertexes[i]
                    callback([v.x, v.y, v.z], indices[i], texCoords[i], [norms[i].x, norms[i].y, norms[i].z], [tangents[i].x, tangents[i].y, tangents[i].z], materialIndexes[0], [bitangents[i].x, bitangents[i].y, bitangents[i].z])
                }
            }
        }
    }
}

然后,我为每个网格创建一个单独的 MeshDescriptor

var descriptors = [MeshDescriptor]()

        for i in 0..<createdTrees.count {
            var d = MeshDescriptor()
            var positions = [SIMD3<Float>]()
            var normals = [SIMD3<Float>]()
            var indexes = [UInt32]()
            var textureCoords = [SIMD2<Float>]()
            var tangents = [SIMD3<Float>]()
            var biTangents = [SIMD3<Float>]()
            var material_Index: Int = 0

createdTrees[i]!.components[ModelComponent.self]!.mesh.contents.forEachVertex { position, index, textureCoordinate, normal, tangent, materialIndex, bitangent in
                positions.append(position)
                normals.append(normal)
                indexes.append(index!)
                textureCoords.append(textureCoordinate!)
                tangents.append(tangent)
                material_Index = materialIndex
                biTangents.append(bitangent)
            }

            d.positions = MeshBuffers.Positions.init(positions)
            d.textureCoordinates = MeshBuffers.TextureCoordinates.init(textureCoords)
            d.normals = MeshBuffers.Normals.init(normals)
            d.tangents = MeshBuffers.Tangents.init(tangents)
            d.bitangents = MeshBuffers.Tangents.init(biTangents)
            d.primitives = .triangles(indexes)
            d.materials = .allFaces(UInt32(material_Index))

            descriptors.append(d)
        }

最后,我用 MeshResource,并通过 MeshDescriptor 数组来生成:

do {
                let combinedMesh = try MeshResource.generate(from: descriptors)
                let material = createdTrees[0]!.components[ModelComponent.self]!.materials[0]

                let comboEntity = ModelEntity(mesh: combinedMesh, materials: [material])

                comboEntity.position = createdTrees[0]!.position

                comboEntity.move(to: Transform(pitch: -90.0 * .pi / 180.0), relativeTo: comboEntity)

                self.gameScene?.addChild(comboEntity)

            } catch {
                print(error.localizedDescription)
            }

结果:只创建了一个实例——但其他的都不存在。并且有些面可见,有些则不可见。

我已经尝试过:翻转法线(将每个法线分量乘以 -1.0),以及颠倒顶点数据数组。

问题:我可以做哪些修改,以实现对 MeshDescriptor 的网格实例化?

解决方案

好的,我再次尝试了 MeshInstancesComponent,解决了此前在阴影投射方面遇到的问题。原来其实是材质/纹理的问题——从 PhysicallyBasedMaterial 迁移到着色器图材质后,所有实例的阴影都变得良好。

以下是我的代码——改自 这里

let count: Int = self.positions.count
let instanceData = try? LowLevelInstanceData(instanceCount: count)
let mesh = referenceEntity.components[ModelComponent.self]!.mesh
let modelID = mesh.contents.models.first?.id

instanceData?.withMutableTransforms {
    for i in 0..<count {
       let transform = Transform(
           scale: [1.0, 1.0, 1.0],
           translation: [self.positions[i].x, self.positions[i].y, self.positions[i].z])

        $0[i] = transform.matrix
    }
}

if let data = instanceData {
   do {
       let component = try MeshInstancesComponent(mesh: mesh, modelID: modelID, instances: data)

        referenceEntity.components.set(component)   
   } catch {

   }
}

注:虽然我尚未验证对绘制数量的影响,但我注意到使用这种实例化策略后,CPU使用显著下降。

站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章