Editing and creating materials
- typical MDL-related tasks such as modifying inputs of material instances, creating new material instances, and attaching function calls to material inputs:
- 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:
- 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.
- 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.
- 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:
- Create the necessary database elements of type mi::IImage and mi::ITexture, which will hold the texture to be used.
- Import the MDL module base, which contains the MDL function mdl::base::file_texture to be used.
- 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.
- 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.
- The function definition is instantiated and its s argument is set to the instance of mdl::base::file_texture.
- 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 }