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::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::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::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::IImage and mi::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::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::IMdl_module::get_function_overloads() 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::Mdl_argument_editor.

example_mdl.cpp

001 /******************************************************************************
002  * © 1986, 2014 NVIDIA Corporation. 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::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::IMdl_material_instance> yellow_material(
122             transaction->edit<mi::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::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::IMdl_material_definition> material_definition(
159             transaction->access<mi::IMdl_material_definition>( "mdl::main::diffuse_material"));
160         mi::base::Handle<mi::IAttribute_container> arguments(
161             transaction->create<mi::IAttribute_container>( "Attribute_container"));
162         mi::base::Handle<mi::IAttachable> tint( arguments->create_attribute<mi::IAttachable>(
163             "tint", material_definition->get_argument_type( "tint")));
164         mi::math::Spectrum white( 1.0f, 1.0f, 1.0f);
165         mi::set_value( tint.get(), white);
166 
167         // Create a material instance of the material definition "mdl::main::diffuse_material" with
168         // the just prepared arguments.
169         mi::Sint32 result;
170         mi::base::Handle<mi::IMdl_material_instance> material_instance(
171             material_definition->create_material_instance( arguments.get(), &result));
172         check_success( result == 0);
173         transaction->store( material_instance.get(), "white_material");
174 
175         // Attach the newly created material instance to the scene element "cube_instance", thereby
176         // replacing the existing material instance "yellow_material".
177         mi::base::Handle<mi::IInstance> instance(
178             transaction->edit<mi::IInstance>( "cube_instance"));
179         mi::base::Handle<mi::IArray> material( instance->edit_attribute<mi::IArray>( "material"));
180         check_success(
181             mi::set_value( material.get(), static_cast<mi::Size>( 0), "white_material") == 0);
182     }
183 
184     render_and_export( neuray, transaction, "file:example_mdl_new_material_instance.png");
185     transaction->commit();
186 }
187 
188 void attach_function_call(
189     mi::base::Handle<mi::neuraylib::INeuray> neuray,
190     mi::base::Handle<mi::neuraylib::IScope> scope,
191     mi::base::Handle<mi::neuraylib::IFactory> factory)
192 {
193     // Create a transaction.
194     mi::base::Handle<mi::neuraylib::ITransaction> transaction(
195         scope->create_transaction());
196     check_success( transaction.is_valid_interface());
197 
198     {
199         // Create a DB element for the image and the texture referencing it.
200         mi::base::Handle<mi::IImage> image( transaction->create<mi::IImage>( "Image"));
201         check_success( image->reset_file( "nvidia_logo.png") == 0);
202         transaction->store( image.get(), "nvidia_image");
203         mi::base::Handle<mi::ITexture> texture( transaction->create<mi::ITexture>( "Texture"));
204         texture->set_image( "nvidia_image");
205         transaction->store( texture.get(), "nvidia_texture");
206     }
207     {
208         // Import the "base.mdl" module.
209         mi::base::Handle<mi::neuraylib::IImport_api> import_api(
210             neuray->get_api_component<mi::neuraylib::IImport_api>());
211         mi::base::Handle<const mi::IImport_result> import_result(
212             import_api->import_elements( transaction.get(), "${shader}/base.mdl"));
213         check_success( import_result->get_error_number() == 0);
214     }
215     {
216         // Lookup the exact name of the DB element for the MDL function "base::file_texture".
217         mi::base::Handle<const mi::IMdl_module> module(
218             transaction->access<mi::IMdl_module>( "mdl::base"));
219         mi::base::Handle<const mi::IArray> overloads(
220             module->get_function_overloads( "mdl::base::file_texture"));
221         check_success( overloads->get_length() == 1);
222         mi::base::Handle<const mi::IString> file_texture_name(
223             overloads->get_element<mi::IString>( static_cast<mi::Uint32>( 0)));
224 
225         // Prepare the arguments of the function call for "mdl::base::file_texture": set the
226         // "texture" argument to the "nvidia_texture" texture.
227         mi::base::Handle<const mi::IMdl_function_definition> function_definition(
228             transaction->access<mi::IMdl_function_definition>( file_texture_name->get_c_str()));
229         mi::base::Handle<mi::IAttribute_container> arguments(
230             transaction->create<mi::IAttribute_container>( "Attribute_container"));
231         mi::base::Handle<mi::IAttachable> texture( arguments->create_attribute<mi::IAttachable>(
232             "texture", function_definition->get_argument_type( "texture")));
233         mi::base::Handle<mi::IRef> texture_value( texture->get_value<mi::IRef>());
234         check_success( mi::set_value( texture_value.get(), "nvidia_texture") == 0);
235 
236         // Create a function call from the function definition "mdl::base::file_texture" with the
237         // just prepared arguments.
238         mi::Sint32 result;
239         mi::base::Handle<mi::IMdl_function_call> function_call(
240             function_definition->create_function_call( arguments.get(), &result));
241         check_success( result == 0);
242         function_definition = 0;
243         transaction->store( function_call.get(), "file_texture_call");
244     }
245     {
246         // Prepare the arguments of the function call for "mdl::base::texture_return.tint": set the
247         // "s" argument to the "file_texture_call" function call.
248         mi::base::Handle<const mi::IMdl_function_definition> function_definition(
249             transaction->access<mi::IMdl_function_definition>(
250                 "mdl::base::texture_return.tint(::base::texture_return)"));
251         mi::base::Handle<mi::IAttribute_container> arguments(
252             transaction->create<mi::IAttribute_container>( "Attribute_container"));
253         mi::base::Handle<mi::IAttachable> s( arguments->create_attribute<mi::IAttachable>(
254             "s", function_definition->get_argument_type( "s")));
255         check_success( mi::set_value( s.get(), "file_texture_call") == 0);
256 
257         // Create a function call from the function definition "mdl::base::file_texture" with the
258         // just prepared arguments.
259         mi::Sint32 result;
260         mi::base::Handle<mi::IMdl_function_call> function_call(
261             function_definition->create_function_call( arguments.get(), &result));
262         check_success( result == 0);
263         transaction->store( function_call.get(), "texture_return.tint_call");
264     }
265     {
266         // Create a new value for the "tint" argument and set it to the "texture_return.tint_call"
267         // function call.
268         mi::base::Handle<mi::IMdl_material_instance> grey_material(
269             transaction->edit<mi::IMdl_material_instance>( "grey_material"));
270         mi::base::Handle<mi::IAttachable> tint(
271             transaction->create<mi::IAttachable>( grey_material->get_argument_type( "tint")));
272         check_success( mi::set_value( tint.get(), "texture_return.tint_call") == 0);
273 
274         // Set the new value for the "tint" argument of "grey_material".
275         grey_material->set_argument( "tint", tint.get());
276     }
277     // The same effect as in the preceding code block can alternatively be achieved by using the
278     // helper class Mdl_argument_editor as follows:
279     {
280         // Attach "texture_return.tint_call" to the "tint" argument of "grey_material".
281         mi::Mdl_argument_editor grey_material( transaction.get(), "grey_material", factory.get());
282         check_success( grey_material.set_attachment( "tint", "texture_return.tint_call") == 0);
283     }
284 
285     render_and_export( neuray, transaction, "file:example_mdl_attached_function_call.png");
286     transaction->commit();
287 }
288 
289 void rendering( mi::base::Handle<mi::neuraylib::INeuray> neuray)
290 {
291     // Get the database and the global scope, which is the root for all transactions.
292     mi::base::Handle<mi::neuraylib::IDatabase> database(
293         neuray->get_api_component<mi::neuraylib::IDatabase>());
294     check_success( database.is_valid_interface());
295     mi::base::Handle<mi::neuraylib::IScope> scope(
296         database->get_global_scope());
297 
298    // Import the scene and render it.
299    import_and_render_original_scene( neuray, scope);
300 
301    // Perform various MDL-related modifications, render the scene, and export the image to disk.
302    mi::base::Handle<mi::neuraylib::IFactory> factory(
303        neuray->get_api_component<mi::neuraylib::IFactory>());
304    modify_material_instance( neuray, scope, factory);
305    create_new_material_instance( neuray, scope, factory);
306    attach_function_call( neuray, scope, factory);
307 }
308 
309 int main( int argc, char* argv[])
310 {
311     // Collect command line parameters
312     if( argc != 2) {
313         std::cerr << "Usage: example_mdl <mdl_path>" << std::endl;
314         keep_console_open();
315         return EXIT_FAILURE;
316     }
317     const char* mdl_path = argv[1];
318 
319     // Access the neuray library
320     mi::base::Handle<mi::neuraylib::INeuray> neuray( load_and_get_ineuray());
321     check_success( neuray.is_valid_interface());
322 
323     // Configure the neuray library
324     configuration( neuray, mdl_path);
325 
326     // Start the neuray library
327     check_success( neuray->start() == 0);
328 
329     // Do the actual rendering
330     rendering( neuray);
331 
332     // Shut down the neuray library
333     check_success( neuray->shutdown() == 0);
334     neuray = 0;
335 
336     // Unload the neuray library
337     check_success( unload());
338 
339     keep_console_open();
340     return EXIT_SUCCESS;
341 }