Creating freeform surfaces
This topic introduces:
- Core concepts related to the mi::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, 2014 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::IFreeform_surface* create_sphere( 028 mi::neuraylib::ITransaction* transaction, mi::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::IFreeform_surface* sphere 088 = transaction->create<mi::IFreeform_surface>( "Freeform_surface"); 089 mi::base::Handle<mi::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::BASIS_BEZIER) { 096 097 // Set up the basis type, degrees, and number of patches 098 surface->set_basis_type( mi::BASIS_BEZIER); 099 surface->set_degree( mi::DIMENSION_U, 2); 100 surface->set_degree( mi::DIMENSION_V, 2); 101 surface->set_patches_size( mi::DIMENSION_U, 4); 102 surface->set_patches_size( mi::DIMENSION_V, 2); 103 104 // Set up the parameter vectors 105 surface->set_parameter( mi::DIMENSION_U, 0, 0.0f); 106 surface->set_parameter( mi::DIMENSION_U, 1, 0.25f); 107 surface->set_parameter( mi::DIMENSION_U, 2, 0.5f); 108 surface->set_parameter( mi::DIMENSION_U, 3, 0.75f); 109 surface->set_parameter( mi::DIMENSION_U, 4, 1.0f); 110 surface->set_parameter( mi::DIMENSION_V, 0, 0.0f); 111 surface->set_parameter( mi::DIMENSION_V, 1, 0.5f); 112 surface->set_parameter( mi::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::BASIS_BSPLINE); 134 surface->set_degree( mi::DIMENSION_U, 2); 135 surface->set_degree( mi::DIMENSION_V, 2); 136 surface->set_patches_size( mi::DIMENSION_U, 7); 137 surface->set_patches_size( mi::DIMENSION_V, 3); 138 139 // Set up the parameter vectors 140 surface->set_parameter( mi::DIMENSION_U, 0, 0.0f); 141 surface->set_parameter( mi::DIMENSION_U, 1, 0.0f); 142 surface->set_parameter( mi::DIMENSION_U, 2, 0.0f); 143 surface->set_parameter( mi::DIMENSION_U, 3, 0.25f); 144 surface->set_parameter( mi::DIMENSION_U, 4, 0.25f); 145 surface->set_parameter( mi::DIMENSION_U, 5, 0.5f); 146 surface->set_parameter( mi::DIMENSION_U, 6, 0.5f); 147 surface->set_parameter( mi::DIMENSION_U, 7, 0.75f); 148 surface->set_parameter( mi::DIMENSION_U, 8, 0.75f); 149 surface->set_parameter( mi::DIMENSION_U, 9, 1.0f); 150 surface->set_parameter( mi::DIMENSION_U, 10, 1.0f); 151 surface->set_parameter( mi::DIMENSION_U, 11, 1.0f); 152 surface->set_parameter( mi::DIMENSION_V, 0, 0.0f); 153 surface->set_parameter( mi::DIMENSION_V, 1, 0.0f); 154 surface->set_parameter( mi::DIMENSION_V, 2, 0.0f); 155 surface->set_parameter( mi::DIMENSION_V, 3, 0.5f); 156 surface->set_parameter( mi::DIMENSION_V, 4, 0.5f); 157 surface->set_parameter( mi::DIMENSION_V, 5, 1.0f); 158 surface->set_parameter( mi::DIMENSION_V, 6, 1.0f); 159 surface->set_parameter( mi::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::IFreeform_surface> mesh( create_sphere( transaction, mi::BASIS_BEZIER)); 203 transaction->store( mesh.get(), "mesh_red"); 204 205 // Create the instance for the red sphere 206 mi::base::Handle<mi::IInstance> instance( transaction->create<mi::IInstance>( "Instance")); 207 instance->attach( "mesh_red"); 208 209 // Set the transformation matrix, the visible attribute, and the material 210 mi::Float64_4_4 matrix( 1.0); 211 matrix.translate( -0.6, -0.5, 0.7); 212 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); 213 matrix *= matrix_scale; 214 instance->set_matrix( matrix); 215 216 mi::base::Handle<mi::IBoolean> visible( 217 instance->create_attribute<mi::IBoolean>( "visible", "Boolean")); 218 visible->set_value( true); 219 220 mi::base::Handle<mi::IRef> material( instance->create_attribute<mi::IRef>( "material", "Ref")); 221 material->set_reference( "red_material"); 222 223 transaction->store( instance.get(), "instance_red"); 224 225 // And attach the instance to the root group 226 mi::base::Handle<mi::IGroup> group( transaction->edit<mi::IGroup>( rootgroup)); 227 group->attach( "instance_red"); 228 group = 0; 229 230 // Create the blue sphere 231 mesh = create_sphere( transaction, mi::BASIS_BSPLINE); 232 233 // Trim the sphere to the part above the ground plane 234 mi::base::Handle<mi::ISurface> surface( mesh->edit_surface( mi::Surface_handle( 0))); 235 surface->set_range( mi::DIMENSION_U, 0.0f, 0.5f); 236 surface = 0; 237 238 transaction->store( mesh.get(), "mesh_blue"); 239 240 // Create the instance for the blue sphere 241 instance = transaction->create<mi::IInstance>( "Instance"); 242 instance->attach( "mesh_blue"); 243 244 // Set the transformation matrix, the visible attribute, and the material 245 matrix = mi::Float64_4_4( 1.0); 246 matrix.translate( -0.6, 0.0, -1.1); 247 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); 248 matrix *= matrix_scale; 249 instance->set_matrix( matrix); 250 251 visible = instance->create_attribute<mi::IBoolean>( "visible", "Boolean"); 252 visible->set_value( true); 253 254 material = instance->create_attribute<mi::IRef>( "material", "Ref"); 255 material->set_reference( "blue_material"); 256 257 transaction->store( instance.get(), "instance_blue"); 258 259 // And attach the instance to the root group 260 group = transaction->edit<mi::IGroup>( rootgroup); 261 group->attach( "instance_blue"); 262 } 263 264 void configuration( mi::base::Handle<mi::neuraylib::INeuray> neuray, const char* mdl_path) 265 { 266 // Configure the neuray library. Here we set the search path for .mdl files. 267 mi::base::Handle<mi::neuraylib::IRendering_configuration> rendering_configuration( 268 neuray->get_api_component<mi::neuraylib::IRendering_configuration>()); 269 check_success( rendering_configuration.is_valid_interface()); 270 check_success( rendering_configuration->add_mdl_path( mdl_path) == 0); 271 272 // Load the FreeImage, Iray Photoreal, and .mi importer plugins. 273 mi::base::Handle<mi::neuraylib::IPlugin_configuration> plugin_configuration( 274 neuray->get_api_component<mi::neuraylib::IPlugin_configuration>()); 275 #ifndef MI_PLATFORM_WINDOWS 276 check_success( plugin_configuration->load_plugin_library( "freeimage.so") == 0); 277 check_success( plugin_configuration->load_plugin_library( "libiray.so") == 0); 278 check_success( plugin_configuration->load_plugin_library( "mi_importer.so") == 0); 279 #else 280 check_success( plugin_configuration->load_plugin_library( "freeimage.dll") == 0); 281 check_success( plugin_configuration->load_plugin_library( "libiray.dll") == 0); 282 check_success( plugin_configuration->load_plugin_library( "mi_importer.dll") == 0); 283 #endif 284 } 285 286 void rendering( mi::base::Handle<mi::neuraylib::INeuray> neuray) 287 { 288 // Get the database, the global scope, which is the root for all transactions, 289 // and create a transaction for importing the scene file and storing the scene. 290 mi::base::Handle<mi::neuraylib::IDatabase> database( 291 neuray->get_api_component<mi::neuraylib::IDatabase>()); 292 check_success( database.is_valid_interface()); 293 mi::base::Handle<mi::neuraylib::IScope> scope( 294 database->get_global_scope()); 295 mi::base::Handle<mi::neuraylib::ITransaction> transaction( 296 scope->create_transaction()); 297 check_success( transaction.is_valid_interface()); 298 299 // Import the scene file 300 mi::base::Handle<mi::neuraylib::IImport_api> import_api( 301 neuray->get_api_component<mi::neuraylib::IImport_api>()); 302 check_success( import_api.is_valid_interface()); 303 mi::base::Handle<const mi::IImport_result> import_result( 304 import_api->import_elements( transaction.get(), "file:main.mi")); 305 check_success( import_result->get_error_number() == 0); 306 307 // Add two instances of freeform surfaces 308 setup_scene( transaction.get(), import_result->get_rootgroup()); 309 310 // Create the scene object 311 mi::base::Handle<mi::neuraylib::IScene> scene( 312 transaction->create<mi::neuraylib::IScene>( "Scene")); 313 scene->set_rootgroup( import_result->get_rootgroup()); 314 scene->set_options( import_result->get_options()); 315 scene->set_camera_instance( import_result->get_camera_inst()); 316 transaction->store( scene.get(), "the_scene"); 317 scene = 0; 318 319 // Create the render context using the Iray Photoreal render mode 320 scene = transaction->edit<mi::neuraylib::IScene>( "the_scene"); 321 mi::base::Handle<mi::neuraylib::IRender_context> render_context( 322 scene->create_render_context( transaction.get(), "iray")); 323 check_success( render_context.is_valid_interface()); 324 mi::base::Handle<mi::IString> scheduler_mode( transaction->create<mi::IString>()); 325 scheduler_mode->set_c_str( "batch"); 326 render_context->set_option( "scheduler_mode", scheduler_mode.get()); 327 scene = 0; 328 329 // Create the render target and render the scene 330 mi::base::Handle<mi::neuraylib::IImage_api> image_api( 331 neuray->get_api_component<mi::neuraylib::IImage_api>()); 332 mi::base::Handle<mi::neuraylib::IRender_target> render_target( 333 new Render_target( image_api.get(), "Color", 512, 384)); 334 check_success( render_context->render( transaction.get(), render_target.get(), 0) >= 0); 335 336 // Write the image to disk 337 mi::base::Handle<mi::neuraylib::IExport_api> export_api( 338 neuray->get_api_component<mi::neuraylib::IExport_api>()); 339 check_success( export_api.is_valid_interface()); 340 mi::base::Handle<mi::neuraylib::ICanvas> canvas( render_target->get_canvas( 0)); 341 export_api->export_canvas( "file:example_freeform_surface.png", canvas.get()); 342 343 transaction->commit(); 344 } 345 346 int main( int argc, char* argv[]) 347 { 348 // Collect command line parameters 349 if( argc != 2) { 350 std::cerr << "Usage: example_freeform_surface <mdl_path>" << std::endl; 351 keep_console_open(); 352 return EXIT_FAILURE; 353 } 354 const char* mdl_path = argv[1]; 355 356 // Access the neuray library 357 mi::base::Handle<mi::neuraylib::INeuray> neuray( load_and_get_ineuray()); 358 check_success( neuray.is_valid_interface()); 359 360 // Configure the neuray library 361 configuration( neuray, mdl_path); 362 363 // Start the neuray library 364 check_success( neuray->start() == 0); 365 366 // Do the actual rendering 367 rendering( neuray); 368 369 // Shut down the neuray library 370 check_success( neuray->shutdown() == 0); 371 neuray = 0; 372 373 // Unload the neuray library 374 check_success( unload()); 375 376 keep_console_open(); 377 return EXIT_SUCCESS; 378 }