Iray SDK API nvidia_logo_transpbg.gif Up
Example for Volumes
[Previous] [Next] [Up]

This example imports a partial scene containing definitions for the camera, a light, and some geometry (a ground plane and a yellow cube). It then creates a volume object and attaches an OpenVDB file to a minimal MDL material.

New Topics

  • Creation of volumes

Detailed Description

Creation of volumes


There is a fundamental difference between a volume object and a mesh object with an associated OpenVDB file. Both can be set up using the same MDL material that references an OpenVDB file to implement volumetric properties. However, volumes do not have an explicit boundary. They can therefore overlap, allow camera positions on the volume's edge, enable proper clipping, and provide much better light sampling with respect to noise. (For details, see Chapter 10, Inhomogeneous volumes, in the Iray Programmer's Manual.)

When a volume object is created in the example's setup_scene() function, the grid bounding box of the OpenVDB dataset to be attached to the volume object must be taken into account in volume->set_bounds. The necessary information is provided via volume_data->get_data_bounds() and volume_data->get_transform(). Using a smaller bounding box will clip the volume and only allow a subset of the entire volume to be displayed. Imagine that the data has a virtual window of given size and orientation. To transform the volume as an entity use the instance->set_matrix which happens as the next step in the example. Here, an arbitrary transform is created to fit the volume to the premade example scene.

Note that the actual data is not yet linked to the object, which will only happen through the MDL material. It is attached to an MDL material named "smoke" as a volumetric texture and the material is assigned to the instance.

Example Source

Source Code Location: examples/example_volumes.cpp

