Iray Programmer's Manual

Editing and creating materials

This topic introduces:
  1. typical MDL-related tasks such as modifying inputs of material instances, creating new material instances, and attaching function calls to material inputs:
  2. The program example_mdl.cpp, which uses the scene main.mi to demonstrate typical MDL-related tasks.

Modifying inputs of existing material instances

This section describes the steps for modifying the input of an existing material instance. In particular, it explains how to change the color of the material instance yellow_material, which is used for the cube and other scene elements in example_mdl.cpp.

To change the color, an interface is required that represents the new value. The method get_argument_type() of the mi::neuraylib::IMdl_material_instance interface yields the type name of the tint argument, which is Attachable. Passing this type name to mi::neuraylib::ITransaction::create() yields the required interface.

To set the value, the free function set_value(mi::IData *data, const mi::Spectrum &value) is used. This template function is quite useful when dealing with instances of mi::IData and mi::neuraylib::IAttribute_set. It tries to match the dynamic type of the mi::IData instance with the static type of the second argument, converts it as necessary, and calls the appropriate setter method. In particular, when dealing with attachables, it avoids the extra call of mi::IAttachable::get_value().

The new value is then set with the set_argument() method.

These steps are shown in example_mdl.cpp in full detail for illustrative purposes. The helper class mi::neuraylib::Mdl_argument_editor can be used to simplify such workflows, as shown in the second code block.

Creating new material instances

This section describes the steps for creating a new material instance and an example for its use.

As described in the previous section, the material instance of yellow_material is shared by a number of scene elements. When the color of this material instance is changed, then the change is reflected in all scene elements that use the shared material. To change the color of the cube only, requires three steps: preparing the arguments of a new material instance, creating the new material instance, and then attaching it to the cube. The steps are described in more detail as follows:

  1. Prepare an attribute container that holds the arguments of the new material instance. This is mandatory if the material definition has parameters without default initializers, otherwise it is optional. In both cases, arguments can be modified after creation of the material instance as shown above.
  2. Use the method create_material_instance() to create a new material instance from the material definition mdl::main::diffuse_material (the same definition that was used for yellow_material). The newly created material instance is stored in the database.
  3. Replace the previously used material instance, yellow_material, with the newly created material instance by changing the material attribute of cube_instance.

These steps are shown in example_mdl.cpp in full detail.

Attaching function calls to material inputs

The preceding sections of this topic focused on constant inputs for material instances. This section describes how to connect another MDL function to the input of the material instance.

The interface mi::IAttachable is used to handle these two modes for arguments of material instances (or function calls). This interface can represent the following:

  • A reference to another database element (similar to mi::IRef), which is used to represent MDL function calls
  • A fixed value (of type mi::IData), which is used to represent constant inputs, for example color, as described in the preceding sections of this topic

The program example_mdl.cpp shows how to apply a texture to the ground plane:

  1. Create the necessary database elements of type mi::neuraylib::IImage and mi::neuraylib::ITexture, which will hold the texture to be used.
  2. Import the MDL module base, which contains the MDL function mdl::base::file_texture to be used.
  3. Instantiate the function definition mdl::base::file_texture with the argument texture set to the database element of type mi::neuraylib::ITexture created in step 1. Actually, the true name of the database element representing the MDL function base::file_texture is not mdl::base::file_texture, but quite a long name that includes the function signature. Hence, the method mi::neuraylib::IMdl_module::get_function_overloads(const char *name, const char *param_sig) const =0 is used to retrieve the exact name of the database element without hard-coding it.
  4. The return type of mdl::base::file_texture is the structure texture_return, which does not match the desired argument type Attachable of the tint argument of the material instance. Hence, the MDL function mdl::base::texture_return.tint is used, which extracts the tint field of the texture_return structure. In this case, the helper function get_function_overloads() was not used, rather the full name mdl::base::texture_return.tint(::base::texture_return) directly.
  5. The function definition is instantiated and its s argument is set to the instance of mdl::base::file_texture.
  6. The tint argument of the material instance used for the ground plane is changed to point to the new function call. The program example_mdl.cpp shows how this step can be simplified using the helper class mi::neuraylib::Mdl_argument_editor.

example_mdl.cpp

