Iray Programmer's Manual

Creating subdivision surfaces

This topic introduces:

  1. Core concepts about the control mesh of a subdivision surface and the instancing of objects:
  2. The program example_subdivision_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 a subdivision surface and three instances of it with different approximation levels.

The control mesh

The control mesh of the subdivision surface is exposed as a polygon mesh with the limitation that only triangles and quads are supported (see the example for polygon meshes). In example_subdivision_surface.cpp, a simple cube is created. Additionally, the crease values of the edges of two opposing faces are set to 1.0, which creates sharp edges in the limit surface (which is a cylinder). Without the crease values the limit surface would be a sphere.

Instancing objects

The procedure to add the subdivision element to the scene database and the scene graph is similar to the procedure used for the tetrahedron and the cylinder in previous examples.

In the earlier examples, one instance of the element was created. In example_subdivision_surface.cpp, three instances of the subdivision element are created. Multiple identical instances share the same geometry. Each instance has its own transformation matrix and each instance can have its own attributes. To show the subdivision process in example_subdivision_surface.cpp, different approximation levels are used for each instance.

example_subdivision_surface.cpp

001 /******************************************************************************
002  * © 1986, 2014 NVIDIA Corporation. All rights reserved.
003  *****************************************************************************/
004 
005 // examples/example_subdivision_surface.cpp
006 //
007 // Creates three instances of a subdivision surface with different approximation levels.
008 //
009 // The example expects the following command line arguments:
010 //
011 //   example_subdivision_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_subdivision_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 subdivision surface with a cube as control mesh.
027 mi::ISubdivision_surface* create_cube( mi::neuraylib::ITransaction* transaction)
028 {
029     const mi::Size n_points = 8;
030     const mi::Size n_quads = 6;
031 
032     mi::Float32_3 cube_points[n_points] = {
033         mi::Float32_3( -0.5, -0.5, -0.5),
034         mi::Float32_3(  0.5, -0.5, -0.5),
035         mi::Float32_3(  0.5,  0.5, -0.5),
036         mi::Float32_3( -0.5,  0.5, -0.5),
037         mi::Float32_3( -0.5, -0.5,  0.5),
038         mi::Float32_3(  0.5, -0.5,  0.5),
039         mi::Float32_3(  0.5,  0.5,  0.5),
040         mi::Float32_3( -0.5,  0.5,  0.5) };
041 
042     mi::Uint32 cube_quads[n_quads][4] = {
043         { 0, 1, 5, 4 },
044         { 4, 5, 6, 7 },
045         { 7, 6, 2, 3 },
046         { 3, 2, 1, 0 },
047         { 1, 2, 6, 5 },
048         { 3, 0, 4, 7 } };
049 
050     // Create an empty subdivision surface
051     mi::ISubdivision_surface* mesh
052         = transaction->create<mi::ISubdivision_surface>( "Subdivision_surface");
053 
054     // Create a cube (points and polygons)
055     mesh->reserve_points( n_points);
056     for( mi::Uint32 i = 0; i < n_points; ++i)
057         mesh->append_point( cube_points[i]);
058     for( mi::Uint32 i = 0; i < n_quads; ++i)
059         mesh->add_polygon( 4);
060 
061     // Map vertices of the polygons to points
062     mi::base::Handle<mi::IPolygon_connectivity> mesh_connectivity( mesh->edit_mesh_connectivity());
063     for( mi::Uint32 i = 0; i < n_quads; ++i)
064         mesh_connectivity->set_polygon_indices( mi::Polygon_handle( i), cube_quads[i]);
065     check_success( mesh->attach_mesh_connectivity( mesh_connectivity.get()) == 0);
066 
067     // Set crease values to 1.0f on two opposing faces to end up with a cylinder
068     mi::Float32 crease_values[4] = { 1.0f, 1.0f, 1.0f, 1.0f };
069     mesh->set_crease_values( mi::Polygon_handle( 1), crease_values);
070     mesh->set_crease_values( mi::Polygon_handle( 3), crease_values);
071 
072     return mesh;
073 }
074 
075 // Add three instances of a subdivision surface with different approximation levels
076 void setup_scene( mi::neuraylib::ITransaction* transaction, const char* rootgroup)
077 {
078     // Remove the existing cube from the scene. The unrefined control mesh (in red) will take its
079     // place.
080     mi::base::Handle<mi::IGroup> group( transaction->edit<mi::IGroup>( rootgroup));
081     group->detach( "cube_instance");
082     group = 0;
083 
084     // Create the subdivision surface
085     mi::base::Handle<mi::ISubdivision_surface> mesh( create_cube( transaction));
086     transaction->store( mesh.get(), "mesh");
087 
088     // Create three instances of the subdivision surface with different materials, approximation
089     // levels and world-to-object transformation matrices.
090     const char* names[3]               = { "instance0", "instance1", "instance2" };
091     const char* materials[3]           = { "red_material", "green_material", "blue_material" };
092     mi::Float32 approximation_level[3] = { 0.0f, 1.0f, 2.0f };
093     const mi::Float32_3 translation[3] = {
094         mi::Float32_3(  1.1f, -0.5f,  0.9f),
095         mi::Float32_3( -0.9f, -0.5f,  0.9f),
096         mi::Float32_3( -0.9f, -0.5f, -1.1f)
097     };
098 
099     for( mi::Size i = 0; i < 3; ++i) {
100 
101         mi::base::Handle<mi::IInstance> instance( transaction->create<mi::IInstance>( "Instance"));
102         instance->attach( "mesh");
103 
104         mi::Float64_4_4 matrix( 1.0);
105         matrix.translate( translation[i]);
106         matrix.rotate( 0.0, 0.25 * MI_PI_2, 0.0);
107         instance->set_matrix( matrix);
108 
109         mi::base::Handle<mi::IBoolean> visible(
110             instance->create_attribute<mi::IBoolean>( "visible", "Boolean"));
111         visible->set_value( true);
112 
113         mi::base::Handle<mi::IRef> material(
114             instance->create_attribute<mi::IRef>( "material", "Ref"));
115         material->set_reference( materials[i]);
116 
117         mi::base::Handle<mi::IStructure> approx(
118             instance->create_attribute<mi::IStructure>( "approx", "Approx"));
119         mi::base::Handle<mi::ISint8> method( approx->get_value<mi::ISint8>( "method"));
120         method->set_value( 0);
121         mi::base::Handle<mi::IFloat32> const_u( approx->get_value<mi::IFloat32>( "const_u"));
122         const_u->set_value( approximation_level[i]);
123 
124         transaction->store( instance.get(), names[i]);
125 
126         mi::base::Handle<mi::IGroup> group( transaction->edit<mi::IGroup>( rootgroup));
127         group->attach( names[i]);
128     }
129 }
130 
131 void configuration( mi::base::Handle<mi::neuraylib::INeuray> neuray, const char* mdl_path)
132 {
133     // Configure the neuray library. Here we set the search path for .mdl files.
134     mi::base::Handle<mi::neuraylib::IRendering_configuration> rendering_configuration(
135         neuray->get_api_component<mi::neuraylib::IRendering_configuration>());
136     check_success( rendering_configuration.is_valid_interface());
137     check_success( rendering_configuration->add_mdl_path( mdl_path) == 0);
138 
139     // Load the FreeImage, Iray Photoreal, and .mi importer plugins.
140     mi::base::Handle<mi::neuraylib::IPlugin_configuration> plugin_configuration(
141         neuray->get_api_component<mi::neuraylib::IPlugin_configuration>());
142 #ifndef MI_PLATFORM_WINDOWS
143     check_success( plugin_configuration->load_plugin_library( "freeimage.so") == 0);
144     check_success( plugin_configuration->load_plugin_library( "libiray.so") == 0);
145     check_success( plugin_configuration->load_plugin_library( "mi_importer.so") == 0);
146 #else
147     check_success( plugin_configuration->load_plugin_library( "freeimage.dll") == 0);
148     check_success( plugin_configuration->load_plugin_library( "libiray.dll") == 0);
149     check_success( plugin_configuration->load_plugin_library( "mi_importer.dll") == 0);
150 #endif
151 }
152 
153 void rendering( mi::base::Handle<mi::neuraylib::INeuray> neuray)
154 {
155     // Get the database, the global scope, which is the root for all transactions,
156     // and create a transaction for importing the scene file and storing the scene.
157     mi::base::Handle<mi::neuraylib::IDatabase> database(
158         neuray->get_api_component<mi::neuraylib::IDatabase>());
159     check_success( database.is_valid_interface());
160     mi::base::Handle<mi::neuraylib::IScope> scope(
161         database->get_global_scope());
162     mi::base::Handle<mi::neuraylib::ITransaction> transaction(
163         scope->create_transaction());
164     check_success( transaction.is_valid_interface());
165 
166     // Import the scene file
167     mi::base::Handle<mi::neuraylib::IImport_api> import_api(
168         neuray->get_api_component<mi::neuraylib::IImport_api>());
169     check_success( import_api.is_valid_interface());
170     mi::base::Handle<const mi::IImport_result> import_result(
171         import_api->import_elements( transaction.get(), "file:main.mi"));
172     check_success( import_result->get_error_number() == 0);
173 
174     // Add three instances of a subdivision surface with different approximation levels
175     setup_scene( transaction.get(), import_result->get_rootgroup());
176 
177     // Create the scene object
178     mi::base::Handle<mi::neuraylib::IScene> scene(
179         transaction->create<mi::neuraylib::IScene>( "Scene"));
180     scene->set_rootgroup(       import_result->get_rootgroup());
181     scene->set_options(         import_result->get_options());
182     scene->set_camera_instance( import_result->get_camera_inst());
183     transaction->store( scene.get(), "the_scene");
184 
185     // Create the render context using the Iray Photoreal render mode
186     scene = transaction->edit<mi::neuraylib::IScene>( "the_scene");
187     mi::base::Handle<mi::neuraylib::IRender_context> render_context(
188         scene->create_render_context( transaction.get(), "iray"));
189     check_success( render_context.is_valid_interface());
190     mi::base::Handle<mi::IString> scheduler_mode( transaction->create<mi::IString>());
191     scheduler_mode->set_c_str( "batch");
192     render_context->set_option( "scheduler_mode", scheduler_mode.get());
193     scene = 0;
194 
195     // Create the render target and render the scene
196     mi::base::Handle<mi::neuraylib::IImage_api> image_api(
197         neuray->get_api_component<mi::neuraylib::IImage_api>());
198     mi::base::Handle<mi::neuraylib::IRender_target> render_target(
199         new Render_target( image_api.get(), "Color", 512, 384));
200     check_success( render_context->render( transaction.get(), render_target.get(), 0) >= 0);
201 
202     // Write the image to disk
203     mi::base::Handle<mi::neuraylib::IExport_api> export_api(
204         neuray->get_api_component<mi::neuraylib::IExport_api>());
205     check_success( export_api.is_valid_interface());
206     mi::base::Handle<mi::neuraylib::ICanvas> canvas( render_target->get_canvas( 0));
207     export_api->export_canvas( "file:example_subdivision_surface.png", canvas.get());
208 
209     transaction->commit();
210 }
211 
212 int main( int argc, char* argv[])
213 {
214     // Collect command line parameters
215     if( argc != 2) {
216         std::cerr << "Usage: example_subdivision_surface <mdl_path>" << std::endl;
217         keep_console_open();
218         return EXIT_FAILURE;
219     }
220     const char* mdl_path = argv[1];
221 
222     // Access the neuray library
223     mi::base::Handle<mi::neuraylib::INeuray> neuray( load_and_get_ineuray());
224     check_success( neuray.is_valid_interface());
225 
226     // Configure the neuray library
227     configuration( neuray, mdl_path);
228 
229     // Start the neuray library
230     check_success( neuray->start() == 0);
231 
232     // Do the actual rendering
233     rendering( neuray);
234 
235     // Shut down the neuray library
236     check_success( neuray->shutdown() == 0);
237     neuray = 0;
238 
239     // Unload the neuray library
240     check_success( unload());
241 
242     keep_console_open();
243     return EXIT_SUCCESS;
244 }