neuray API Programmer's Manual

Example for Rendering

[Previous] [Next] [Up]

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;
}

[Previous] [Next] [Up]