Трассировка лучей с использованием Nvidia Optix с открытой библиотекой импорта активов (assimp) — рендеринг нескольких сеток

Я пытаюсь объединить универсальность 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 сетки) дает следующий результат: введите здесь описание изображения

Вы можете видеть, что визуализируется только часть второго меша (тело карлика). Я пробовал рендерить каждый меш отдельно, по одному, и они рендерились полностью. Но при соединении их я получаю это.

Я думаю, что проблема либо с созданием геометрии, возможно, я ошибаюсь в этих строках:

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 с открытой библиотекой импорта активов для рендеринга файлов с несколькими сетками?


person Al5678    schedule 14.06.2017    source источник


Ответы (1)


Я нашел решение, хотя не уверен, насколько оптимальное.

Каждый меш по-прежнему получает свою собственную геометрию, однако вместо создания отдельных буферов вершин, индексов и нормалей, которые являются общими для всех геометрий, я создаю отдельные буферы для каждой геометрии.

Тогда вместо

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

Результат: введите здесь описание изображения

person Al5678    schedule 22.06.2017