Hello David,
I’ve got a slide bar in the GUI for the object rotation which simply creates a rotation matrix that transforms the OpenGL geometry (red dragon) for each frame. There’s also a model selector in the GUI, which replaces the geometry with the new model (and also updates the acceleration structure of the OptiXRenderer). So OptiX is basically generating a screen space texture containing the shadow factors (by the way using the inverted projection and view matrix with large far clipping plane values for now, if you remember, as I had to make progress with the renderer for an upcoming paper) and in OpenGL, the shader is using this texture to shade the correct images.
However, since I have no clue (other than regenerating the acceleration structure with the geometry data that was already rotated by the amount given by the GUI on the CPU, which I do without the transformation every time I change the model (cube, dragon, tree) geometry) where to fill in the transformation in my modified tutorial code (the tutorial doesn’t cover transformations not at all, unfortunately) the images generated by OpenGL and OptiX diverge when the model rotation is changed. So far, to init the renderer, I generate the initial acceleration structure
OptixTraversableHandle cgbv::optix::OptixRenderer::build_accelleration_structure()
{
std::cout << "Building Acceleration Structure...";
for (auto& buffer : vertex_buffer)
buffer.free();
for (auto& buffer : normal_buffer)
buffer.free();
for (auto& buffer : index_buffer)
buffer.free();
vertex_buffer.resize(meshes.size());
normal_buffer.resize(meshes.size());
index_buffer.resize(meshes.size());
OptixTraversableHandle accel_structure_handle = 0ull;
// Triangle Inputs
// -----------------------------------------------------------
std::vector<OptixBuildInput> triangle_input(meshes.size());
std::vector<CUdeviceptr> device_vertices(meshes.size());
std::vector<CUdeviceptr> device_indices(meshes.size());
std::vector<uint32_t> triangle_input_flags(meshes.size());
for (int mesh_id = 0; mesh_id < static_cast<int>(meshes.size()); ++mesh_id)
{
auto& model = meshes[mesh_id];
vertex_buffer[mesh_id].alloc_and_upload(model.vertex);
index_buffer[mesh_id].alloc_and_upload(model.index);
if(!model.normal.empty())
normal_buffer[mesh_id].alloc_and_upload(model.normal);
triangle_input[mesh_id] = {};
triangle_input[mesh_id].type = OPTIX_BUILD_INPUT_TYPE_TRIANGLES;
device_vertices[mesh_id] = vertex_buffer[mesh_id].get_device_pointer();
device_indices[mesh_id] = index_buffer[mesh_id].get_device_pointer();
triangle_input[mesh_id].triangleArray.vertexFormat = OPTIX_VERTEX_FORMAT_FLOAT3;
triangle_input[mesh_id].triangleArray.vertexStrideInBytes = sizeof(glm::vec3);
triangle_input[mesh_id].triangleArray.numVertices = static_cast<int>(model.vertex.size());
triangle_input[mesh_id].triangleArray.vertexBuffers = &device_vertices[mesh_id];
triangle_input[mesh_id].triangleArray.indexFormat = OPTIX_INDICES_FORMAT_UNSIGNED_INT3;
triangle_input[mesh_id].triangleArray.indexStrideInBytes = sizeof(glm::ivec3);
triangle_input[mesh_id].triangleArray.numIndexTriplets = static_cast<int>(model.index.size());
triangle_input[mesh_id].triangleArray.indexBuffer = device_indices[mesh_id];
triangle_input_flags[mesh_id] = 0;
triangle_input[mesh_id].triangleArray.flags = &triangle_input_flags[mesh_id];
triangle_input[mesh_id].triangleArray.numSbtRecords = 1;
triangle_input[mesh_id].triangleArray.sbtIndexOffsetBuffer = 0;
triangle_input[mesh_id].triangleArray.sbtIndexOffsetSizeInBytes = 0;
triangle_input[mesh_id].triangleArray.sbtIndexOffsetStrideInBytes = 0;
}
// -----------------------------------------------------------
// BLAS setup
// -----------------------------------------------------------
OptixAccelBuildOptions accel_options = {};
accel_options.buildFlags = OPTIX_BUILD_FLAG_NONE | OPTIX_BUILD_FLAG_ALLOW_COMPACTION;
accel_options.motionOptions.numKeys = 1;
accel_options.operation = OPTIX_BUILD_OPERATION_BUILD;
OptixAccelBufferSizes blas_buffer_sizes;
optix::error::check(optixAccelComputeMemoryUsage(optix.context, &accel_options, triangle_input.data(), static_cast<int>(meshes.size()), &blas_buffer_sizes));
// -----------------------------------------------------------
// Prepare Compaction
// -----------------------------------------------------------
CUDABuffer compacted_size_buffer;
compacted_size_buffer.alloc(sizeof(uint64_t));
OptixAccelEmitDesc emit_descriptor;
emit_descriptor.type = OPTIX_PROPERTY_TYPE_COMPACTED_SIZE;
emit_descriptor.result = compacted_size_buffer.get_device_pointer();
// -----------------------------------------------------------
// execuite build (main stage)
// -----------------------------------------------------------
CUDABuffer temp_buffer;
temp_buffer.alloc(blas_buffer_sizes.tempSizeInBytes);
CUDABuffer output_buffer;
output_buffer.alloc(blas_buffer_sizes.outputSizeInBytes);
optix::error::check(optixAccelBuild(optix.context, 0, &accel_options, triangle_input.data(), static_cast<int>(meshes.size()), temp_buffer.get_device_pointer(), temp_buffer.get_size_in_bytes(), output_buffer.get_device_pointer(), output_buffer.get_size_in_bytes(), &accel_structure_handle, &emit_descriptor, 1));
cuda::error::cuda_sync_check();
// -----------------------------------------------------------
// perform compaction
// -----------------------------------------------------------
uint64_t compacted_size;
compacted_size_buffer.download(&compacted_size, 1);
//acceleration_structure_buffer.alloc(compacted_size);
acceleration_structure_buffer.resize(compacted_size);
optix::error::check(optixAccelCompact(optix.context, 0, accel_structure_handle, acceleration_structure_buffer.get_device_pointer(), acceleration_structure_buffer.get_size_in_bytes(), &accel_structure_handle));
cuda::error::cuda_sync_check();
// -----------------------------------------------------------
// Clean Up
// -----------------------------------------------------------
output_buffer.free();
temp_buffer.free();
compacted_size_buffer.free();
// -----------------------------------------------------------
meshes_touched = false;
std::cout << "done" << std::endl;
return accel_structure_handle;
}
and then I generate the Shader Binary Table
void cgbv::optix::OptixRenderer::build_shader_binary_table()
{
std::cout << "Building Shader Binary Table...";
// Raygen Records
// ----------------------------------------------------------------------------------
std::vector<optix::RaygenRecord> raygen_records;
for (int i = 0; i < optix.raygen_pg.size(); ++i)
{
optix::RaygenRecord record;
optix::error::check(optixSbtRecordPackHeader(optix.raygen_pg[i], &record));
record.data = nullptr;
raygen_records.push_back(record);
}
optix.raygen_records_buffer.alloc_and_upload(raygen_records);
optix.shader_binding_table.raygenRecord = optix.raygen_records_buffer.get_device_pointer();
// ----------------------------------------------------------------------------------
// Miss Records
// ----------------------------------------------------------------------------------
std::vector<optix::MissRecord> miss_records;
for (int i = 0; i < optix.miss_pg.size(); ++i)
{
optix::MissRecord record;
optix::error::check(optixSbtRecordPackHeader(optix.miss_pg[i], &record));
record.data = nullptr;
miss_records.push_back(record);
}
optix.miss_records_buffer.alloc_and_upload(miss_records);
optix.shader_binding_table.missRecordBase = optix.miss_records_buffer.get_device_pointer();
optix.shader_binding_table.missRecordStrideInBytes = sizeof(optix::MissRecord);
optix.shader_binding_table.missRecordCount = static_cast<int>(miss_records.size());
// ----------------------------------------------------------------------------------
// Hitgroup Records
// ----------------------------------------------------------------------------------
update_hitgroup_pg_for_sbt();
// ----------------------------------------------------------------------------------
std::cout << "done" << std::endl;
}
and to update the hitgroup programme groups I call
void cgbv::optix::OptixRenderer::update_hitgroup_pg_for_sbt()
{
if (optix.hitgroup_records_buffer.get_device_pointer())
optix.hitgroup_records_buffer.free();
int num_objects = static_cast<int>(meshes.size());
std::vector<optix::HitgroupRecord> hitgroup_records;
for (int mesh_id = 0; mesh_id < num_objects; ++mesh_id)
{
for (int ray_id = 0; ray_id < static_cast<int>(optix::RayType::Count); ++ray_id)
{
optix::HitgroupRecord record;
// all meshes use the same code, so all same hit group
optix::error::check(optixSbtRecordPackHeader(optix.hitgroup_pg[ray_id], &record));
record.data.vertex = reinterpret_cast<glm::vec3*>(vertex_buffer[mesh_id].get_device_pointer());
record.data.normal = reinterpret_cast<glm::vec3*>(normal_buffer[mesh_id].get_device_pointer());
record.data.index = reinterpret_cast<glm::ivec3*>(index_buffer[mesh_id].get_device_pointer());
record.data.colour = meshes[mesh_id].colour;
hitgroup_records.push_back(record);
}
}
optix.hitgroup_records_buffer.alloc_and_upload(hitgroup_records);
optix.shader_binding_table.hitgroupRecordBase = optix.hitgroup_records_buffer.get_device_pointer();
optix.shader_binding_table.hitgroupRecordStrideInBytes = sizeof(optix::HitgroupRecord);
optix.shader_binding_table.hitgroupRecordCount = static_cast<int>(hitgroup_records.size());
}
So, when I change the model geometry (to cube, tree or back to dragon), I basically call
void cgbv::optix::OptixRenderer::update_geometry_structure()
{
launch_params.traversable = build_accelleration_structure();
update_hitgroup_pg_for_sbt();
}
and create everything new from scratch (there was a warning that the acceleration structure may degenerate quickly when frequently changed). So, from the first glimpse at the links you provided, I probably have to change quite a bit there, eh?
Regarding optixMotionGeometry, I’m using Win11, Driver is 528.02, GPU is a 3090 and the OptiX Version is 7.6.0
Thank you very much for your help!
Markus