使用 Nvidia Optix 和 Open Asset Import Library (assimp) 进行光线追踪 - 渲染多个网格

Ray Tracing using Nvidia Optix with Open Asset Import Library (assimp) - rendering multiple meshes

我正在尝试将 Open Asset Import Library(读取各种 3D 模型文件类型)的多功能性与 NVidia Optix 光线追踪相结合来渲染模型。

到目前为止,只要我渲染的模型由单个网格组成,它就可以工作。当我尝试渲染一个包含多个网格的文件时,我只能得到部分结果。我无法缩小问题所在,寻找一些见解。相关代码在这里:

正在使用 assimp 导入器加载文件并创建 optix 缓冲区:

int loadAsset(const char* path)
{
Assimp::Importer importer;

scene = importer.ReadFile(
    path,
    aiProcess_Triangulate
    //| aiProcess_JoinIdenticalVertices
    | aiProcess_SortByPType
    | aiProcess_ValidateDataStructure
    | aiProcess_SplitLargeMeshes
    | aiProcess_FixInfacingNormals
    );

if (scene) {
    getBoundingBox(&scene_min, &scene_max);
    scene_center.x = (scene_min.x + scene_max.x) / 2.0f;
    scene_center.y = (scene_min.y + scene_max.y) / 2.0f;
    scene_center.z = (scene_min.z + scene_max.z) / 2.0f;

    float3 optixMin = { scene_min.x, scene_min.y, scene_min.z };
    float3 optixMax = { scene_max.x, scene_max.y, scene_max.z };
    aabb.set(optixMin, optixMax);

    unsigned int numVerts = 0;
    unsigned int numFaces = 0;

    if (scene->mNumMeshes > 0) {    
        printf("Number of meshes: %d\n", scene->mNumMeshes);

        // get the running total number of vertices & faces for all meshes
        for (unsigned int i = 0; i < scene->mNumMeshes; i++) {
            numVerts += scene->mMeshes[i]->mNumVertices;
            numFaces += scene->mMeshes[i]->mNumFaces;
        }
        printf("Found %d Vertices and %d Faces\n", numVerts, numFaces);

        // set up buffers
        optix::Buffer vertices = context->createBuffer(RT_BUFFER_INPUT, RT_FORMAT_FLOAT3, numVerts);
        optix::Buffer normals = context->createBuffer(RT_BUFFER_INPUT, RT_FORMAT_FLOAT3, numVerts);
        optix::Buffer faces = context->createBuffer(RT_BUFFER_INPUT, RT_FORMAT_UNSIGNED_INT3, numFaces);
        optix::Buffer materials = context->createBuffer(RT_BUFFER_INPUT, RT_FORMAT_UNSIGNED_INT, numVerts);

        // unused buffer
        Buffer tbuffer = context->createBuffer(RT_BUFFER_INPUT, RT_FORMAT_FLOAT2, 0);

        // create material
        std::string defaultPtxPath = "C:\ProgramData\NVIDIA Corporation\OptiX SDK 4.1.0\SDK\build\lib\ptx\";
        Program phong_ch = context->createProgramFromPTXFile(defaultPtxPath + "optixPrimitiveIndexOffsets_generated_phong.cu.ptx", "closest_hit_radiance");
        Program phong_ah = context->createProgramFromPTXFile(defaultPtxPath + "optixPrimitiveIndexOffsets_generated_phong.cu.ptx", "any_hit_shadow");

        Material matl = context->createMaterial();
        matl->setClosestHitProgram(0, phong_ch);
        matl->setAnyHitProgram(1, phong_ah);
        matl["Kd"]->setFloat(0.7f, 0.7f, 0.7f);
        matl["Ka"]->setFloat(1.0f, 1.0f, 1.0f);
        matl["Kr"]->setFloat(0.0f, 0.0f, 0.0f);
        matl["phong_exp"]->setFloat(1.0f);

        std::string triangle_mesh_ptx_path(ptxPath("triangle_mesh.cu"));
        Program meshIntersectProgram = context->createProgramFromPTXFile(triangle_mesh_ptx_path, "mesh_intersect");
        Program meshBboxProgram = context->createProgramFromPTXFile(triangle_mesh_ptx_path, "mesh_bounds");

        optix::float3 *vertexMap = reinterpret_cast<optix::float3*>(vertices->map());
        optix::float3 *normalMap = reinterpret_cast<optix::float3*>(normals->map());
        optix::uint3 *faceMap = reinterpret_cast<optix::uint3*>(faces->map());
        unsigned int *materialsMap = static_cast<unsigned int*>(materials->map());

        context["vertex_buffer"]->setBuffer(vertices);
        context["normal_buffer"]->setBuffer(normals);
        context["index_buffer"]->setBuffer(faces);
        context["texcoord_buffer"]->setBuffer(tbuffer);
        context["material_buffer"]->setBuffer(materials);


        Group group = createSingleGeometryGroup(meshIntersectProgram, meshBboxProgram, vertexMap,
            normalMap, faceMap, materialsMap, matl);

        context["top_object"]->set(group);
        context["top_shadower"]->set(group);

        vertices->unmap();
        normals->unmap();
        faces->unmap();
        materials->unmap();
    }

    return 0;
}
return 1;
}

