Iray Programmer's Manual

Creating a scene programmatically

This topic introduces:

  1. Core concepts about creating a scene by using API calls. Alternatively, a scene can be created by importing a scene file.
  2. example_scene.cpp, which demonstrates how to create a scene by using calls to the Iray API

Comparison to the provided scene file: main.mi

The example program example_scene.cpp is very similar to example_rendering.cpp. The key difference is that example_scene.cpp does not import a scene file; it calls the function create_scene() which constructs a scene programatically by using calls to the Iray API.

This programmatically created scene is almost identical to the scene which is created by importing main.mi. The key difference is that some features which are not needed in example_rendering.cpp have been ommitted in example_scene.cpp (some MDL material instances and the texture coordinates for the ground plane). The only file loaded from disk is main.mdl, which contains the MDL definitions for the materials used in example_scene.cpp.

Order of scene element creation

The various scene elements are created one-by-one in a bottom-up order (this is the same order as in the "main.mi" file). Order is important as soon as references between database elements are created.

A referenced DB element must already exist in the database when the reference is set. If a top-down order is needed for some reason, you must delay setting the reference until after the referenced database element is stored in the database.

Procedure for scene element creation

Creating a certain scene element is typically a three step procedure:

  1. Create a scene element by using a mi::neuraylib::ITransaction::create() call

  2. Set up its properties, including any attributes

  3. Use mi::neuraylib::ITransaction::store() to store the scene element in the database under a name that can be used later to re-access or to reference it.

Note: MDL material instances and functions deviate from this scheme because they require the use of a dedicated factory function for the corresponding material or function definition.

Categorization of scene elements

The different scene elements can be categorized as follows:

  • Structural elements. Examples: instances and groups

  • Leaf nodes. Examples: cameras, lights, geometry, and options

  • MDL-related elements. Examples: materials and functions

  • Miscellaneous elements. Examples: images, textures, light profiles, and BSDF measurements

The details of the various scene element types is beyond the scope of this example. For additional details, see the examples for MDL and triangle meshes.

example_scene.cpp

