Iray Programmer's Manual

Creating freeform surfaces

This topic introduces:

  1. 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:
  2. 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, 2015 NVIDIA ARC GmbH. 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     check_success( neuray->start() == 0);
369 
370     // Do the actual rendering
371     rendering( neuray);
372 
373     // Shut down the neuray library
374     check_success( neuray->shutdown() == 0);
375     neuray = 0;
376 
377     // Unload the neuray library
378     check_success( unload());
379 
380     keep_console_open();
381     return EXIT_SUCCESS;
382 }