以及用于创建几何图形和填充缓冲区的相关函数:

Group createSingleGeometryGroup(Program meshIntersectProgram, Program meshBboxProgram, optix::float3 *vertexMap,
optix::float3 *normalMap, optix::uint3 *faceMap, unsigned int *materialsMap, Material matl) {

Group group = context->createGroup();
optix::Acceleration accel = context->createAcceleration("Trbvh");
group->setAcceleration(accel);
std::vector<GeometryInstance> gis;
unsigned int vertexOffset = 0u;
unsigned int faceOffset = 0u;

for (unsigned int m = 0; m < scene->mNumMeshes; m++) {
    aiMesh *mesh = scene->mMeshes[m];
    if (!mesh->HasPositions()) {
        throw std::runtime_error("Mesh contains zero vertex positions");
    }
    if (!mesh->HasNormals()) {
        throw std::runtime_error("Mesh contains zero vertex normals");
    }

    printf("Mesh #%d\n\tNumVertices: %d\n\tNumFaces: %d\n", m, mesh->mNumVertices, mesh->mNumFaces);

    // add points           
    for (unsigned int i = 0u; i < mesh->mNumVertices; i++) {
        aiVector3D pos = mesh->mVertices[i];
        aiVector3D norm = mesh->mNormals[i];

        vertexMap[i + vertexOffset] = optix::make_float3(pos.x, pos.y, pos.z) + aabb.center();
        normalMap[i + vertexOffset] = optix::normalize(optix::make_float3(norm.x, norm.y, norm.z));
        materialsMap[i + vertexOffset] = 0u;

    }

    // add faces
    for (unsigned int i = 0u; i < mesh->mNumFaces; i++) {
        aiFace face = mesh->mFaces[i];

        // add triangles
        if (face.mNumIndices == 3) {
            faceMap[i + faceOffset] = optix::make_uint3(face.mIndices[0], face.mIndices[1], face.mIndices[2]);
        }
        else {
            printf("face indices != 3\n");
            faceMap[i + faceOffset] = optix::make_uint3(-1);
        }
    }

    // create geometry
    optix::Geometry geometry = context->createGeometry();
    geometry->setPrimitiveCount(mesh->mNumFaces);
    geometry->setIntersectionProgram(meshIntersectProgram);
    geometry->setBoundingBoxProgram(meshBboxProgram);
    geometry->setPrimitiveIndexOffset(faceOffset);

    optix::GeometryInstance gi = context->createGeometryInstance(geometry, &matl, &matl + 1);
    gis.push_back(gi);

    vertexOffset += mesh->mNumVertices;
    faceOffset += mesh->mNumFaces;

}

printf("VertexOffset: %d\nFaceOffset: %d\n", vertexOffset, faceOffset);

// add all geometry instances to a geometry group
GeometryGroup gg = context->createGeometryGroup();
gg->setChildCount(static_cast<unsigned int>(gis.size()));
for (unsigned i = 0u; i < gis.size(); i++) {
    gg->setChild(i, gis[i]);
}
Acceleration a = context->createAcceleration("Trbvh");
gg->setAcceleration(a);

group->setChildCount(1);
group->setChild(0, gg);

return group;
}

运行 assimp 示例文件中的上述代码(使用 dwarf.x,文件包含 2 个网格)产生此结果:

您只能看到第二个网格(矮人的 body)的一部分被渲染。我尝试分别渲染每个网格,一次渲染一个,然后它们会完整渲染。但是当把它们放在一起时,我得到了这个。

我认为问题出在创建几何体上,也许我的这些行有误:

geometry->setPrimitiveCount(mesh->mNumFaces);
geometry->setPrimitiveIndexOffset(faceOffset);

或 assimp 后处理标志

    scene = importer.ReadFile(
    path,
    aiProcess_Triangulate
    //| aiProcess_JoinIdenticalVertices
    | aiProcess_SortByPType
    | aiProcess_ValidateDataStructure
    | aiProcess_SplitLargeMeshes
    | aiProcess_FixInfacingNormals
    );

(注意上面,我不得不注释掉 JoinIdenticalVertices 因为它给了我一个非常错误的结果,如下所示):

有没有人能够成功地将 nvidia optix 与开放资产导入库结合起来以渲染具有多个网格的文件?

我找到了一个解决方案,但不确定如何优化。

每个网格仍然有自己的几何体,但是我没有创建在所有几何体之间共享的单个顶点、索引和法线缓冲区,而是为每个几何体创建单独的缓冲区。

然后,而不是

context["vertex_buffer"]->setBuffer(vertices);
context["normal_buffer"]->setBuffer(normals);
context["index_buffer"]->setBuffer(faces);
context["texcoord_buffer"]->setBuffer(tbuffer);
context["material_buffer"]->setBuffer(materials);

我用

geometry["vertex_buffer"]->setBuffer(vertices);
geometry["normal_buffer"]->setBuffer(normals);
geometry["index_buffer"]->setBuffer(faces);
geometry["texcoord_buffer"]->setBuffer(tbuffer);
geometry["material_buffer"]->setBuffer(materials);

结果: