Dear all,
Could you help me to understand how exactly SRT transformation motion works in OptiX 7.4?
Consider the following example in which I build IAS->SRT->GAS, where SRT implements a translation along X at 0.25 (a simplified version of optixSimpleMotionBlur with the SRT applied to a triangle instead of a sphere):
// A single-triangle mesh
const std::array<float3, 3> vertices =
{ { { 1.0f, -1.0f, -1.0f }, { 0.0f, -1.0f, 1.0f }, { -1.0f, -1.0f, -1.0f } } };
CUdeviceptr d_vertices=0; // Copy the mesh to the device memory
...
// Build GAS, save handle to OptixTraversableHandle m_gas_handle
OptixAccelBuildOptions accelOptions = {};
accelOptions.buildFlags = OPTIX_BUILD_FLAG_ALLOW_RANDOM_VERTEX_ACCESS | OPTIX_BUILD_FLAG_ALLOW_COMPACTION;
...
// Prepare SRT motion transform as a parent node to GAS
OptixSRTData srt_data[2] =
{
//sx, a, b, pvx, sy, c, pvy, sz, pvz, qx, qy, qz, qw, tx, ty, tz
{1.f, 0.f, 0.f, 0.f, 1.f, 0.f, 0.f, 1.f, 0.f, 0.f, 0.f, 0.f, 1.f, 0.f, 0.f, 0.f},
{1.f, 0.f, 0.f, 0.f, 1.f, 0.f, 0.f, 1.f, 0.f, 0.f, 0.f, 0.f, 1.f, 0.25f, 0.f, 0.f}
};
OptixSRTMotionTransform motion_transform = {};
motion_transform.child = m_gas_handle; // GAS
motion_transform.motionOptions.numKeys = 2;
motion_transform.motionOptions.timeBegin = 0.f;
motion_transform.motionOptions.timeEnd = 1.f;
memcpy( motion_transform.srtData, srt_data, 2 * 16 * sizeof( float ) );
...
// Prepare IAS as a parent node to motion transform
const float static_transform[12] = {1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0 };
const size_t instance_size_in_bytes = sizeof( OptixInstance ) * 1;
OptixInstance optix_instances[ 1 ];
memset( optix_instances, 0, instance_size_in_bytes );
optix_instances[0].flags = OPTIX_INSTANCE_FLAG_NONE;
optix_instances[0].instanceId = 0;
optix_instances[0].sbtOffset = 0;
optix_instances[0].visibilityMask = 1;
optix_instances[0].traversableHandle = m_motion_transform_handle;
memcpy( optix_instances[0].transform, static_transform, sizeof( float ) * 12 );
// Copy instances to the device memory
CUdeviceptr d_instances;
...
// Build IAS
OptixBuildInput instance_input = {};
instance_input.type = OPTIX_BUILD_INPUT_TYPE_INSTANCES;
instance_input.instanceArray.instances = d_instances;
instance_input.instanceArray.numInstances = 1;
OptixAccelBuildOptions accel_options = {};
accel_options.buildFlags = OPTIX_BUILD_FLAG_NONE;
accel_options.operation = OPTIX_BUILD_OPERATION_BUILD;
...
I cast one ray with ray_time=1.0
and direction (0, 1, 0)
, and I want to find the intersection point in the closest hit program:
extern "C" __global__ void __closesthit__project()
{
float3 triangle[3];
optixGetTriangleVertexData(
optixGetGASTraversableHandle(),
optixGetPrimitiveIndex(),
optixGetSbtGASIndex(),
optixGetRayTime(),
&triangle[0]
);
const float2 barycentrics = optixGetTriangleBarycentrics();
float3 hit_point_bary = triangle[0]*(1-barycentrics.x-barycentrics.y) +
triangle[1]*barycentrics.x +
triangle[2]*barycentrics.y;
float3 hit_point_tmax = optixGetWorldRayOrigin() + scale(optixGetRayTmax(), optixGetWorldRayDirection());
// Print the triangle and the hit points
...
}
The printout for the triangle
variable is (1.0, -1.0, -1.0), (0.0, -1.0, 1.0), (-1.0, -1.0, -1.0)
, for the hit_point_bary
it’s (-0.25, -1.0, 0.0)
, and for the hit_point_tmax
it’s (0.0, -1.0, 0.0)
.
- Is it correct that
hit_point_bary
is in the object coordinates, whilehit_point_tmax
is in the world coordinates? - How do I get the hit point in world coordinates using the barycentric coordinates if the triangle is not transformed?
In programming guide, I read
The motion matrix transform traversable (OptixMatrixMotionTransform) transforms the ray during traversal using a motion matrix.
- How exactly is the ray being transformed? Does this imply that the barycentric coordinates are actually transformed?
I feel it’s a very basic question on motion, but still I fail to understand it…
Thanks a lot in advance!
Kind regards,
Pavel