Iray Programmer's Manual

Creating freeform surfaces

This topic introduces:

  1. Core concepts related to the mi::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, 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 }