001 /******************************************************************************
002  * © 1986, 2016 NVIDIA Corporation. All rights reserved.
003  *****************************************************************************/
004 
005 // examples/example_scene.cpp
006 //
007 // Creates a scene programmatically, renders the scene, and writes the image to disk.
008 //
009 // The example expects the following command line arguments:
010 //
011 //   example_scene <scene_file> <mdl_path>
012 //
013 // scene_file       some scene file, e.g., main.mi
014 // mdl_path         path to the MDL modules, e.g., iray-<version>/mdl
015 //
016 // The rendered image is written to a file named "example_scene.png".
017 
018 #include <iostream>
019 
020 #include <mi/neuraylib.h>
021 
022 // Include code shared by all examples.
023 #include "example_shared.h"
024 // Include an implementation of IRender_target.
025 #include "example_render_target_simple.h"
026 
027 // Geometry of the yellow cube.
028 const mi::Uint32 cube_n_points    = 8;
029 const mi::Uint32 cube_n_normals   = 6;
030 const mi::Uint32 cube_n_triangles = 12;
031 
032 mi::Float32_3 cube_points[cube_n_points] = {
033     mi::Float32_3( -0.5, -0.5, -0.5),
034     mi::Float32_3( -0.5, -0.5,  0.5),
035     mi::Float32_3( -0.5,  0.5, -0.5),
036     mi::Float32_3( -0.5,  0.5,  0.5),
037     mi::Float32_3(  0.5, -0.5, -0.5),
038     mi::Float32_3(  0.5, -0.5,  0.5),
039     mi::Float32_3(  0.5,  0.5, -0.5),
040     mi::Float32_3(  0.5,  0.5,  0.5) };
041 
042 mi::Float32_3 cube_normals[cube_n_normals] = {
043     mi::Float32_3(  0.0,  0.0, -1.0),
044     mi::Float32_3(  0.0, -1.0,  0.0),
045     mi::Float32_3( -1.0,  0.0,  0.0),
046     mi::Float32_3(  0.0,  0.0,  1.0),
047     mi::Float32_3(  0.0,  1.0,  0.0),
048     mi::Float32_3(  1.0,  0.0,  0.0) };
049 
050 mi::neuraylib::Triangle_point_indices cube_mesh_connectivity[cube_n_triangles] = {
051     mi::neuraylib::Triangle_point_indices(  0,  2,  6),
052     mi::neuraylib::Triangle_point_indices(  6,  4,  0),
053     mi::neuraylib::Triangle_point_indices(  0,  4,  5),
054     mi::neuraylib::Triangle_point_indices(  5,  1,  0),
055     mi::neuraylib::Triangle_point_indices(  0,  1,  3),
056     mi::neuraylib::Triangle_point_indices(  3,  2,  0),
057     mi::neuraylib::Triangle_point_indices(  1,  5,  7),
058     mi::neuraylib::Triangle_point_indices(  7,  3,  1),
059     mi::neuraylib::Triangle_point_indices(  2,  3,  7),
060     mi::neuraylib::Triangle_point_indices(  7,  6,  2),
061     mi::neuraylib::Triangle_point_indices(  4,  6,  7),
062     mi::neuraylib::Triangle_point_indices(  7,  5,  4) };
063 
064 mi::neuraylib::Triangle_point_indices cube_normal_connectivity[cube_n_triangles] = {
065     mi::neuraylib::Triangle_point_indices(  0,  0,  0),
066     mi::neuraylib::Triangle_point_indices(  0,  0,  0),
067     mi::neuraylib::Triangle_point_indices(  1,  1,  1),
068     mi::neuraylib::Triangle_point_indices(  1,  1,  1),
069     mi::neuraylib::Triangle_point_indices(  2,  2,  2),
070     mi::neuraylib::Triangle_point_indices(  2,  2,  2),
071     mi::neuraylib::Triangle_point_indices(  3,  3,  3),
072     mi::neuraylib::Triangle_point_indices(  3,  3,  3),
073     mi::neuraylib::Triangle_point_indices(  4,  4,  4),
074     mi::neuraylib::Triangle_point_indices(  4,  4,  4),
075     mi::neuraylib::Triangle_point_indices(  5,  5,  5),
076     mi::neuraylib::Triangle_point_indices(  5,  5,  5) };
077 
078 // Geometry of the grey ground plane.
079 const mi::Uint32 ground_n_points    = 4;
080 const mi::Uint32 ground_n_normals   = 1;
081 const mi::Uint32 ground_n_triangles = 2;
082 
083 mi::Float32_3 ground_points[ground_n_points] = {
084     mi::Float32_3( -2.0,  0.0, -2.0),
085     mi::Float32_3( -2.0,  0.0,  2.0),
086     mi::Float32_3(  2.0,  0.0, -2.0),
087     mi::Float32_3(  2.0,  0.0,  2.0) };
088 
089 mi::Float32_3 ground_normals[ground_n_normals] = {
090     mi::Float32_3(  0.0,  1.0,  0.0) };
091 
092 mi::neuraylib::Triangle_point_indices ground_mesh_connectivity[ground_n_triangles] = {
093     mi::neuraylib::Triangle_point_indices(  0,  1,  3),
094     mi::neuraylib::Triangle_point_indices(  3,  2,  0) };
095 
096 mi::neuraylib::Triangle_point_indices ground_normal_connectivity[ground_n_triangles] = {
097     mi::neuraylib::Triangle_point_indices(  0,  0,  0),
098     mi::neuraylib::Triangle_point_indices(  0,  0,  0) };
099 
100 // Creates an attribute "name" of type "Boolean" on "attribute_set" and sets its value to "value".
101 void create_flag( mi::neuraylib::IAttribute_set* attribute_set, const char* name, bool value)
102 {
103     mi::base::Handle<mi::IBoolean> attribute(
104         attribute_set->create_attribute<mi::IBoolean>( name, "Boolean"));
105     attribute->set_value( value);
106 }
107 
108 // Programmatically creates a scene via API calls (the same scene as in "main.mi").
109 mi::neuraylib::IScene* create_scene(
110     mi::base::Handle<mi::neuraylib::INeuray> neuray,
111     mi::neuraylib::ITransaction* transaction)
112 {
113     {
114         // Import the MDL module "main"
115         mi::base::Handle<mi::neuraylib::IImport_api> import_api(
116             neuray->get_api_component<mi::neuraylib::IImport_api>());
117         check_success( import_api.is_valid_interface());
118         mi::base::Handle<const mi::neuraylib::IImport_result> import_result(
119             import_api->import_elements( transaction, "file:main.mdl"));
120         check_success( import_result->get_error_number() == 0);
121     }
122     {
123         // Create the options "options"
124         mi::base::Handle<mi::neuraylib::IOptions> options(
125             transaction->create<mi::neuraylib::IOptions>( "Options"));
126         transaction->store( options.get(), "options");
127     }
128     {
129         // Create the MDL material instance "yellow_material" used by "cube_instance"
130         mi::base::Handle<mi::neuraylib::IAttribute_container> args(
131             transaction->create<mi::neuraylib::IAttribute_container>( "Attribute_container"));
132         mi::base::Handle<mi::IAttachable> tint(
133             args->create_attribute<mi::IAttachable>( "tint", "Attachable<Spectrum>"));
134         mi::set_value( tint.get(), mi::Spectrum( 0.942f, 0.807216f, 0.33441f));
135         mi::base::Handle<const mi::neuraylib::IMdl_material_definition> md(
136             transaction->access<mi::neuraylib::IMdl_material_definition>(
137                 "mdl::main::diffuse_material"));
138         mi::base::Handle<mi::neuraylib::IMdl_material_instance> mi(
139             md->create_material_instance( args.get()));
140         check_success( mi.get());
141         transaction->store( mi.get(), "yellow_material");
142     }
143     {
144         // Create the MDL material instance "grey_material" used by "ground_instance"
145         mi::base::Handle<mi::neuraylib::IAttribute_container> args(
146             transaction->create<mi::neuraylib::IAttribute_container>( "Attribute_container"));
147         mi::base::Handle<mi::IAttachable> tint(
148             args->create_attribute<mi::IAttachable>( "tint", "Attachable<Spectrum>"));
149         mi::set_value( tint.get(), mi::Spectrum( 0.306959f, 0.306959f, 0.306959f));
150         mi::base::Handle<const mi::neuraylib::IMdl_material_definition> md(
151             transaction->access<mi::neuraylib::IMdl_material_definition>(
152                 "mdl::main::diffuse_material"));
153         mi::base::Handle<mi::neuraylib::IMdl_material_instance> mi(
154             md->create_material_instance( args.get()));
155         check_success( mi.get());
156         transaction->store( mi.get(), "grey_material");
157     }
158     {
159         // Create the MDL material instance "white_material" used by "light"
160         mi::base::Handle<mi::neuraylib::IAttribute_container> args(
161             transaction->create<mi::neuraylib::IAttribute_container>( "Attribute_container"));
162         mi::base::Handle<mi::IAttachable> tint(
163             args->create_attribute<mi::IAttachable>( "tint", "Attachable<Spectrum>"));
164         mi::set_value( tint.get(), mi::Spectrum( 1000.0f, 1000.0f, 1000.0f));
165         mi::base::Handle<const mi::neuraylib::IMdl_material_definition> md(
166             transaction->access<mi::neuraylib::IMdl_material_definition>(
167                 "mdl::main::diffuse_light"));
168         mi::base::Handle<mi::neuraylib::IMdl_material_instance> mi(
169             md->create_material_instance( args.get()));
170         check_success( mi.get());
171         transaction->store( mi.get(), "white_light");
172     }
173     {
174         // Create the triangle mesh "cube"
175         mi::base::Handle<mi::neuraylib::ITriangle_mesh> mesh(
176             transaction->create<mi::neuraylib::ITriangle_mesh>( "Triangle_mesh"));
177         create_flag( mesh.get(), "visible", true);
178         create_flag( mesh.get(), "reflection_cast", true);
179         create_flag( mesh.get(), "reflection_recv", true);
180         create_flag( mesh.get(), "refraction_cast", true);
181         create_flag( mesh.get(), "refraction_recv", true);
182         create_flag( mesh.get(), "shadow_cast", true);
183         create_flag( mesh.get(), "shadow_recv", true);
184 
185         // Set point data and mesh connectivity
186         mesh->reserve_points( cube_n_points);
187         for( mi::Uint32 i = 0; i < cube_n_points; ++i)
188             mesh->append_point( cube_points[i]);
189         mesh->reserve_triangles( cube_n_triangles);
190         for( mi::Uint32 i = 0; i < cube_n_triangles; ++i)
191             mesh->append_triangle( cube_mesh_connectivity[i]);
192 
193         // Set normal data and normal connectivity
194         mi::base::Handle<mi::neuraylib::ITriangle_connectivity> connectivity(
195             mesh->create_connectivity());
196         for( mi::Uint32 i = 0; i < cube_n_triangles; ++i)
197             connectivity->set_triangle_indices(
198                 mi::neuraylib::Triangle_handle( i), cube_normal_connectivity[i]);
199         mi::base::Handle<mi::neuraylib::IAttribute_vector> normals(
200             connectivity->create_attribute_vector( mi::neuraylib::ATTR_NORMAL));
201         for( mi::Uint32 i = 0; i < cube_n_normals; ++i)
202             normals->append_vector3( cube_normals[i]);
203         check_success( connectivity->attach_attribute_vector( normals.get()) == 0);
204         check_success( mesh->attach_connectivity( connectivity.get()) == 0);
205         transaction->store( mesh.get(), "cube");
206     }
207     {
208         // Create the instance "cube_instance" referencing "cube"
209         mi::base::Handle<mi::neuraylib::IInstance> instance(
210             transaction->create<mi::neuraylib::IInstance>( "Instance"));
211         check_success( instance->attach( "cube") == 0);
212         mi::Float64_4_4 matrix( 1.0f);
213         matrix.translate( 1.3f, -0.5f, 1.0f);
214         instance->set_matrix( matrix);
215         mi::base::Handle<mi::IRef> material(
216             instance->create_attribute<mi::IRef>( "material", "Ref"));
217         check_success( material->set_reference( "yellow_material") == 0);
218         transaction->store( instance.get(), "cube_instance");
219     }
220     {
221         // Create the triangle mesh "ground"
222         mi::base::Handle<mi::neuraylib::ITriangle_mesh> mesh(
223             transaction->create<mi::neuraylib::ITriangle_mesh>( "Triangle_mesh"));
224         create_flag( mesh.get(), "visible", true);
225         create_flag( mesh.get(), "reflection_cast", true);
226         create_flag( mesh.get(), "reflection_recv", true);
227         create_flag( mesh.get(), "refraction_cast", true);
228         create_flag( mesh.get(), "refraction_recv", true);
229         create_flag( mesh.get(), "shadow_cast", true);
230         create_flag( mesh.get(), "shadow_recv", true);
231 
232         // Set point data and mesh connectivity
233         mesh->reserve_points( ground_n_points);
234         for( mi::Uint32 i = 0; i < ground_n_points; ++i)
235             mesh->append_point( ground_points[i]);
236         mesh->reserve_triangles( ground_n_triangles);
237         for( mi::Uint32 i = 0; i < ground_n_triangles; ++i)
238             mesh->append_triangle( ground_mesh_connectivity[i]);
239 
240         // Set normal data and normal connectivity
241         mi::base::Handle<mi::neuraylib::ITriangle_connectivity> connectivity(
242             mesh->create_connectivity());
243         for( mi::Uint32 i = 0; i < ground_n_triangles; ++i)
244             connectivity->set_triangle_indices(
245                 mi::neuraylib::Triangle_handle( i), ground_normal_connectivity[i]);
246         mi::base::Handle<mi::neuraylib::IAttribute_vector> normals(
247             connectivity->create_attribute_vector( mi::neuraylib::ATTR_NORMAL));
248         for( mi::Uint32 i = 0; i < ground_n_normals; ++i)
249             normals->append_vector3( ground_normals[i]);
250         check_success( connectivity->attach_attribute_vector( normals.get()) == 0);
251         check_success( mesh->attach_connectivity( connectivity.get()) == 0);
252         transaction->store( mesh.get(), "ground");
253     }
254     {
255         // Create the instance "ground_instance" referencing "ground"
256         mi::base::Handle<mi::neuraylib::IInstance> instance(
257             transaction->create<mi::neuraylib::IInstance>( "Instance"));
258         check_success( instance->attach( "ground") == 0);
259         mi::base::Handle<mi::IRef> material(
260             instance->create_attribute<mi::IRef>( "material", "Ref"));
261         check_success( material->set_reference( "grey_material") == 0);
262         transaction->store( instance.get(), "ground_instance");
263     }
264     {
265         // Create the light "light"
266         mi::base::Handle<mi::neuraylib::ILight> light(
267             transaction->create<mi::neuraylib::ILight>( "Light"));
268         create_flag( light.get(), "shadow_cast", true);
269         mi::base::Handle<mi::IRef> material(
270             light->create_attribute<mi::IRef>( "material", "Ref"));
271         check_success( material->set_reference( "white_light") == 0);
272         transaction->store( light.get(), "light");
273     }
274     {
275         // Create the instance "light_instance" referencing "light"
276         mi::base::Handle<mi::neuraylib::IInstance> instance(
277             transaction->create<mi::neuraylib::IInstance>( "Instance"));
278         check_success( instance->attach( "light") == 0);
279         mi::Float64_4_4 matrix( 1.0f);
280         matrix.translate( 5.1f, -7.3f, -1.6f);
281         instance->set_matrix( matrix);
282         transaction->store( instance.get(), "light_instance");
283     }
284     {
285         // Create the camera "camera"
286         mi::base::Handle<mi::neuraylib::ICamera> camera(
287             transaction->create<mi::neuraylib::ICamera>( "Camera"));
288         camera->set_focal( 50.0f);
289         camera->set_aperture( 44.0f);
290         camera->set_aspect( 1.33333f);
291         camera->set_resolution_x( 512);
292         camera->set_resolution_y( 384);
293         camera->set_clip_min( 0.1);
294         camera->set_clip_max( 1000);
295         transaction->store( camera.get(), "camera");
296     }
297     {
298         // Create the instance "camera_instance" referencing "camera"
299         mi::base::Handle<mi::neuraylib::IInstance> instance(
300             transaction->create<mi::neuraylib::IInstance>( "Instance"));
301         check_success( instance->attach( "camera") == 0);
302         mi::Float64_4_4 matrix(
303             0.68826f,  0.37107f, -0.623382f, 0.0f,
304             0.00000f,  0.85929f,  0.511493f, 0.0f,
305             0.72546f, -0.35204f,  0.591414f, 0.0f,
306             0.00000f,  0.00000f, -6.256200f, 1.0f);
307         instance->set_matrix( matrix);
308         transaction->store( instance.get(), "camera_instance");
309     }
310     {
311         // Create the group "rootgroup" containing all instances
312         mi::base::Handle<mi::neuraylib::IGroup> group(
313             transaction->create<mi::neuraylib::IGroup>( "Group"));
314         check_success( group->attach( "cube_instance"  ) == 0);
315         check_success( group->attach( "ground_instance") == 0);
316         check_success( group->attach( "light_instance" ) == 0);
317         check_success( group->attach( "camera_instance") == 0);
318         transaction->store( group.get(), "rootgroup");
319     }
320 
321     // Create the scene object itself and return it.
322     mi::neuraylib::IScene* scene = transaction->create<mi::neuraylib::IScene>( "Scene");
323     scene->set_rootgroup( "rootgroup");
324     scene->set_camera_instance( "camera_instance");
325     scene->set_options( "options");
326     return scene;
327 }
328 
329 void configuration( mi::base::Handle<mi::neuraylib::INeuray> neuray, const char* mdl_path)
330 {
331     // Configure the neuray library. Here we set the search path for .mdl files.
332     mi::base::Handle<mi::neuraylib::IRendering_configuration> rendering_configuration(
333         neuray->get_api_component<mi::neuraylib::IRendering_configuration>());
334     check_success( rendering_configuration.is_valid_interface());
335     check_success( rendering_configuration->add_mdl_path( mdl_path) == 0);
336 
337     // Load the FreeImage, Iray Photoreal, and .mi importer plugins.
338     mi::base::Handle<mi::neuraylib::IPlugin_configuration> plugin_configuration(
339         neuray->get_api_component<mi::neuraylib::IPlugin_configuration>());
340 #ifndef MI_PLATFORM_WINDOWS
341     check_success( plugin_configuration->load_plugin_library( "freeimage.so") == 0);
342     check_success( plugin_configuration->load_plugin_library( "libiray.so") == 0);
343     check_success( plugin_configuration->load_plugin_library( "mi_importer.so") == 0);
344 #else
345     check_success( plugin_configuration->load_plugin_library( "freeimage.dll") == 0);
346     check_success( plugin_configuration->load_plugin_library( "libiray.dll") == 0);
347     check_success( plugin_configuration->load_plugin_library( "mi_importer.dll") == 0);
348 #endif
349 }
350 
351 void rendering( mi::base::Handle<mi::neuraylib::INeuray> neuray)
352 {
353     // Get the database, the global scope of the database, and create a transaction in the global
354     // scope for importing the scene file and storing the scene.
355     mi::base::Handle<mi::neuraylib::IDatabase> database(
356         neuray->get_api_component<mi::neuraylib::IDatabase>());
357     check_success( database.is_valid_interface());
358     mi::base::Handle<mi::neuraylib::IScope> scope(
359         database->get_global_scope());
360     mi::base::Handle<mi::neuraylib::ITransaction> transaction(
361         scope->create_transaction());
362     check_success( transaction.is_valid_interface());
363 
364     // Create the scene
365     mi::base::Handle<mi::neuraylib::IScene> scene( create_scene( neuray, transaction.get()));
366     transaction->store( scene.get(), "the_scene");
367 
368     // Create the render context using the Iray Photoreal render mode
369     scene = transaction->edit<mi::neuraylib::IScene>( "the_scene");
370     mi::base::Handle<mi::neuraylib::IRender_context> render_context(
371         scene->create_render_context( transaction.get(), "iray"));
372     check_success( render_context.is_valid_interface());
373     mi::base::Handle<mi::IString> scheduler_mode( transaction->create<mi::IString>());
374     scheduler_mode->set_c_str( "batch");
375     render_context->set_option( "scheduler_mode", scheduler_mode.get());
376     scene = 0;
377 
378     // Create the render target and render the scene
379     mi::base::Handle<mi::neuraylib::IImage_api> image_api(
380         neuray->get_api_component<mi::neuraylib::IImage_api>());
381     mi::base::Handle<mi::neuraylib::IRender_target> render_target(
382         new Render_target( image_api.get(), "Color", 512, 384));
383     check_success( render_context->render( transaction.get(), render_target.get(), 0) >= 0);
384 
385     // Write the image to disk
386     mi::base::Handle<mi::neuraylib::IExport_api> export_api(
387         neuray->get_api_component<mi::neuraylib::IExport_api>());
388     check_success( export_api.is_valid_interface());
389     mi::base::Handle<mi::neuraylib::ICanvas> canvas( render_target->get_canvas( 0));
390     export_api->export_canvas( "file:example_scene.png", canvas.get());
391 
392     // All transactions need to get committed or aborted.
393     transaction->commit();
394 }
395 
396 int main( int argc, char* argv[])
397 {
398     // Collect command line parameters
399     if( argc != 2) {
400         std::cerr << "Usage: example_scene <mdl_path>" << std::endl;
401         keep_console_open();
402         return EXIT_FAILURE;
403     }
404     const char* mdl_path = argv[1];
405 
406     // Access the neuray library
407     mi::base::Handle<mi::neuraylib::INeuray> neuray( load_and_get_ineuray());
408     check_success( neuray.is_valid_interface());
409 
410     // Configure the neuray library
411     configuration( neuray, mdl_path);
412 
413     // Start the neuray library
414     mi::Sint32 result = neuray->start();
415     check_start_success( result);
416 
417     // Do the actual rendering
418     rendering( neuray);
419 
420     // Shut down the neuray library
421     check_success( neuray->shutdown() == 0);
422     neuray = 0;
423 
424     // Unload the neuray library
425     check_success( unload());
426 
427     keep_console_open();
428     return EXIT_SUCCESS;
429 }