OSPRay v2.0 Porting Guide
OSPRay v2.0.0 introduces a number of new features and updates, as well as some API changes. This guide is intended as an introduction to the new features and the concepts behind them, and as a guide to porting applications using OSPRay v1.8.x to v2.0.0.
Parameters
Setting parameters
OSPRay traded having a limited number of parameter types (with a
unique function signature for each type) by instead having a single
generic function that takes any type found in the
OSPDataType
enum. The new function signature is as
follows:
void ospSetParam(OSPObject, const char *name, OSPDataType type, const void *mem)
This function takes the address of the value being set, where care should be taken when setting pointer-based types. For example, consider the following C-array:
float myValues[] = {0.f, 1.f, 2.f}
If the application wants to set these values as an OSP_VEC3F, note
that the const void *mem
parameter to
ospSetParam
should point to what is the address of what
would be a vec3f
. Thus either of the following are
valid:
void ospSetParam(object, "some_parameter", OSP_VEC3F, &myValues[0]);
or
void ospSetParam(object, "some_parameter", OSP_VEC3F, myValues);
The second variant relies on implicit casting from
float[]
to float *
, which will point to the
address of the first value. This then gets casted to a
vec3f
to be set on the target object
.
Some of the ospSet*
functions were preserved as utility
wrapper functions outlined later in this document (and can be found in
ospray_util.h
).
OSPDataType type checking
Where it was previously accepted to create data of generic type
OSP_OBJECT
to represent a list of any object type, the
OSPDataType
must now match the object(s) it contains.
Objects
New Object Hierarchy
OSPRay objects, such as OSPGeometry
and
OSPVolume
, have a new hierarchy that affords more control
over geometry and volume transformation and instancing in the scene.
Previously, the workflow was to create an object, fill it with the
necessary parameters, and then place the object into an
OSPModel
via, for example, ospAddGeometry
. An
example is shown below using the C API.
= ospNewGeometry("triangles");
OSPGeometry mesh // set parameters on mesh
(mesh);
ospCommit
= ospNewModel();
OSPModel world (world, mesh);
ospAddGeometry(mesh);
ospRelease(world); ospCommit
In OSPRay v2.0.0, there is now an OSPWorld
, which
effectively replaces the old OSPModel
. In addition, there
are now 3 new objects that exist “in between” the geometry and the
world: OSPGeometricModel
, OSPGroup
, and
OSPInstance
. There is also an
OSPVolumetricModel
equivalent for working with
OSPVolume
objects.
The new workflow is shown below. Note that calls to
ospRelease()
have been removed for brevity.
// create a geometry
= ospNewGeometry("triangles");
OSPGeometry mesh // set parameters on mesh
(mesh);
ospCommit
// put the geometry in a model (a geometry can exist in more than one model)
= ospNewGeometricModel(mesh);
OSPGeometricModel model (model);
ospCommit
// put the geometric model(s) in a group
= ospNewGroup();
OSPGroup group = ospNewSharedData1D(&model, OSP_GEOMETRIC_MODEL, 1);
OSPData geometricModels (group, "geometry", geometricModels);
ospSetObject(group);
ospCommit
// put the group in an instance (a group can be instanced more than once)
= ospNewInstance(group);
OSPInstance (instance);
ospCommit
// put the instance in the world
= ospNewWorld();
OSPWorld world = ospNewSharedData1D(&instance, OSP_INSTANCE, 1);
OSPData instances (world, "instance", instances);
ospSetObject(world); ospCommit
While this looks more complex at first, the new hierarchy structure provides more fine control over appearance information and instance transformations.
In OSPRay v1.x, geometries and volumes contained both structural and appearance information which limited their reuse in other objects. For example, the volume’s transfer function can now be different between an isosurface, slice, and rendered volume all in the same scene without duplicating the actual volume itself.
OSPGeometry and OSPVolume
OSPGeometry
and OSPVolume
contain the
physical data represented by the object. For geometries, this is the
intersectable surface. For volumes, it is the scalar field to be
sampled.
OSPGeometricModel and OSPVolumetricModel
OSPGeometricModel
and OSPVolumetricModel
contain appearance information about the geometry or volume that they
hold. They have a one-to-N relationship with OSPGeometry
and OSPVolume
objects (i.e. a geometry or volume can exist
in more than one model), but commonly exist as one-to-one. This could be
used to create multiple copies of a geometry with different materials,
for example.
OSPGeometricModel
s can hold primitive color information
(e.g. the color used for each sphere in a Spheres
geometry), a material or list of materials, and primitive material IDs
(i.e. indexes into the list of materials if it is used).
OSPVolumetricModel
s can hold transfer functions.
OSPGroup
OSPGroup
objects contain zero or more
OSPGeometricModel
and OSPVolumetricModel
objects. They can hold geometries and volumes simultaneously.
This is useful to collect together any objects that are logically grouped together in the scene.
OSPInstance
OSPInstance
contains transformation information on an
OSPGroup
object. It has a one-to-one relationship with
OSPGroup
.
This means that each OSPInstance
contains exactly one
OSPGroup
. Similar to models and groups, a single
OSPGroup
may be placed into multiple
OSPInstace
objects. This allows for instancing of
multiple objects throughout the scene, each with different
transformations.
OSPInstance
objects holds an affine transformation
matrix that is applied to all objects in its group.
OSPWorld
OSPWorld
is the final container for all
OSPInstance
and OSPLight
objects. It can
contain one or more instances and lights. The world is passed along with
a renderer, camera, and framebuffer to ospRenderFrame
to
generate an image.
void ospRetain(OSPObject)
To allow the user greater control over the lifetime of objects, a new
API ospRetain
has been introduced. This call increments an
object’s reference count, and can delay automatic deletion.
Updated Public Parameter Names
OSPRay v2.0.0 has updated public parameter names (the strings used in
ospSetParam
) to a more consistent naming convention. OSPRay
now will print a warning (visible if debug logs are enabled) if a
parameter provided by the user is not used by an object. This can help
catch cases where applications are using parameter names from OSPRay
v1.8.5 or mistyped names. Some objects have required parameters. In
these cases, OSPRay will invoke the error callback indicating which
object and which parameter.
OSPRay now universally uses the camelCase naming convention for all
object types and parameter names. Furthermore, parameter names which are
data arrays now use singular names to indicate what the elements are,
and parameters which form a logical “struture-of-arrays” are
communicated via the "[structure_name].[member_name]"
naming convention.
For example, the "opacities"
array for
linear
transfer functions is now named
"opacity"
.
Finally, some parameters have changed from being string-based to
enum-based. All enums can be found in OSPEnums.h
, where
their usage can be found in the main API documentation.
ospRenderFrame
ospRenderFrame
has changed in two ways. The signature
has changed from:
float ospRenderFrame(OSPFrameBuffer, OSPRenderer,
const uint32_t frameBufferChannels = OSP_FB_COLOR);
to
(OSPFrameBuffer, OSPRenderer, OSPCamera, OSPWorld); OSPFuture ospRenderFrame
And, notably, it is no longer blocking. Two new calls
ospIsReady
, and ospWait
are available to
manage synchronization.
Utility Library
As a convenience, a lightweight utility library has been provided to
help users port from the previous versions and reduce boilerplate code.
This set of additional API calls are all implemented in terms of the
core API found in ospray.h
, where they can be found in
ospray_util.h
. Their definitions are compiled into
libospray
(the ospray::ospray
CMake target)
and are compatible with any valid device implementation.
OSPData and Parameter helpers
The core OSPRay API has been simplified by removing many of the type specializations from the data and parameter set calls. The utility library provides wrappers to the familiar calls listed below:
(OSPObject, const char *n, const char *s);
ospSetString(OSPObject, const char *n, OSPObject obj);
ospSetObject
(OSPObject, const char *n, int x);
ospSetBool(OSPObject, const char *n, float x);
ospSetFloat(OSPObject, const char *n, int x);
ospSetInt
(OSPObject, const char *n, float x, float y);
ospSetVec2f(OSPObject, const char *n, float x, float y, float z);
ospSetVec3f(OSPObject, const char *n, float x, float y, float z, float w);
ospSetVec4f
(OSPObject, const char *n, int x, int y);
ospSetVec2i(OSPObject, const char *n, int x, int y, int z);
ospSetVec3i(OSPObject, const char *n, int x, int y, int z, int w);
ospSetVec4i
(OSPObject, const char *n, OSPDataType type, OSPObject obj); ospSetObjectAsData
OSPRay v1.x calls to ospSetData
have been replaced with
ospSetObject
. Convenience wrappers have also been provided
to specialize ospNewData
, and the new
ospNewSharedData
and ospCopyData
APIs.
(const void *sharedData, OSPDataType type, uint32_t numItems);
ospNewSharedData1D(const void *sharedData, OSPDataType type, uint32_t numItems, int64_t byteStride);
ospNewSharedData1DStride(const void *sharedData, OSPDataType type, uint32_t numItems1, uint32_t numItems2);
ospNewSharedData2D(const void *sharedData, OSPDataType type, uint32_t numItems1, int64_t byteStride1, uint32_t numItems2, int64_t byteStride2);
ospNewSharedData2DStride(const void *sharedData, OSPDataType type, uint32_t numItems1, uint32_t numItems2, uint32_t numItems3);
ospNewSharedData3D
(OSPDataType type, uint32_t numItems);
ospNewData1D(OSPDataType type, uint32_t numItems1, uint32_t numItems2);
ospNewData2D
(const OSPData source, OSPData destination, uint32_t destinationIndex);
ospCopyData1D(const OSPData source, OSPData destination, uint32_t destinationIndex1, uint32_t destinationIndex2); ospCopyData2D
Object Usage Simplification
To simplify setting data onto an object, if there is only a single
data item, the wrapper ospSetObjectAsData
permits the user
to assign this item without first creating a data object containing that
item.
For example:
= ospNewSharedData1D(&model, OSP_GEOMETRIC_MODEL, 1);
OSPData geometricModels (group, "geometry", geometricModels);
ospSetObject(geometricModels); ospRelease
simply becomes:
(group, "geometry", OSP_GEOMETRIC_MODEL, model); ospSetObjectAsData
Rendering helpers
While ospRenderFrame
is now asynchronous, some users
will prefer the original blocking behavior that returns the frame
variance. The utility library provides a wrapper to this
functionality:
float ospRenderFrameBlocking(OSPFrameBuffer fb,
,
OSPRenderer renderer,
OSPCamera camera) OSPWorld world