I’m trying to implement a cosine density PDF, similar to what Peter Shirley does in his “The Rest of Your Life” book. Considering we can’t use virtual functions with OptiX, I tried to use callable programs:
RT_CALLABLE_PROGRAM void cosine_build(onb &uvw, float3 normal){
uvw.build_from_w(vec3f(normal));
}
RT_CALLABLE_PROGRAM float cosine_value(onb &uvw, float3 direction) {
float cosine = dot(unit_vector(vec3f(direction)), uvw.w);
if(cosine > 0.f)
return cosine / CUDART_PI_F;
else
return 0.f;
}
RT_CALLABLE_PROGRAM float3 cosine_generate(onb &uvw, DRand48 &rnd) {
return uvw.local(random_cosine_direction(rnd)).as_float3();
}
I call these programs from the Raygen program, as follows:
// PDF callable programs
rtDeclareVariable(rtCallableProgramId<void(onb, float3)>, cosine_build, , );
rtDeclareVariable(rtCallableProgramId<float(onb, float3)>, cosine_value, , );
rtDeclareVariable(rtCallableProgramId<float3(onb, DRand48)>, cosine_generate, , );
// ...
onb uvw;
cosine_build(uvw, prd.out.normal.as_float3());
float pdf_val = cosine_value(uvw, prd.out.scattered_direction.as_float3());
float3 pdf_dir = cosine_generate(uvw, rnd);
And assign them in the host side like this:
virtual void assignTo(optix::Context &g_context, optix::Program &program) const override {
optix::Program build = g_context->createProgramFromPTXString(embedded_cosine_pdf_programs, "cosine_build");
optix::Program value = g_context->createProgramFromPTXString(embedded_cosine_pdf_programs, "cosine_value");
optix::Program generate = g_context->createProgramFromPTXString(embedded_cosine_pdf_programs, "cosine_generate");
program["cosine_build"]->setProgramId(build);
program["cosine_value"]->setProgramId(value);
program["cosine_generate"]->setProgramId(generate);
}
where “program” is the Raygen program variable. The issue is, the result is incorrect and completely different than what I expected:
I tried to copy paste the contents of the callable programs into inlined device functions, like the following, as a test.
inline __device__ void cosine_build(onb &uvw, float3 normal){
uvw.build_from_w(vec3f(normal));
}
inline __device__ float cosine_value(onb &uvw, float3 direction) {
float cosine = dot(unit_vector(vec3f(direction)), uvw.w);
if(cosine > 0.f)
return cosine / CUDART_PI_F;
else
return 0.f;
}
inline __device__ float3 cosine_generate(onb &uvw, DRand48 &rnd) {
return uvw.local(random_cosine_direction(rnd)).as_float3();
}
I didn’t change how these functions are called from the raygen program, or anything else in the project. To my surprise, it worked as initially expected:
My question is, why exactly am I seeing this behavior? The functions’ content is fundamentally the same as the callable programs, so why exactly is the result completely different?
I’m currently using CUDA 10 and OptiX 5.1 on a Windows 10 machine.