Adding curves in Optix_Apps


I am trying to include curves support in the Optix_Apps intro_runtime.

I am quite ok in loading .hair file but when I try to add built-in the intersection_program for cubic bspline with the optixBuiltinISModuleGet() function, I encounter the

error lnk2019 unresolved external symbol in optixBuiltinISModuleGet

here is a part of the code I use in the :

  std::string ptxClosesthit = readPTX("./intro_runtime_core/closesthit.ptx");
  std::string ptxAnyhit     = readPTX("./intro_runtime_core/anyhit.ptx");
  OptixModule moduleClosesthit;
  OptixModule moduleAnyhit;

  OPTIX_CHECK( m_api.optixModuleCreateFromPTX(m_context, &moduleCompileOptions, &pipelineCompileOptions, ptxClosesthit.c_str(), ptxClosesthit.size(), nullptr, nullptr, &moduleClosesthit) );
  OPTIX_CHECK( m_api.optixModuleCreateFromPTX(m_context, &moduleCompileOptions, &pipelineCompileOptions, ptxAnyhit.c_str(),     ptxAnyhit.size(),     nullptr, nullptr, &moduleAnyhit) );

  OptixProgramGroupDesc programGroupDescHitRadiance;

  memset(&programGroupDescHitRadiance,       0, sizeof(OptixProgramGroupDesc));
  // ADDED 
  const OptixModuleCompileOptions defaultOptions = { OPTIX_COMPILE_DEFAULT_MAX_REGISTER_COUNT,
  OptixModule cubicCurveModule;
  OptixBuiltinISOptions builtinISOptions = {};
  OPTIX_CHECK(optixBuiltinISModuleGet(m_context, &defaultOptions, &pipelineCompileOptions, &builtinISOptions, &cubicCurveModule)); // Here is the error
  programGroupDescHitRadiance.kind  = OPTIX_PROGRAM_GROUP_KIND_HITGROUP;
  programGroupDescHitRadiance.flags = OPTIX_PROGRAM_GROUP_FLAGS_NONE;
  programGroupDescHitRadiance.hitgroup.moduleCH            = moduleClosesthit;
  programGroupDescHitRadiance.hitgroup.entryFunctionNameCH = "__closesthit__radiance";
  programGroupDescHitRadiance.hitgroup.moduleIS = cubicCurveModule;
  programGroupDescHitRadiance.hitgroup.entryFunctionNameIS = 0;

  OptixProgramGroup programGroupHitRadiance;

  OPTIX_CHECK( m_api.optixProgramGroupCreate(m_context, &programGroupDescHitRadiance,       1, &programGroupOptions, nullptr, nullptr, &programGroupHitRadiance      ) );

could you help me with that please ?

For information, I runned the OptixHair sample from optix7.1 and it works pretty well. I take inspiration from this code to implement it in Optix_Apps. So I already adapted the Hair and HairFile classes for OptixApps and now I am trying to add a specific closest_hit for curves.

Here are the versions I use :
Visual Studio 2017

Many thanks for your help !

You’re missing the OptixFunctionTable m_api in this code.

OPTIX_CHECK( optixBuiltinISModuleGet(m_context, &defaultOptions, &pipelineCompileOptions, &builtinISOptions, &cubicCurveModule) );

Compare that to any other call with OPTIX_CHECK, e.g. in the last line of your posted code.

I’m not using the optix_stubs.h header from the OptiX SDK which puts the OptiX function table entry points into the global namespace, but load it explicitly into a local OptixFunctionTable m_api because I need it per device in the multi-GPU capable applications.

Please also pull the OptiX_Apps repository again. Your code still contains memset for the structure initialization which I replaced when making the examples compatible to the OptiX SDK 7.1.0.

Additionally do not introduce new OptixModuleCompileOptions defaultOptions.
Use and adjust the existing moduleCompileOptions earlier in my code.

Also note that you need to change the OptixPipelineCompileOptions::usesPrimitiveTypeFlags field to contain OPTIX_PRIMITIVE_TYPE_FLAGS_ROUND_CUBIC_BSPLINE | OPTIX_PRIMITIVE_TYPE_FLAGS_TRIANGLE (resp. the other two curve types) when using them inside the scene.
This will affect performance, so only set the flags you really require.

Please note that the nvlink_shared example is prepared to handle different geometric primitive types, because I have an internal version which is handling triangles, curves and custom primitives already. (Backporting to rtigo3 which is also optimized for single GPU is easily possible.)
The Application::appendInstance() is already handling arbitrary nodes under Instance nodes.
That would need additions to the SceneGraph: a new NodeType NT_CURVES, adding class Curves : public Node to be able to store curve data inside the host scene graph,
Then some code which loads the hair data into that Curves node, an additional GeometryData Device::createGeometry(std::shared_ptr<sg::Curves> geometry) function which builds the Curves AS,
A new case for NT_CURVES in Raytracer::traverseNode() (note that in rtigo3 this i inside the device class)
and the necessary changes to the OptixModules and SBT to assign the intersection/closesthit and intersection/anyhit_shadow hit records to the resp. raytypes.
Basically look at where Triangles nodes are handled and duplicate and adjust that to handle Curves.
You can also differentiate that to the different types of linear, quadratic and cubic curves. I have only used the cubic B-splines so far.

Hello @droettger,

Many thanks for this very fast and complete answer. I’ll try to implement all these specifications and I’ll come back to you.


Hello droettger.

I’ve finally managed to add the curves to the rtigo3 program, thanks to your explanations! Many thanks for that.

Now I’d like to implement the following BCSDF Maybe you’ve already implemented this?
I’ve already done it on other raytracers but with optix I don’t know how to extract the h (or v) parameter that is mentionned at the beginning of the paper.

So my question is : Do you know how to find the h (or v) parameter of this paper please ?

Thank you in advance !

The h and v parameters in that paper are referring to the normalized distance of the hit point relative to the curve radius, correct? This paper is assuming the intersector is a flat ray-facing ribbon intersector, and OptiX currently only supports a round swept-circle intersection geometry. You may want to consider evaluating all values in the paper derived from h to see if you can avoid computing h just to later recompute information about the cylindrical hit point that you already have. You may also want to consider investigating shading models that start with the assumption of round geometry.

I can see two ways to compute h directly - the formula for h in the paper is h = sin(gamma), where gamma is the angle between the ray facing direction w and the round surface (geometric) normal n. Computing the round surface normal is straightforward, there is example code for that in the OptiX SDK. Then you can use sin(acos(dot(n,w))), or you can use the simplification length(cross(n,w)) to calculate h. The cross product is probably (but not guaranteed) faster, it depends on what else you’re doing. The other way to compute h is to project your hit point onto the line that is perpendicular to your ray and passes through the curve center, then compare the distance from your projected hit point to the curve’s radius. Both of these approaches will require querying and evaluating the curve.

I hope that makes sense & helps. Do check to make sure you really need h, or if you can avoid computing it.


Hello @dhart,

Thank you very much for this complete answer. Indeed, it seems that the h parameter is not necessary if we already got the HitPoint.

I’ll try to implement that and I’ll come back to you if I have new questions.