Example for Rendering
This example imports a scene file, renders the scene, and writes the image to disk.
New Topics
-
Providing the rendering infrastructure
-
Accessing the database
-
Importing a scene file
-
Rendering of a scene
Detailed Description
- Providing the rendering infrastructure
-
To render a scene, you have to provide some rendering infrastructure for the neuray API. Basically, you have to provide buffers where the rendered image is written to. These buffers are defined by the interfaces mi::neuraylib::IRender_target, mi::neuraylib::ICanvas, and mi::neuraylib::ITile. You have to provide implementations of these interfaces, which will be used by neuray.
This example provides a very simple implementation of these interfaces. In this implementation the render target has only a single color canvas, which consists of a single tile. The pixel format is RGBA. Additionally, there is a method that writes the rendered image to disk.
- Accessing the database
-
The mi::neuraylib::IDatabase interface is one of the central interface classes that become available once neuray has been started. Using this interface, you can access the scene database. In particular, you can either access the global scope or create your own scope. The mi::neuraylib::IScope interface allows you to create transactions, which are required for all operations that access the database. See Overview of the Neuray API for further explanations about the database, scopes, and transactions.
This example uses the database interface to access the global scope and creates a transaction in the global scope. All transactions need either to get committed or aborted. By committing a transaction, all changes (if any) will become visible for subsequently started transactions. Aborting a transaction discards any changes made in that transaction.
- Importing a scene file
-
The import of files is handled by the API component mi::neuraylib::IImport_api. The actual import is done by the method mi::neuraylib::IImport_api::import_elements(). The result of the import operation is described by the interface INVALID_DOXYREFmi::IImport_result. This interface provides among other things the method INVALID_DOXYREF to retrieve the resulting error number. This error number is 0 in case of success, all other numbers indicate failures.
The example also creates an instance of the mi::neuraylib::IScene interface, which is later used for rendering. To this end, the name of the root group, the camera, and the options are set in the scene. These names are available from the import result.
- Rendering of a scene
-
Rendering a scene requires a render target and a render context. The render target has to be provided by you, as explained earlier in this example. The render context can be obtained via INVALID_DOXYREFmi::neuraylib::IScene::get_render_context().
Example Source
Source Code Location: examples/example_render_target.h
/****************************************************************************** * Copyright 1986, 2011 NVIDIA Corporation. All rights reserved. *****************************************************************************/ // examples/example_render_target.h // // Code shared by all rendering examples #ifndef EXAMPLE_RENDER_TARGET_H #define EXAMPLE_RENDER_TARGET_H #include <mi/neuraylib.h> // A simple implementation of the ITile interface with RGBA pixel format. class Tile : public mi::base::Interface_implement<mi::neuraylib::ITile> { public: // Constructor. Creates a Tile of the given width and height. Tile( mi::Uint32 width, mi::Uint32 height) { m_width = width; m_height = height; m_data = new mi::Uint8[m_width * m_height * 4]; } // Destructor ~Tile() { delete[] m_data; } // Implement the interface of mi::neuraylib::ITile void set_pixel( mi::Uint32 x_offset, mi::Uint32 y_offset, const mi::Float32* floats) { mi::Uint8* position = &m_data[(x_offset + y_offset * m_width) * 4]; position[0] = static_cast<mi::Uint8>( mi::math::clamp( floats[0], 0.0f, 1.0f) * 255); position[1] = static_cast<mi::Uint8>( mi::math::clamp( floats[1], 0.0f, 1.0f) * 255); position[2] = static_cast<mi::Uint8>( mi::math::clamp( floats[2], 0.0f, 1.0f) * 255); position[3] = static_cast<mi::Uint8>( mi::math::clamp( floats[3], 0.0f, 1.0f) * 255); } void get_pixel( mi::Uint32 x_offset, mi::Uint32 y_offset, mi::Float32* floats) const { mi::Uint8* position = &m_data[(x_offset + y_offset * m_width) * 4]; floats[0] = static_cast<mi::Float32>( position[0]) / 255; floats[1] = static_cast<mi::Float32>( position[1]) / 255; floats[2] = static_cast<mi::Float32>( position[2]) / 255; floats[3] = static_cast<mi::Float32>( position[3]) / 255; } const char* get_type() const { return "Rgba"; } mi::Uint32 get_resolution_x() const { return m_width; } mi::Uint32 get_resolution_y() const { return m_height; } const void* get_data() const { return m_data; } void* get_data() { return m_data; } private: // Width of the tile mi::Uint32 m_width; // Height of the tile mi::Uint32 m_height; // The data of this tile, 4 bytes per pixel mi::Uint8* m_data; }; // A simple implementation of the ICanvas interface with a single tile. class Canvas : public mi::base::Interface_implement<mi::neuraylib::ICanvas> { public: // Constructor. Creates a Canvas with a single tile of the given width and height. Canvas( mi::Uint32 width, mi::Uint32 height) { m_width = width; m_height = height; m_tile = new Tile( width, height); } // Destructor ~Canvas() { m_tile->release(); } // Implement the interface of mi::neuraylib::ICanvas mi::Uint32 get_resolution_x() const { return m_width; } mi::Uint32 get_resolution_y() const { return m_height; } mi::Uint32 get_tile_resolution_x() const { return m_width; } mi::Uint32 get_tile_resolution_y() const { return m_height; } mi::Uint32 get_tiles_size_x() const { return 1; } mi::Uint32 get_tiles_size_y() const { return 1; } mi::Uint32 get_layers_size() const { return 1; } const mi::neuraylib::ITile* get_tile( mi::Uint32 pixel_x, mi::Uint32 pixel_y, mi::Uint32 layer = 0) const { m_tile->retain(); return m_tile; } mi::neuraylib::ITile* get_tile( mi::Uint32 pixel_x, mi::Uint32 pixel_y, mi::Uint32 layer = 0) { m_tile->retain(); return m_tile; } const char* get_type( mi::Uint32 layer) const { return "Rgba"; } private: // Width of the canvas mi::Uint32 m_width; // Height of the canvas mi::Uint32 m_height; // The only tile of this canvas Tile* m_tile; }; // A simple implementation of the IRender_target interface with a single canvas. class Render_target : public mi::base::Interface_implement<mi::neuraylib::IRender_target> { public: // Constructor. Creates a Render_target with a single Canvas of the given width and height. Render_target( mi::Uint32 width, mi::Uint32 height) { m_canvas = new Canvas( width, height); } // Destructor ~Render_target() { m_canvas->release(); } // Implement the interface of mi::neuraylib::IRender_target mi::Uint32 get_canvas_count() const { return 1; } const char* get_canvas_name( mi::Uint32 index) const { return "result"; } const mi::neuraylib::ICanvas* get_canvas( mi::Uint32 index) const { m_canvas->retain(); return m_canvas; } mi::neuraylib::ICanvas* get_canvas( mi::Uint32 index) { m_canvas->retain(); return m_canvas; } private: // The only canvas of this render target Canvas* m_canvas; }; #endif // MI_EXAMPLE_RENDER_TARGET_H
Source Code Location: examples/example_rendering.cpp
/****************************************************************************** * Copyright 1986, 2011 NVIDIA Corporation. All rights reserved. *****************************************************************************/ // examples/example_rendering.cpp // // Imports a scene file, renders the scene, and writes the image to disk. #include <iostream> #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" 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, const char* scene_file) { // 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(), scene_file)); check_success( import_result->get_error_number() == 0); // 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(), "default")); 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_rendering.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_rendering <scene_file> <shader_path> // // scene_file some scene file // shader_path path to the shaders, e.g., neuray-<version>/shaders // // The rendered image is written to a file named "example_rendering.png". // int main( int argc, char* argv[]) { // Collect command line parameters if( argc != 3) { std::cerr << "Usage: example_rendering <scene_file> <shader_path>" << std::endl; return EXIT_FAILURE; } const char* scene_file = argv[1]; const char* shader_path = argv[2]; // 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, scene_file); // Shut down the neuray library check_success( neuray->shutdown() == 0); neuray = 0; // Unload the neuray library check_success( unload()); return EXIT_SUCCESS; }