001 /******************************************************************************
002  * © 1986, 2015 NVIDIA ARC GmbH. All rights reserved.
003  *****************************************************************************/
004 
005 // examples/example_mdl.cpp
006 //
007 // Imports a scene file, performs various MDL-related operations, and writes the rendered results
008 // to disk.
009 //
010 // The example expects the following command line arguments:
011 //
012 //   example_mdl <mdl_path>
013 //
014 // mdl_path         path to the MDL modules, e.g., iray-<version>/mdl
015 //
016 // The rendered images are written to files named "example_mdl_*.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 void configuration( mi::base::Handle<mi::neuraylib::INeuray> neuray, const char* mdl_path)
028 {
029     // Configure the neuray library. Here we set the search path for .mdl files.
030     mi::base::Handle<mi::neuraylib::IRendering_configuration> rendering_configuration(
031         neuray->get_api_component<mi::neuraylib::IRendering_configuration>());
032     check_success( rendering_configuration.is_valid_interface());
033     check_success( rendering_configuration->add_mdl_path( mdl_path) == 0);
034 
035     // Load the FreeImage, Iray Photoreal, and .mi importer plugins.
036     mi::base::Handle<mi::neuraylib::IPlugin_configuration> plugin_configuration(
037         neuray->get_api_component<mi::neuraylib::IPlugin_configuration>());
038 #ifndef MI_PLATFORM_WINDOWS
039     check_success( plugin_configuration->load_plugin_library( "freeimage.so") == 0);
040     check_success( plugin_configuration->load_plugin_library( "libiray.so") == 0);
041     check_success( plugin_configuration->load_plugin_library( "mi_importer.so") == 0);
042 #else
043     check_success( plugin_configuration->load_plugin_library( "freeimage.dll") == 0);
044     check_success( plugin_configuration->load_plugin_library( "libiray.dll") == 0);
045     check_success( plugin_configuration->load_plugin_library( "mi_importer.dll") == 0);
046 #endif
047 }
048 
049 void render_and_export(
050     mi::base::Handle<mi::neuraylib::INeuray> neuray,
051     mi::base::Handle<mi::neuraylib::ITransaction> transaction,
052     const char* uri)
053 {
054     // Create the render context using the Iray Photoreal render mode.
055     mi::base::Handle<mi::neuraylib::IScene> scene(
056         transaction->edit<mi::neuraylib::IScene>( "the_scene"));
057     mi::base::Handle<mi::neuraylib::IRender_context> render_context(
058         scene->create_render_context( transaction.get(), "iray"));
059     check_success( render_context.is_valid_interface());
060     mi::base::Handle<mi::IString> scheduler_mode( transaction->create<mi::IString>());
061     scheduler_mode->set_c_str( "batch");
062     render_context->set_option( "scheduler_mode", scheduler_mode.get());
063     scene = 0;
064 
065     // Create the render target and render the scene.
066     mi::base::Handle<mi::neuraylib::IImage_api> image_api(
067         neuray->get_api_component<mi::neuraylib::IImage_api>());
068     mi::base::Handle<mi::neuraylib::IRender_target> render_target(
069         new Render_target( image_api.get(), "Color", 512, 384));
070     check_success( render_context->render( transaction.get(), render_target.get(), 0) >= 0);
071 
072     // Write the image to disk.
073     mi::base::Handle<mi::neuraylib::IExport_api> export_api(
074         neuray->get_api_component<mi::neuraylib::IExport_api>());
075     check_success( export_api.is_valid_interface());
076     mi::base::Handle<mi::neuraylib::ICanvas> canvas( render_target->get_canvas( 0));
077     export_api->export_canvas( uri, canvas.get());
078 }
079 
080 void import_and_render_original_scene(
081     mi::base::Handle<mi::neuraylib::INeuray> neuray,
082     mi::base::Handle<mi::neuraylib::IScope> scope)
083 {
084     // Create a transaction for importing the scene file and storing the scene.
085     mi::base::Handle<mi::neuraylib::ITransaction> transaction(
086         scope->create_transaction());
087     check_success( transaction.is_valid_interface());
088 
089     // Import the scene file.
090     mi::base::Handle<mi::neuraylib::IImport_api> import_api(
091         neuray->get_api_component<mi::neuraylib::IImport_api>());
092     check_success( import_api.is_valid_interface());
093     mi::base::Handle<const mi::neuraylib::IImport_result> import_result(
094         import_api->import_elements( transaction.get(), "file:main.mi"));
095     check_success( import_result->get_error_number() == 0);
096 
097     // Create the scene object.
098     mi::base::Handle<mi::neuraylib::IScene> scene(
099         transaction->create<mi::neuraylib::IScene>( "Scene"));
100     scene->set_rootgroup(       import_result->get_rootgroup());
101     scene->set_options(         import_result->get_options());
102     scene->set_camera_instance( import_result->get_camera_inst());
103     transaction->store( scene.get(), "the_scene");
104 
105     render_and_export( neuray, transaction, "file:example_mdl_original.png");
106     transaction->commit();
107 }
108 
109 void modify_material_instance(
110     mi::base::Handle<mi::neuraylib::INeuray> neuray,
111     mi::base::Handle<mi::neuraylib::IScope> scope,
112     mi::base::Handle<mi::neuraylib::IFactory> factory)
113 {
114     // Create a transaction.
115     mi::base::Handle<mi::neuraylib::ITransaction> transaction(
116         scope->create_transaction());
117     check_success( transaction.is_valid_interface());
118 
119     {
120         // Create a new value for the "tint" argument and set it to bright grey.
121         mi::base::Handle<mi::neuraylib::IMdl_material_instance> yellow_material(
122             transaction->edit<mi::neuraylib::IMdl_material_instance>( "yellow_material"));
123         mi::base::Handle<mi::IAttachable> tint(
124             transaction->create<mi::IAttachable>( yellow_material->get_argument_type( "tint")));
125         mi::math::Spectrum bright_grey( 0.7f, 0.7f, 0.7f);
126         mi::set_value( tint.get(), bright_grey);
127 
128         // Set the new value for the "tint" argument of "yellow_material".
129         yellow_material->set_argument( "tint", tint.get());
130     }
131     // The same effect as in the preceding code block can alternatively be achieved by using the
132     // helper class Mdl_argument_editor as follows:
133     {
134         // Set the "tint" argument of "yellow_material" to bright grey.
135         mi::neuraylib::Mdl_argument_editor yellow_material(
136             transaction.get(), "yellow_material", factory.get());
137         check_success(
138             yellow_material.set_value( "tint", mi::math::Spectrum( 0.7f, 0.7f, 0.7f)) == 0);
139 
140     }
141 
142     render_and_export( neuray, transaction, "file:example_mdl_modified_material_argument.png");
143     transaction->commit();
144 }
145 
146 void create_new_material_instance(
147     mi::base::Handle<mi::neuraylib::INeuray> neuray,
148     mi::base::Handle<mi::neuraylib::IScope> scope,
149     mi::base::Handle<mi::neuraylib::IFactory> /*factory*/)
150 {
151     // Create a transaction.
152     mi::base::Handle<mi::neuraylib::ITransaction> transaction(
153         scope->create_transaction());
154     check_success( transaction.is_valid_interface());
155 
156     {
157         // Prepare the arguments for the new material instance: set the "tint" argument to white.
158         mi::base::Handle<const mi::neuraylib::IMdl_material_definition> material_definition(
159             transaction->access<mi::neuraylib::IMdl_material_definition>(
160                 "mdl::main::diffuse_material"));
161         mi::base::Handle<mi::neuraylib::IAttribute_container> arguments(
162             transaction->create<mi::neuraylib::IAttribute_container>( "Attribute_container"));
163         mi::base::Handle<mi::IAttachable> tint( arguments->create_attribute<mi::IAttachable>(
164             "tint", material_definition->get_argument_type( "tint")));
165         mi::math::Spectrum white( 1.0f, 1.0f, 1.0f);
166         mi::set_value( tint.get(), white);
167 
168         // Create a material instance of the material definition "mdl::main::diffuse_material" with
169         // the just prepared arguments.
170         mi::Sint32 result;
171         mi::base::Handle<mi::neuraylib::IMdl_material_instance> material_instance(
172             material_definition->create_material_instance( arguments.get(), &result));
173         check_success( result == 0);
174         transaction->store( material_instance.get(), "white_material");
175 
176         // Attach the newly created material instance to the scene element "cube_instance", thereby
177         // replacing the existing material instance "yellow_material".
178         mi::base::Handle<mi::neuraylib::IInstance> instance(
179             transaction->edit<mi::neuraylib::IInstance>( "cube_instance"));
180         mi::base::Handle<mi::IArray> material( instance->edit_attribute<mi::IArray>( "material"));
181         check_success(
182             mi::set_value( material.get(), static_cast<mi::Size>( 0), "white_material") == 0);
183     }
184 
185     render_and_export( neuray, transaction, "file:example_mdl_new_material_instance.png");
186     transaction->commit();
187 }
188 
189 void attach_function_call(
190     mi::base::Handle<mi::neuraylib::INeuray> neuray,
191     mi::base::Handle<mi::neuraylib::IScope> scope,
192     mi::base::Handle<mi::neuraylib::IFactory> factory)
193 {
194     // Create a transaction.
195     mi::base::Handle<mi::neuraylib::ITransaction> transaction(
196         scope->create_transaction());
197     check_success( transaction.is_valid_interface());
198 
199     {
200         // Create a DB element for the image and the texture referencing it.
201         mi::base::Handle<mi::neuraylib::IImage> image(
202             transaction->create<mi::neuraylib::IImage>( "Image"));
203         check_success( image->reset_file( "nvidia_logo.png") == 0);
204         transaction->store( image.get(), "nvidia_image");
205         mi::base::Handle<mi::neuraylib::ITexture> texture(
206             transaction->create<mi::neuraylib::ITexture>( "Texture"));
207         texture->set_image( "nvidia_image");
208         transaction->store( texture.get(), "nvidia_texture");
209     }
210     {
211         // Import the "base.mdl" module.
212         mi::base::Handle<mi::neuraylib::IImport_api> import_api(
213             neuray->get_api_component<mi::neuraylib::IImport_api>());
214         mi::base::Handle<const mi::neuraylib::IImport_result> import_result(
215             import_api->import_elements( transaction.get(), "${shader}/base.mdl"));
216         check_success( import_result->get_error_number() == 0);
217     }
218     {
219         // Lookup the exact name of the DB element for the MDL function "base::file_texture".
220         mi::base::Handle<const mi::neuraylib::IMdl_module> module(
221             transaction->access<mi::neuraylib::IMdl_module>( "mdl::base"));
222         mi::base::Handle<const mi::IArray> overloads(
223             module->get_function_overloads( "mdl::base::file_texture"));
224         check_success( overloads->get_length() == 1);
225         mi::base::Handle<const mi::IString> file_texture_name(
226             overloads->get_element<mi::IString>( static_cast<mi::Uint32>( 0)));
227 
228         // Prepare the arguments of the function call for "mdl::base::file_texture": set the
229         // "texture" argument to the "nvidia_texture" texture.
230         mi::base::Handle<const mi::neuraylib::IMdl_function_definition> function_definition(
231             transaction->access<mi::neuraylib::IMdl_function_definition>(
232                 file_texture_name->get_c_str()));
233         mi::base::Handle<mi::neuraylib::IAttribute_container> arguments(
234             transaction->create<mi::neuraylib::IAttribute_container>( "Attribute_container"));
235         mi::base::Handle<mi::IAttachable> texture( arguments->create_attribute<mi::IAttachable>(
236             "texture", function_definition->get_argument_type( "texture")));
237         mi::base::Handle<mi::IRef> texture_value( texture->get_value<mi::IRef>());
238         check_success( mi::set_value( texture_value.get(), "nvidia_texture") == 0);
239 
240         // Create a function call from the function definition "mdl::base::file_texture" with the
241         // just prepared arguments.
242         mi::Sint32 result;
243         mi::base::Handle<mi::neuraylib::IMdl_function_call> function_call(
244             function_definition->create_function_call( arguments.get(), &result));
245         check_success( result == 0);
246         function_definition = 0;
247         transaction->store( function_call.get(), "file_texture_call");
248     }
249     {
250         // Prepare the arguments of the function call for "mdl::base::texture_return.tint": set the
251         // "s" argument to the "file_texture_call" function call.
252         mi::base::Handle<const mi::neuraylib::IMdl_function_definition> function_definition(
253             transaction->access<mi::neuraylib::IMdl_function_definition>(
254                 "mdl::base::texture_return.tint(::base::texture_return)"));
255         mi::base::Handle<mi::neuraylib::IAttribute_container> arguments(
256             transaction->create<mi::neuraylib::IAttribute_container>( "Attribute_container"));
257         mi::base::Handle<mi::IAttachable> s( arguments->create_attribute<mi::IAttachable>(
258             "s", function_definition->get_argument_type( "s")));
259         check_success( mi::set_value( s.get(), "file_texture_call") == 0);
260 
261         // Create a function call from the function definition "mdl::base::file_texture" with the
262         // just prepared arguments.
263         mi::Sint32 result;
264         mi::base::Handle<mi::neuraylib::IMdl_function_call> function_call(
265             function_definition->create_function_call( arguments.get(), &result));
266         check_success( result == 0);
267         transaction->store( function_call.get(), "texture_return.tint_call");
268     }
269     {
270         // Create a new value for the "tint" argument and set it to the "texture_return.tint_call"
271         // function call.
272         mi::base::Handle<mi::neuraylib::IMdl_material_instance> grey_material(
273             transaction->edit<mi::neuraylib::IMdl_material_instance>( "grey_material"));
274         mi::base::Handle<mi::IAttachable> tint(
275             transaction->create<mi::IAttachable>( grey_material->get_argument_type( "tint")));
276         check_success( mi::set_value( tint.get(), "texture_return.tint_call") == 0);
277 
278         // Set the new value for the "tint" argument of "grey_material".
279         grey_material->set_argument( "tint", tint.get());
280     }
281     // The same effect as in the preceding code block can alternatively be achieved by using the
282     // helper class Mdl_argument_editor as follows:
283     {
284         // Attach "texture_return.tint_call" to the "tint" argument of "grey_material".
285         mi::neuraylib::Mdl_argument_editor grey_material(
286             transaction.get(), "grey_material", factory.get());
287         check_success( grey_material.set_attachment( "tint", "texture_return.tint_call") == 0);
288     }
289 
290     render_and_export( neuray, transaction, "file:example_mdl_attached_function_call.png");
291     transaction->commit();
292 }
293 
294 void rendering( mi::base::Handle<mi::neuraylib::INeuray> neuray)
295 {
296     // Get the database, the global scope of the database, and create a transaction in the global
297     // scope.
298     mi::base::Handle<mi::neuraylib::IDatabase> database(
299         neuray->get_api_component<mi::neuraylib::IDatabase>());
300     check_success( database.is_valid_interface());
301     mi::base::Handle<mi::neuraylib::IScope> scope(
302         database->get_global_scope());
303 
304    // Import the scene and render it.
305    import_and_render_original_scene( neuray, scope);
306 
307    // Perform various MDL-related modifications, render the scene, and export the image to disk.
308    mi::base::Handle<mi::neuraylib::IFactory> factory(
309        neuray->get_api_component<mi::neuraylib::IFactory>());
310    modify_material_instance( neuray, scope, factory);
311    create_new_material_instance( neuray, scope, factory);
312    attach_function_call( neuray, scope, factory);
313 }
314 
315 int main( int argc, char* argv[])
316 {
317     // Collect command line parameters
318     if( argc != 2) {
319         std::cerr << "Usage: example_mdl <mdl_path>" << std::endl;
320         keep_console_open();
321         return EXIT_FAILURE;
322     }
323     const char* mdl_path = argv[1];
324 
325     // Access the neuray library
326     mi::base::Handle<mi::neuraylib::INeuray> neuray( load_and_get_ineuray());
327     check_success( neuray.is_valid_interface());
328 
329     // Configure the neuray library
330     configuration( neuray, mdl_path);
331 
332     // Start the neuray library
333     check_success( neuray->start() == 0);
334 
335     // Do the actual rendering
336     rendering( neuray);
337 
338     // Shut down the neuray library
339     check_success( neuray->shutdown() == 0);
340     neuray = 0;
341 
342     // Unload the neuray library
343     check_success( unload());
344 
345     keep_console_open();
346     return EXIT_SUCCESS;
347 }