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

时间:2017-06-14 13:52:24

标签: c++ nvidia mesh assimp optix

我正在尝试将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个网格)会产生以下结果:enter image description here

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

我认为问题在于创建几何体,或许我有这些线错:

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,因为它给了我一个可怕的错误结果,如下所示): enter image description here

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

1 个答案:

答案 0 :(得分:2)

我找到了一个解决方案,虽然不确定它是多么优化。

每个网格仍然获得自己的几何体,但是不是创建在所有几何体之间共享的单个顶点,索引和常规缓冲区,而是为每个几何体创建单独的缓冲区。

然后,而不是

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);

结果: enter image description here