Example for Subdivision surfaces
This example imports a partial scene containing definitions for the camera, a light, and some geometry (a ground plane and a yellow cube). It then creates via the API a subdivision surface and three instances of it with different approximation levels.
Detailed Description
- Creation of subdivision surfaces
-
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 previous example for polygon meshes). In this example we create a simple cube. In addition the crease values of the edges of two opposing faces are set to 1.0. This will result in sharp edges in the limit surface which is a cylinder (without the crease values the limit surface would be a sphere) .
- Instancing of objects
-
The subdivision is included in the scene similar as the tetrahedron and the cylinder in the previous examples for triangle and polygon meshes. But in contrast, not only a single instance, but three instances are used in this example.
Instances allow to share the geometry of multiple identical objects. Each instance has its own transformation matrix. Furthermore, each instance can have its own attributes. In our example, we use different approximation levels for each instance to show the subdivision process.
Example Source
Source Code Location: examples/example_subdivision_surface.cpp
/****************************************************************************** * Copyright 1986, 2011 NVIDIA Corporation. All rights reserved. *****************************************************************************/ // examples/example_subdivision_surface.cpp // // Creates a three instances of a subdivision surface with different approximation levels. #include <mi/neuraylib.h> // Include code shared by all examples. #include "example_shared.h" // Include an implementation of ITile, ICanvas, and IRender_target. #include "example_render_target.h" #include <iostream> // Create a subdivision surface with a cube as control mesh. INVALID_DOXYREFmi::ISubdivision_surface* create_cube( mi::neuraylib::ITransaction* transaction) { const mi::Size n_points = 8; const mi::Size n_quads = 6; mi::Float32_3 cube_points[n_points] = { mi::Float32_3( -0.5, -0.5, -0.5), mi::Float32_3( 0.5, -0.5, -0.5), mi::Float32_3( 0.5, 0.5, -0.5), mi::Float32_3( -0.5, 0.5, -0.5), mi::Float32_3( -0.5, -0.5, 0.5), mi::Float32_3( 0.5, -0.5, 0.5), mi::Float32_3( 0.5, 0.5, 0.5), mi::Float32_3( -0.5, 0.5, 0.5) }; mi::Uint32 cube_quads[n_quads][4] = { { 0, 1, 5, 4 }, { 4, 5, 6, 7 }, { 7, 6, 2, 3 }, { 3, 2, 1, 0 }, { 1, 2, 6, 5 }, { 3, 0, 4, 7 } }; // Create an empty subdivision surface INVALID_DOXYREFmi::ISubdivision_surface* mesh = transaction->create<INVALID_DOXYREFmi::ISubdivision_surface>( "Subdivision_surface"); // Create a cube (points and polygons) mesh->INVALID_DOXYREF( n_points); for( mi::Uint32 i = 0; i < n_points; ++i) mesh->INVALID_DOXYREF( cube_points[i]); for( mi::Uint32 i = 0; i < n_quads; ++i) mesh->INVALID_DOXYREF( 4); // Map vertices of the polygons to points mi::base::Handle< mi::IPolygon_connectivity> mesh_connectivity( mesh->INVALID_DOXYREF()); for( mi::Uint32 i = 0; i < n_quads; ++i) mesh_connectivity->set_polygon_indices( INVALID_DOXYREFmi::Polygon_handle( i), cube_quads[i]); check_success( mesh->INVALID_DOXYREF( mesh_connectivity.get()) == 0); // Set crease values to 1.0f on two opposing faces to end up with a cylinder mi::Float32 crease_values[4] = { 1.0f, 1.0f, 1.0f, 1.0f }; mesh->INVALID_DOXYREF( INVALID_DOXYREFmi::Polygon_handle( 1), crease_values); mesh->INVALID_DOXYREF( INVALID_DOXYREFmi::Polygon_handle( 3), crease_values); return mesh; } // Add three instances of a subdivision surface with different approximation levels void setup_scene( mi::neuraylib::ITransaction* transaction, const char* rootgroup) { // Remove the existing cube from the scene. The unrefined control mesh (in red) will take its // place. mi::base::Handle< mi::IGroup> group( transaction->edit<INVALID_DOXYREFmi::IGroup>( rootgroup)); group->detach( "cube1_inst"); group = 0; // Create the subdivision surface mi::base::Handle< mi::ISubdivision_surface> mesh( create_cube(transaction)); transaction->store( mesh.get(), "mesh"); // Create three instances of the subdivision surface with different materials, approximation // levels and world-to-object transformation matrices. const char* names[3] = { "instance0", "instance1", "instance2" }; const char* materials[3] = { "redSG", "greenSG", "blueSG" }; mi::Float32 approximation_level[3] = { 0.0f, 1.0f, 2.0f }; const mi::Float32_3 translation[3] = { mi::Float32_3( 0.5f, 0.0f, 1.5f), mi::Float32_3( -1.5f, 0.0f, 1.5f), mi::Float32_3( -1.5f, 0.0f, -0.5f) }; for( mi::Size i = 0; i < 3; ++i) { mi::base::Handle< mi::IInstance> instance( transaction->create<INVALID_DOXYREFmi::IInstance>( "Instance")); instance->attach( "mesh"); mi::Float64_4_4 matrix( 1.0); matrix.translate( translation[i]); matrix.rotate( 0.0, 0.25 * MI_PI_2, 0.0); instance->set_world_to_obj( matrix); mi::base::Handle< mi::IBoolean> visible( instance->create_attribute<mi::IBoolean>( "visible", "Boolean")); visible->set_value( true); mi::base::Handle< mi::IRef> material( instance->create_attribute<mi::IRef>( "material", "Ref")); material->set_reference( materials[i]); mi::base::Handle< mi::IStructure> approx( instance->create_attribute<mi::IStructure>( "approx", "Approx")); mi::base::Handle< mi::ISint8> method( approx->get_value<mi::ISint8>( "method")); method->set_value( 0); mi::base::Handle< mi::IFloat32> const_u( approx->get_value<mi::IFloat32>( "const_u")); const_u->set_value( approximation_level[i]); transaction->store( instance.get(), names[i]); mi::base::Handle< mi::IGroup> group( transaction->edit<INVALID_DOXYREFmi::IGroup>( rootgroup)); group->attach( names[i]); } } void configuration( mi::base::Handle< mi::neuraylib::INeuray> neuray, const char* shader_path) { // Configure the neuray library. Here we set some paths needed by the renderer. mi::base::Handle< mi::neuraylib::IRendering_configuration> rendering_configuration( neuray->get_api_component<mi::neuraylib::IRendering_configuration>()); check_success( rendering_configuration.is_valid_interface()); check_success( rendering_configuration->add_shader_path( shader_path) == 0); // Load the FreeImage image plugin and the LLVM backend for MetaSL. mi::base::Handle< mi::neuraylib::IPlugin_configuration> plugin_configuration( neuray->get_api_component<mi::neuraylib::IPlugin_configuration>()); #ifndef MI_PLATFORM_WINDOWS check_success( plugin_configuration->load_plugin_library( "freeimage.so") == 0); check_success( plugin_configuration->load_plugin_library( "gen_llvm.so") == 0); #else check_success( plugin_configuration->load_plugin_library( "freeimage.dll") == 0); check_success( plugin_configuration->load_plugin_library( "gen_llvm.dll") == 0); #endif } void rendering( mi::base::Handle< mi::neuraylib::INeuray> neuray) { // Get the database, the global scope, which is the root for all transactions, // and create a transaction for importing the scene file and storing the scene. mi::base::Handle< mi::neuraylib::IDatabase> database( neuray->get_api_component<mi::neuraylib::IDatabase>()); check_success( database.is_valid_interface()); mi::base::Handle< mi::neuraylib::IScope> scope( database->get_global_scope()); mi::base::Handle< mi::neuraylib::ITransaction> transaction( scope->create_transaction()); check_success( transaction.is_valid_interface()); // Import the scene file mi::base::Handle< mi::neuraylib::IImport_api> import_api( neuray->get_api_component<mi::neuraylib::IImport_api>()); check_success( import_api.is_valid_interface()); mi::base::Handle< const mi::IImport_result> import_result( import_api->import_elements( transaction.get(), "main.mi")); check_success( import_result->get_error_number() == 0); // Add three instances of a subdivision surface with different approximation levels setup_scene( transaction.get(), import_result->get_rootgroup()); // Create the scene object mi::base::Handle< mi::neuraylib::IScene> scene( transaction->create<mi::neuraylib::IScene>( "Scene")); scene->set_rootgroup( import_result->get_rootgroup()); scene->set_options( import_result->get_options()); scene->set_camera_instance( import_result->get_camera_inst()); transaction->store( scene.get(), "the_scene"); // Create the render context using the default renderer scene = transaction->edit<mi::neuraylib::IScene>( "the_scene"); mi::base::Handle< mi::neuraylib::IRender_context> render_context( scene->get_render_context( transaction.get(), "rt_bsp")); scene = 0; // Create the render target and render the scene Render_target render_target( 512, 384); check_success( render_context->render( transaction.get(), &render_target, NULL) == 0); // Write the image to disk mi::base::Handle< mi::neuraylib::IExport_api> export_api( neuray->get_api_component<mi::neuraylib::IExport_api>()); check_success( export_api.is_valid_interface()); mi::base::Handle< mi::neuraylib::ICanvas> canvas( render_target.get_canvas( 0)); export_api->export_canvas( "example_subdivision_surface.png", canvas.get(), 100); // All transactions need to get committed or aborted, not really important in this example. transaction->commit(); } // The example takes the following command line arguments: // // example_subdivision_surface <shader_path> // // shader_path path to the shaders, e.g., neuray-<version>/shaders // // The rendered image is written to a file named "example_subdivision_surface.png". // int main( int argc, char* argv[]) { // Collect command line parameters if( argc != 2) { std::cerr << "Usage: example_subdivision_surface <shader_path>" << std::endl; return EXIT_FAILURE; } const char* shader_path = argv[1]; // Access the neuray library mi::base::Handle< mi::neuraylib::INeuray> neuray( load_and_get_ineuray()); check_success( neuray.is_valid_interface()); // Configure the neuray library configuration ( neuray, shader_path); // Start the neuray library check_success( neuray->start( true) == 0); // Do the actual rendering rendering( neuray); // Shut down the neuray library check_success( neuray->shutdown() == 0); neuray = 0; // Unload the neuray library check_success( unload()); return EXIT_SUCCESS; }