Creating freeform surfaces
This topic introduces:
- Core concepts related to the mi::neuraylib::IFreeform_surface interface and the use of the transform matrix of a sphere to create an ellipsoidal shape:
- The program example_freeform_surface.cpp, which serves as an example implementation. The program imports a partial scene containing definitions for the camera, a light, and some geometry (a ground plane and a yellow cube). Using the API, it then creates two freeform surfaces and adds them to the scene.
Creating a freeform surface
In example_freeform_surface.cpp, the function create_sphere() is used to create a freeform surface that represents a sphere. A representation as Bezier surface of degree 2 in both directions is used. The sphere is centered at the origin; its radius is 1.0.
Adding freeform objects to a scene
In example_freeform_surface.cpp, two freeform objects are added to the scene: a red sphere, and a blue partial ellipsoid. The freeform surface of the blue ellipsoid represents a sphere; the ellipsoidal shape is created by using its transformation matrix. The parameter range in U-direction is restricted to [0.0, 0.5] to cut away the part of the ellipsoid that lies below the ground plane.
example_freeform_surface.cpp
001 /****************************************************************************** 002 * © 1986, 2016 NVIDIA Corporation. All rights reserved. 003 *****************************************************************************/ 004 005 // examples/example_freeform_surface.cpp 006 // 007 // Creates a three instances of a freeform surface with different approximation levels. 008 // 009 // The example expects the following command line arguments: 010 // 011 // example_freeform_surface <mdl_path> 012 // 013 // mdl_path path to the MDL modules, e.g., iray-<version>/mdl 014 // 015 // The rendered image is written to a file named "example_freeform_surface.png". 016 017 #include <mi/neuraylib.h> 018 019 // Include code shared by all examples. 020 #include "example_shared.h" 021 // Include an implementation of IRender_target. 022 #include "example_render_target_simple.h" 023 024 #include <iostream> 025 026 // Create a sphere as a freeform surface. 027 mi::neuraylib::IFreeform_surface* create_sphere( 028 mi::neuraylib::ITransaction* transaction, mi::neuraylib::Basis_type basis) 029 { 030 const mi::Size n_points_u = 9; 031 const mi::Size n_points_v = 5; 032 033 // The control points 034 mi::Float32_3 control_points[n_points_u * n_points_v] = { 035 mi::Float32_3( 0.0f, 0.0f, -1.0f), 036 mi::Float32_3( 0.0f, 0.0f, -1.0f), 037 mi::Float32_3( 0.0f, 0.0f, -1.0f), 038 mi::Float32_3( 0.0f, 0.0f, -1.0f), 039 mi::Float32_3( 0.0f, 0.0f, -1.0f), 040 mi::Float32_3( 0.0f, 0.0f, -1.0f), 041 mi::Float32_3( 0.0f, 0.0f, -1.0f), 042 mi::Float32_3( 0.0f, 0.0f, -1.0f), 043 mi::Float32_3( 0.0f, 0.0f, -1.0f), 044 045 mi::Float32_3( 1.0f, 0.0f, -1.0f), 046 mi::Float32_3( 1.0f, 1.0f, -1.0f), 047 mi::Float32_3( 0.0f, 1.0f, -1.0f), 048 mi::Float32_3( -1.0f, 1.0f, -1.0f), 049 mi::Float32_3( -1.0f, 0.0f, -1.0f), 050 mi::Float32_3( -1.0f, -1.0f, -1.0f), 051 mi::Float32_3( 0.0f, -1.0f, -1.0f), 052 mi::Float32_3( 1.0f, -1.0f, -1.0f), 053 mi::Float32_3( 1.0f, 0.0f, -1.0f), 054 055 mi::Float32_3( 1.0f, 0.0f, 0.0f), 056 mi::Float32_3( 1.0f, 1.0f, 0.0f), 057 mi::Float32_3( 0.0f, 1.0f, 0.0f), 058 mi::Float32_3( -1.0f, 1.0f, 0.0f), 059 mi::Float32_3( -1.0f, 0.0f, 0.0f), 060 mi::Float32_3( -1.0f, -1.0f, 0.0f), 061 mi::Float32_3( 0.0f, -1.0f, 0.0f), 062 mi::Float32_3( 1.0f, -1.0f, 0.0f), 063 mi::Float32_3( 1.0f, 0.0f, 0.0f), 064 065 mi::Float32_3( 1.0f, 0.0f, 1.0f), 066 mi::Float32_3( 1.0f, 1.0f, 1.0f), 067 mi::Float32_3( 0.0f, 1.0f, 1.0f), 068 mi::Float32_3( -1.0f, 1.0f, 1.0f), 069 mi::Float32_3( -1.0f, 0.0f, 1.0f), 070 mi::Float32_3( -1.0f, -1.0f, 1.0f), 071 mi::Float32_3( 0.0f, -1.0f, 1.0f), 072 mi::Float32_3( 1.0f, -1.0f, 1.0f), 073 mi::Float32_3( 1.0f, 0.0f, 1.0f), 074 075 mi::Float32_3( 0.0f, 0.0f, 1.0f), 076 mi::Float32_3( 0.0f, 0.0f, 1.0f), 077 mi::Float32_3( 0.0f, 0.0f, 1.0f), 078 mi::Float32_3( 0.0f, 0.0f, 1.0f), 079 mi::Float32_3( 0.0f, 0.0f, 1.0f), 080 mi::Float32_3( 0.0f, 0.0f, 1.0f), 081 mi::Float32_3( 0.0f, 0.0f, 1.0f), 082 mi::Float32_3( 0.0f, 0.0f, 1.0f), 083 mi::Float32_3( 0.0f, 0.0f, 1.0f) 084 }; 085 086 // Create an empty freeform surface with one surface 087 mi::neuraylib::IFreeform_surface* sphere 088 = transaction->create<mi::neuraylib::IFreeform_surface>( "Freeform_surface"); 089 mi::base::Handle<mi::neuraylib::ISurface> surface( sphere->add_surface()); 090 091 // There are two choices for the basis used to parameterize the freeform surface: bezier and 092 // b-spline. The other data depends on that choice, except for the control points which are 093 // identical (for this specific case). 094 095 if( basis == mi::neuraylib::BASIS_BEZIER) { 096 097 // Set up the basis type, degrees, and number of patches 098 surface->set_basis_type( mi::neuraylib::BASIS_BEZIER); 099 surface->set_degree( mi::neuraylib::DIMENSION_U, 2); 100 surface->set_degree( mi::neuraylib::DIMENSION_V, 2); 101 surface->set_patches_size( mi::neuraylib::DIMENSION_U, 4); 102 surface->set_patches_size( mi::neuraylib::DIMENSION_V, 2); 103 104 // Set up the parameter vectors 105 surface->set_parameter( mi::neuraylib::DIMENSION_U, 0, 0.0f); 106 surface->set_parameter( mi::neuraylib::DIMENSION_U, 1, 0.25f); 107 surface->set_parameter( mi::neuraylib::DIMENSION_U, 2, 0.5f); 108 surface->set_parameter( mi::neuraylib::DIMENSION_U, 3, 0.75f); 109 surface->set_parameter( mi::neuraylib::DIMENSION_U, 4, 1.0f); 110 surface->set_parameter( mi::neuraylib::DIMENSION_V, 0, 0.0f); 111 surface->set_parameter( mi::neuraylib::DIMENSION_V, 1, 0.5f); 112 surface->set_parameter( mi::neuraylib::DIMENSION_V, 2, 1.0f); 113 114 // The weights 115 mi::Float32 weights[n_points_u * n_points_v] = { 116 1.0f, 1.0f, 2.0f, 1.0f, 1.0f, 1.0f, 2.0f, 1.0f, 1.0f, 117 1.0f, 1.0f, 2.0f, 1.0f, 1.0f, 1.0f, 2.0f, 1.0f, 1.0f, 118 2.0f, 2.0f, 4.0f, 2.0f, 2.0f, 2.0f, 4.0f, 2.0f, 2.0f, 119 1.0f, 1.0f, 2.0f, 1.0f, 1.0f, 1.0f, 2.0f, 1.0f, 1.0f, 120 1.0f, 1.0f, 2.0f, 1.0f, 1.0f, 1.0f, 2.0f, 1.0f, 1.0f 121 }; 122 123 // Set up the control points and weights 124 for( mi::Uint32 i = 0; i < n_points_u; ++i) 125 for( mi::Uint32 j = 0; j < n_points_v; ++j) { 126 surface->set_control_point( i, j, control_points[j * n_points_u + i]); 127 surface->set_weight( i, j, weights[j * n_points_u + i]); 128 } 129 130 } else { 131 132 // Set up the basis type, degrees, and number of patches 133 surface->set_basis_type( mi::neuraylib::BASIS_BSPLINE); 134 surface->set_degree( mi::neuraylib::DIMENSION_U, 2); 135 surface->set_degree( mi::neuraylib::DIMENSION_V, 2); 136 surface->set_patches_size( mi::neuraylib::DIMENSION_U, 7); 137 surface->set_patches_size( mi::neuraylib::DIMENSION_V, 3); 138 139 // Set up the parameter vectors 140 surface->set_parameter( mi::neuraylib::DIMENSION_U, 0, 0.0f); 141 surface->set_parameter( mi::neuraylib::DIMENSION_U, 1, 0.0f); 142 surface->set_parameter( mi::neuraylib::DIMENSION_U, 2, 0.0f); 143 surface->set_parameter( mi::neuraylib::DIMENSION_U, 3, 0.25f); 144 surface->set_parameter( mi::neuraylib::DIMENSION_U, 4, 0.25f); 145 surface->set_parameter( mi::neuraylib::DIMENSION_U, 5, 0.5f); 146 surface->set_parameter( mi::neuraylib::DIMENSION_U, 6, 0.5f); 147 surface->set_parameter( mi::neuraylib::DIMENSION_U, 7, 0.75f); 148 surface->set_parameter( mi::neuraylib::DIMENSION_U, 8, 0.75f); 149 surface->set_parameter( mi::neuraylib::DIMENSION_U, 9, 1.0f); 150 surface->set_parameter( mi::neuraylib::DIMENSION_U, 10, 1.0f); 151 surface->set_parameter( mi::neuraylib::DIMENSION_U, 11, 1.0f); 152 surface->set_parameter( mi::neuraylib::DIMENSION_V, 0, 0.0f); 153 surface->set_parameter( mi::neuraylib::DIMENSION_V, 1, 0.0f); 154 surface->set_parameter( mi::neuraylib::DIMENSION_V, 2, 0.0f); 155 surface->set_parameter( mi::neuraylib::DIMENSION_V, 3, 0.5f); 156 surface->set_parameter( mi::neuraylib::DIMENSION_V, 4, 0.5f); 157 surface->set_parameter( mi::neuraylib::DIMENSION_V, 5, 1.0f); 158 surface->set_parameter( mi::neuraylib::DIMENSION_V, 6, 1.0f); 159 surface->set_parameter( mi::neuraylib::DIMENSION_V, 7, 1.0f); 160 161 // The weights 162 mi::Float32 w = 0.5f * std::sqrt( 2.0f); 163 mi::Float32 weights[n_points_u * n_points_v] = { 164 1.0f, w, 1.0f, w, 1.0f, w, 1.0f, w, 1.0f, 165 w, 0.5f, w, 0.5f, w, 0.5f, w, 0.5f, w, 166 1.0f, w, 1.0f, w, 1.0f, w, 1.0f, w, 1.0f, 167 w, 0.5f, w, 0.5f, w, 0.5f, w, 0.5f, w, 168 1.0f, w, 1.0f, w, 1.0f, w, 1.0f, w, 1.0f 169 }; 170 171 // Set up the control points and weights 172 for( mi::Uint32 i = 0; i < n_points_u; ++i) 173 for( mi::Uint32 j = 0; j < n_points_v; ++j) { 174 surface->set_control_point( i, j, control_points[j * n_points_u + i]); 175 surface->set_weight( i, j, weights[j * n_points_u + i]); 176 } 177 } 178 179 surface = 0; 180 181 // Set up the approximation granularity 182 mi::base::Handle<mi::IStructure> approx( 183 sphere->create_attribute<mi::IStructure>( "approx", "Approx")); 184 mi::base::Handle<mi::ISint8> method( approx->get_value<mi::ISint8>( "method")); 185 method->set_value( 0); 186 method = 0; 187 mi::base::Handle<mi::IFloat32> const_u( approx->get_value<mi::IFloat32>( "const_u")); 188 const_u->set_value( 12.0f); 189 const_u = 0; 190 mi::base::Handle<mi::IFloat32> const_v( approx->get_value<mi::IFloat32>( "const_v")); 191 const_v->set_value( 12.0f); 192 const_v = 0; 193 approx = 0; 194 195 return sphere; 196 } 197 198 // Add an instance of a red sphere and an instance of a blue half-sphere. 199 void setup_scene( mi::neuraylib::ITransaction* transaction, const char* rootgroup) 200 { 201 // Create the red sphere 202 mi::base::Handle<mi::neuraylib::IFreeform_surface> mesh( 203 create_sphere( transaction, mi::neuraylib::BASIS_BEZIER)); 204 transaction->store( mesh.get(), "mesh_red"); 205 206 // Create the instance for the red sphere 207 mi::base::Handle<mi::neuraylib::IInstance> instance( 208 transaction->create<mi::neuraylib::IInstance>( "Instance")); 209 instance->attach( "mesh_red"); 210 211 // Set the transformation matrix, the visible attribute, and the material 212 mi::Float64_4_4 matrix( 1.0); 213 matrix.translate( -0.6, -0.5, 0.7); 214 mi::Float64_4_4 matrix_scale( 2.0, 0, 0, 0, 0, 2.0, 0, 0, 0, 0, 2.0, 0, 0, 0, 0, 1.0); 215 matrix *= matrix_scale; 216 instance->set_matrix( matrix); 217 218 mi::base::Handle<mi::IBoolean> visible( 219 instance->create_attribute<mi::IBoolean>( "visible", "Boolean")); 220 visible->set_value( true); 221 222 mi::base::Handle<mi::IRef> material( instance->create_attribute<mi::IRef>( "material", "Ref")); 223 material->set_reference( "red_material"); 224 225 transaction->store( instance.get(), "instance_red"); 226 227 // And attach the instance to the root group 228 mi::base::Handle<mi::neuraylib::IGroup> group( 229 transaction->edit<mi::neuraylib::IGroup>( rootgroup)); 230 group->attach( "instance_red"); 231 group = 0; 232 233 // Create the blue sphere 234 mesh = create_sphere( transaction, mi::neuraylib::BASIS_BSPLINE); 235 236 // Trim the sphere to the part above the ground plane 237 mi::base::Handle<mi::neuraylib::ISurface> surface( 238 mesh->edit_surface( mi::neuraylib::Surface_handle( 0))); 239 surface->set_range( mi::neuraylib::DIMENSION_U, 0.0f, 0.5f); 240 surface = 0; 241 242 transaction->store( mesh.get(), "mesh_blue"); 243 244 // Create the instance for the blue sphere 245 instance = transaction->create<mi::neuraylib::IInstance>( "Instance"); 246 instance->attach( "mesh_blue"); 247 248 // Set the transformation matrix, the visible attribute, and the material 249 matrix = mi::Float64_4_4( 1.0); 250 matrix.translate( -0.6, 0.0, -1.1); 251 matrix_scale = mi::Float64_4_4( 2.0, 0, 0, 0, 0, 1.0, 0, 0, 0, 0, 2.0, 0, 0, 0, 0, 1.0); 252 matrix *= matrix_scale; 253 instance->set_matrix( matrix); 254 255 visible = instance->create_attribute<mi::IBoolean>( "visible", "Boolean"); 256 visible->set_value( true); 257 258 material = instance->create_attribute<mi::IRef>( "material", "Ref"); 259 material->set_reference( "blue_material"); 260 261 transaction->store( instance.get(), "instance_blue"); 262 263 // And attach the instance to the root group 264 group = transaction->edit<mi::neuraylib::IGroup>( rootgroup); 265 group->attach( "instance_blue"); 266 } 267 268 void configuration( mi::base::Handle<mi::neuraylib::INeuray> neuray, const char* mdl_path) 269 { 270 // Configure the neuray library. Here we set the search path for .mdl files. 271 mi::base::Handle<mi::neuraylib::IRendering_configuration> rendering_configuration( 272 neuray->get_api_component<mi::neuraylib::IRendering_configuration>()); 273 check_success( rendering_configuration.is_valid_interface()); 274 check_success( rendering_configuration->add_mdl_path( mdl_path) == 0); 275 276 // Load the FreeImage, Iray Photoreal, and .mi importer plugins. 277 mi::base::Handle<mi::neuraylib::IPlugin_configuration> plugin_configuration( 278 neuray->get_api_component<mi::neuraylib::IPlugin_configuration>()); 279 #ifndef MI_PLATFORM_WINDOWS 280 check_success( plugin_configuration->load_plugin_library( "freeimage.so") == 0); 281 check_success( plugin_configuration->load_plugin_library( "libiray.so") == 0); 282 check_success( plugin_configuration->load_plugin_library( "mi_importer.so") == 0); 283 #else 284 check_success( plugin_configuration->load_plugin_library( "freeimage.dll") == 0); 285 check_success( plugin_configuration->load_plugin_library( "libiray.dll") == 0); 286 check_success( plugin_configuration->load_plugin_library( "mi_importer.dll") == 0); 287 #endif 288 } 289 290 void rendering( mi::base::Handle<mi::neuraylib::INeuray> neuray) 291 { 292 // Get the database, the global scope of the database, and create a transaction in the global 293 // scope for importing the scene file and storing the scene. 294 mi::base::Handle<mi::neuraylib::IDatabase> database( 295 neuray->get_api_component<mi::neuraylib::IDatabase>()); 296 check_success( database.is_valid_interface()); 297 mi::base::Handle<mi::neuraylib::IScope> scope( 298 database->get_global_scope()); 299 mi::base::Handle<mi::neuraylib::ITransaction> transaction( 300 scope->create_transaction()); 301 check_success( transaction.is_valid_interface()); 302 303 // Import the scene file 304 mi::base::Handle<mi::neuraylib::IImport_api> import_api( 305 neuray->get_api_component<mi::neuraylib::IImport_api>()); 306 check_success( import_api.is_valid_interface()); 307 mi::base::Handle<const mi::neuraylib::IImport_result> import_result( 308 import_api->import_elements( transaction.get(), "file:main.mi")); 309 check_success( import_result->get_error_number() == 0); 310 311 // Add two instances of freeform surfaces 312 setup_scene( transaction.get(), import_result->get_rootgroup()); 313 314 // Create the scene object 315 mi::base::Handle<mi::neuraylib::IScene> scene( 316 transaction->create<mi::neuraylib::IScene>( "Scene")); 317 scene->set_rootgroup( import_result->get_rootgroup()); 318 scene->set_options( import_result->get_options()); 319 scene->set_camera_instance( import_result->get_camera_inst()); 320 transaction->store( scene.get(), "the_scene"); 321 scene = 0; 322 323 // Create the render context using the Iray Photoreal render mode 324 scene = transaction->edit<mi::neuraylib::IScene>( "the_scene"); 325 mi::base::Handle<mi::neuraylib::IRender_context> render_context( 326 scene->create_render_context( transaction.get(), "iray")); 327 check_success( render_context.is_valid_interface()); 328 mi::base::Handle<mi::IString> scheduler_mode( transaction->create<mi::IString>()); 329 scheduler_mode->set_c_str( "batch"); 330 render_context->set_option( "scheduler_mode", scheduler_mode.get()); 331 scene = 0; 332 333 // Create the render target and render the scene 334 mi::base::Handle<mi::neuraylib::IImage_api> image_api( 335 neuray->get_api_component<mi::neuraylib::IImage_api>()); 336 mi::base::Handle<mi::neuraylib::IRender_target> render_target( 337 new Render_target( image_api.get(), "Color", 512, 384)); 338 check_success( render_context->render( transaction.get(), render_target.get(), 0) >= 0); 339 340 // Write the image to disk 341 mi::base::Handle<mi::neuraylib::IExport_api> export_api( 342 neuray->get_api_component<mi::neuraylib::IExport_api>()); 343 check_success( export_api.is_valid_interface()); 344 mi::base::Handle<mi::neuraylib::ICanvas> canvas( render_target->get_canvas( 0)); 345 export_api->export_canvas( "file:example_freeform_surface.png", canvas.get()); 346 347 transaction->commit(); 348 } 349 350 int main( int argc, char* argv[]) 351 { 352 // Collect command line parameters 353 if( argc != 2) { 354 std::cerr << "Usage: example_freeform_surface <mdl_path>" << std::endl; 355 keep_console_open(); 356 return EXIT_FAILURE; 357 } 358 const char* mdl_path = argv[1]; 359 360 // Access the neuray library 361 mi::base::Handle<mi::neuraylib::INeuray> neuray( load_and_get_ineuray()); 362 check_success( neuray.is_valid_interface()); 363 364 // Configure the neuray library 365 configuration( neuray, mdl_path); 366 367 // Start the neuray library 368 mi::Sint32 result = neuray->start(); 369 check_start_success( result); 370 371 // Do the actual rendering 372 rendering( neuray); 373 374 // Shut down the neuray library 375 check_success( neuray->shutdown() == 0); 376 neuray = 0; 377 378 // Unload the neuray library 379 check_success( unload()); 380 381 keep_console_open(); 382 return EXIT_SUCCESS; 383 }