/******************************************************************************
* Copyright 2023 NVIDIA Corporation. All rights reserved.
*****************************************************************************/
// examples/example_volumes.cpp
//
// Creates and manipulates inhomogeneous volume objects.
//
// The example expects the following command line arguments:
//
// example_volumes <mdl_path> [<volume_file>]
//
// mdl_path path to the MDL modules, e.g., iray-<version>/mdl
// volume_file Optional path to a VDB volume.
// The OpenVDB side (https://www.openvdb.org/download/) lists
// a number of freely available volumes.
// This example makes use of the a custom smoke 'smoke.vdb' data set
// provided with the example.
// It is possible to supply other volumes as optional argument, but
// the parameters of the material might be wrong which might produce
// invisible volumes.
//
// The rendered image is written to a file named "example_volumes.png".
#include <mi/neuraylib.h>
// Include code shared by all examples.
#include "example_shared.h"
// Include an implementation of IRender_target.
#include "example_render_target_simple.h"
#include <iostream>
// Add a volume object that has the 'smoke' material attached.
void setup_scene( mi::neuraylib::INeuray* neuray,
const char* rootgroup,
const char* volume_file)
{
// The "unique" element names used in the example
const char* element_name = "Smoke";
const char* instance_name = "Smoke_instance";
const char* vol_name = "Vol";
const char* tex_name = "Vol_tex";
// Create volume data container
transaction->create<mi::neuraylib::IVolume_data>("Volume_data") );
// List VDB content
mi::base::Handle<const mi::IArray> channel_info(volume_data->list_contents( volume_file));
fprintf(stderr, "----------------------------------------------------------\n");
fprintf(stderr, "Volume file '%s' grid info\n", volume_file);
fprintf(stderr, "----------------------------------------------------------\n");
for (mi::Size i = 0; i < channel_info->get_length(); ++i) {
channel_info->get_element<mi::neuraylib::IVolume_info>( i));
const mi::Voxel_block_struct& vb = item->get_data_bounds();
const mi::Float32_4_4& m = item->get_transform();
fprintf(stderr,
"name='%s', value type='%s', data bounds=((%d, %d, %d), (%d, %d, %d)), "
"transform=(%f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f)\n",
item->get_name() ? item->get_name() : "",
item->get_value_type(),
vb.min.x, vb.min.y, vb.min.z, vb.max.x, vb.max.y, vb.max.z,
m.xx, m.xy, m.xz, m.xw, m.yx, m.yy, m.yz, m.yw, m.zx, m.zy, m.zz, m.zw, m.wx, m.wy, m.wz, m.ww);
}
fprintf(stderr, "----------------------------------------------------------\n");
// Load the volume data
// nullptr in second argument -> default channel
check_success( volume_data->reset_file( volume_file) == 0);
mi::Voxel_block voxel_bounds = volume_data->get_data_bounds();
mi::Float32_4_4 voxel_transform = volume_data->get_transform();
transaction->store(volume_data.get(), vol_name, mi::neuraylib::ITransaction::LOCAL_SCOPE);
// Create the volume object
transaction->create<mi::neuraylib::IVolume>("Volume") );
mi::Bbox3 bbox ( voxel_bounds );
volume->set_bounds( bbox, voxel_transform);
transaction->store( volume.get(), element_name);
// Create an instance of that volume
transaction->create<mi::neuraylib::IInstance>("Instance") );
instance->attach( element_name);
instance->create_attribute<mi::IBoolean>( "visible", "Boolean"));
visible->set_value( true);
// Set an instrance transformation to fit the smoke volume to our example scene
mi::Float64_4_4 matrix ( voxel_transform );
// Start by inverting the data given transform.
matrix.invert();
// Center at origin
matrix.translate( -voxel_bounds.center().x,
-voxel_bounds.center().y,
-voxel_bounds.center().z);
// Scale relative to the data size, but keep aspect ratio
mi::Float64_4_4 scale( 3.0f / static_cast<float>(voxel_bounds.extent().y));
scale.ww = 1.0f; matrix *= scale;
// Move above ground plane and towards focus
matrix.translate(-1.0f, 1.5f, 1.0f);
matrix.invert();
instance->set_matrix( matrix);
instance->create_attribute<mi::IRef>( "material", "Ref"));
material->set_reference( "smoke");
transaction->create<mi::neuraylib::ITexture>("Texture") );
texture->set_volume( vol_name);
transaction->store( texture.get(), tex_name, mi::neuraylib::ITransaction::LOCAL_SCOPE);
// Attach the texture to the material property "density"
mdl_factory->create_expression_factory( transaction));
transaction, "smoke", mdl_factory.get());
check_success( smoke_material.set_value( "density", tex_name) == 0);
transaction->store( instance.get(), instance_name, mi::neuraylib::ITransaction::LOCAL_SCOPE);
// Attach the instance to the root group
transaction->edit<mi::neuraylib::IGroup>( rootgroup));
group->attach( instance_name);
}
void configuration( mi::neuraylib::INeuray* neuray, const char* mdl_path)
{
// Configure the neuray library. Here we set the search path for .mdl files.
check_success( rc->add_mdl_path( mdl_path) == 0);
check_success( rc->add_mdl_path( ".") == 0);
check_success( rc->add_resource_path( ".") == 0);
// Load the OpenImageIO, Iray Photoreal, and .mi importer plugins.
check_success( pc->load_plugin_library( "nv_openimageio" MI_BASE_DLL_FILE_EXT) == 0);
check_success( pc->load_plugin_library( "libiray" MI_BASE_DLL_FILE_EXT) == 0);
check_success( pc->load_plugin_library( "mi_importer" MI_BASE_DLL_FILE_EXT) == 0);
}
void rendering( mi::neuraylib::INeuray* neuray, const char* volume_file)
{
// Get the database, the global scope of the database, and create a transaction
// in the global scope for importing the scene file and storing the scene.
check_success( database.is_valid_interface());
database->get_global_scope());
scope->create_transaction());
check_success( transaction.is_valid_interface());
// Import the scene file
check_success( import_api.is_valid_interface());
import_api->import_elements( transaction.get(), "file:main.mi"));
check_success( import_result->get_error_number() == 0);
// Add the volume to the scene
setup_scene( neuray, transaction.get(), import_result->get_rootgroup(), volume_file);
// Create the scene object
transaction->create<mi::neuraylib::IScene>( "Scene"));
scene->set_rootgroup( import_result->get_rootgroup());
scene->set_options( import_result->get_options());
scene->set_camera_instance( import_result->get_camera_inst());
transaction->store( scene.get(), "the_scene");
// Create the render context using the Iray Photoreal render mode
scene = transaction->edit<mi::neuraylib::IScene>( "the_scene");
scene->create_render_context( transaction.get(), "iray"));
check_success( render_context.is_valid_interface());
mi::base::Handle<mi::IString> scheduler_mode( transaction->create<mi::IString>());
scheduler_mode->set_c_str( "batch");
render_context->set_option( "scheduler_mode", scheduler_mode.get());
scene = 0;
// Create the render target and render the scene
new Render_target( image_api.get(), "Color", 512, 384));
check_success( render_context->render( transaction.get(), render_target.get(), 0) >= 0);
// Write the image to disk
check_success( export_api.is_valid_interface());
mi::base::Handle<mi::neuraylib::ICanvas> canvas( render_target->get_canvas( 0));
export_api->export_canvas( "file:example_volumes.png", canvas.get());
transaction->commit();
}
int main( int argc, char* argv[])
{
// Collect command line parameters
if( argc != 3 && argc != 2) {
std::cerr << "Usage: example_volumes <mdl_path> [<volume_file>]" << std::endl;
keep_console_open();
return EXIT_FAILURE;
}
const char* mdl_path = argv[1];
const char* volume_file = argc == 2 ? "smoke.vdb" : argv[2];
// Access the neuray library
mi::base::Handle<mi::neuraylib::INeuray> neuray( load_and_get_ineuray());
check_success( neuray.is_valid_interface());
// Configure the neuray library
configuration( neuray.get(), mdl_path);
// Start the neuray library
mi::Sint32 result = neuray->start();
check_start_success( result);
// Do the actual rendering
rendering( neuray.get(), volume_file);
// Shut down the neuray library
check_success( neuray->shutdown() == 0);
neuray = 0;
// Unload the neuray library
check_success( unload());
keep_console_open();
return EXIT_SUCCESS;
}
This interface represents bool.
Definition: inumber.h:122
A reference is an object that acts as a pointer to other database elements.
Definition: iref.h:25
A simple string class.
Definition: istring.h:22
Handle class template for interfaces, automatizing the lifetime control via reference counting.
Definition: handle.h:113
Axis-aligned N-dimensional bounding box class template of fixed dimension.
Definition: bbox.h:74
NxM-dimensional matrix class template of fixed dimensions.
Definition: matrix.h:367
A wrapper around the interface for MDL material instances and function calls.
Definition: argument_editor.h:57
This interface is used to interact with the distributed database.
Definition: idatabase.h:293
This interface is used to export files.
Definition: iexport_api.h:38
A group is a container for other scene elements.
Definition: igroup.h:39
This interface provides various utilities related to canvases and buffers.
Definition: iimage_api.h:49
This interface is used to import files.
Definition: iimport_api.h:100
An instance is a scene element that adds a transformation and attributes to another scene element.
Definition: iinstance.h:117
Factory for various MDL interfaces and functions.
Definition: imdl_factory.h:53
This is an object representing the Iray library.
Definition: ineuray.h:44
virtual Sint32 shutdown(bool blocking=true)=0
Shuts down the library.
virtual base::IInterface * get_api_component(const base::Uuid &uuid) const =0
Returns an API component from the Iray SDK API.
virtual Sint32 start(bool blocking=true)=0
Starts the operation of the Iray library.
This interface is used to load plugins and to query information about loaded plugins.
Definition: iplugin_configuration.h:24
This interface is used to query and change the rendering configuration.
Definition: irendering_configuration.h:109
The scene is the top-level element describing a subset of DB elements to be rendered.
Definition: iscene.h:44
Textures add image processing options to images.
Definition: itexture.h:68
A transaction provides a consistent view on the database.
Definition: itransaction.h:81
virtual base::IInterface * create(const char *type_name, Uint32 argc=0, const base::IInterface *argv[]=0)=0
Creates an object of the type type_name.
virtual base::IInterface * edit(const char *name)=0
Retrieves an element from the database and returns it ready for editing.
static const mi::Uint8 LOCAL_SCOPE
Symbolic privacy level for the privacy level of the scope of this transaction.
Definition: itransaction.h:252
virtual Sint32 commit()=0
Commits the transaction.
virtual Sint32 store(base::IInterface *db_element, const char *name, Uint8 privacy=LOCAL_SCOPE)=0
Stores the element db_element in the database under the name name and with the privacy level privacy.
Interface representing a volume data set.
Definition: ivolume.h:89
Basic information about one of the data sets contained in a volume data file.
Definition: ivolume.h:202
Interface representing a top-level volume.
Definition: ivolume.h:37
#define MI_BASE_DLL_FILE_EXT
The operating system specific default filename extension for shared libraries (DLLs)
Definition: config.h:340
Uint64 Size
Unsigned integral type that is large enough to hold the size of all types.
Definition: types.h:112
signed int Sint32
32-bit signed integer.
Definition: types.h:46
Vector extent() const
Returns the size of the bounding box.
Definition: bbox.h:459
Vector center() const
Returns the center point of the bounding box.
Definition: bbox.h:456
Vector_struct<T, DIM> max
Elementwise maximal bounding box corner.
Definition: bbox.h:49
Vector_struct<T, DIM> min
Elementwise minimal bounding box corner.
Definition: bbox.h:48
Iray SDK API.
Storage class for an axis-aligned N-dimensional bounding box class template of fixed dimension.
Definition: bbox.h:47
[Previous] [Next] [Up]