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