A minimal example with PlotOptiX

Vitality Learning
3 min readFeb 5, 2023

PlotOptiX is a data visualisation and ray tracing library for Python based on the NVIDIA OptiX framework.

The purpose of this short post is showing a simple example illustrating the ray tracing features of PlotOptiX with particular reference to the generation of rays and to their intersections with an elementary scene. The examples are available at the GitHub repository.

NpOptiX and TkOptiX

PlotOptiX has two working modalities: NpOptiX and TkOptiX. With NpOptiX, the ray tracing results are exposed to the User which is then in charge to possibly manipulate and/or visualize them. Opposite to that, TkOptiX automatically shows the ray tracing results. In this post, we will be interested in using NpOptiX.

The scenario

The worked out example considers a scene made up by a plate with a4x4 size formed by two triangles. To launch and trace the rays, customized ray origins and directions are provided.

The two triangles are described by the coordinates of their vertices and by triplets containing the indices of the triangle vertices as follows:

Definition of the ray tracing engine

The ray tracing engine is initialized thanks to the instruction:

which generates an array of nu x nv rays and which invokes the displayResults function once the accumulation phase of ray tracing has been completed. For more information, see the Ray-tracing and compute loop page. The displayResults function shows some ray tracing results. In particular, two figures showing the positions of the intersection points of the rays with the object mesh and the distance between the launch and the intersection points are displayed. The displayResults function will be shown shortly. It withdraws the ray tracing results from the output buffer of the ray tracer. The choice of invokingdisplayResults at the on_rt_accum_done phase is related to the need of waiting for the completion of the ray tracing operations and so for the filling of the output buffer.

The vertices of the triangles and the above described triangle indices configure the mesh by the instructions:

It is now necessary to define the ray origins. In the discussed example, we consider a Cartesian grid of nu x nv points, equispaces in the rectangle [-2, 2] x [-2, 2] and set at a quota z=-1 :

The origins are then assigned to the originslabel by the instruction:

Moreover, it is necessary to define the directions along which the rays should propagate. In the considered example, this is done by constructing an nu x nv array of unit vectors directed from the origins towards the plate:

It should be noticed that the fourth component of directionsTexture represents the maximum range of the ray.

The directions are then assigned to the directions label thanks to the instruction:

The next step is to set up a customized camera and assigning it to the custom_camlabel. This is done by the instruction:

The cam_type = "CustomProjXYZtoDir" option corresponds to the fact that we are customizing the ray origins and directions.

Before finally tracing the rays, the maximum number of accumulation frames is fixed to 1 . In this way, a single set of rays, namely, that provided by the above described U, V andWmatrices, is launched:

Setting a larger max_accumulation_frames is used for computer graphics applications, in particular, to perform averages among different ray launches.

Finally, the ray tracer is executed thanks to the instruction:

We report below the above mentioneddisplayResults function:

Such a function exploits the data stored in the_hit_pos field holding the x , y andz coordinates of the intersection points as well as the distances of the ray origins to the intersection points.

Moreover, it exploits the data stored in the _geo_id[:, :, 1] field which is a matrix of the same size of the ray grid and that holds the triangles’ IDs if the ray has intersected a primitive, otherwise a very large value. Such data are used both to filter the rays that have missed a valid intersection or to locate the intersected primitives. In the example of interest, the primitives are only two, so that a non-valid intersection is marked with a primitive ID equal to 3 .

--

--

Vitality Learning

We are teaching, researching and consulting parallel programming on Graphics Processing Units (GPUs) since the delivery of CUDA. We also play Matlab and Python.