Material Definition Language API nvidia_logo_transpbg.gif Up
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Groups Pages
Example for Execution of Compiled MDL Materials (CPU)
[Previous] [Up] [Next]

This example shows how a renderer can call the code generated by the "native" backend for compiled materials to evaluate sub-expressions of multiple materials on the CPU.

New Topics

  • MDL material state (CPU)
  • Execution of generated code (CPU)
  • Texture handling

Detailed Description

MDL material state (CPU)


The MDL material state structure mi::neuraylib::Shading_state_material is a representation of the renderer state as defined in section 19 "Renderer state" in the MDL specification. It is used to make the state of the renderer (like the position of an intersection point on the surface, the shading normal and the texture coordinates) available to the generated code.

Here's the material state structure just for reference:

struct Shading_state_material {
tct_float3 normal; // state::normal() result
tct_float3 geom_normal; // state::geom_normal() result
tct_float3 position; // state::position() result
tct_float animation_time; // state::animation_time() result
const tct_float3 *text_coords; // state::texture_coordinate() table
const tct_float3 *tangent_u; // state::texture_tangent_u() table
const tct_float3 *tangent_v; // state::texture_tangent_v() table
tct_float4 *text_results; // texture results lookup table
const char *ro_data_segment; // read-only data segment
const tct_float4 *world_to_object; // world-to-object transform matrix
const tct_float4 *object_to_world; // object-to-world transform matrix
tct_int object_id; // state::object_id() result
};

Please refer to the structure documentation for more information.

In this example, we fill the material state structure with some example values and only use one texture space. For the world-to-object and object-to-world transformation matrices we use identity matrices. We will iterate the position and text_coords fields over a 2x2 quad around the center of the world with position x and y coordinates ranging from -1 to 1 and the texture uv-coordinates ranging from 0 to 1, respectively.

Execution of generated code (CPU)


For the native (CPU) backend, filling this structure is actually already enough to prepare the execution of the generated code. Calling mi::neuraylib::ITarget_code::execute() with the material state and a sufficiently large result buffer will fill the buffer with the requested result. In this example, we will provide a NULL pointer as a target argument block. For instance compilation, the target argument block parameter is not used, and for class compilation, providing a NULL pointer will result in the default target argument block for the used material instance. See Instance-compilation and class-compilation for more details about compilation modes.

In this example, we bake a sub-expression of a material into a texture by executing the generated code for every texel updating the material state accordingly. At the end, we write the texture to disk.

Texture handling


Per default, the MDL API provides a built-in runtime which handles texture access functions (texture lookups etc.). In some situations, you may however want to provide your own implementation. This requires you to disable the built-in texture handler by setting the mi::neuraylib::IMdl_backend option use_builtin_resource_handler to "off" (via mi::neuraylib::IMdl_backend::set_option()). It also requires you to implement the following functions:

  • tex_lookup_float4_2d
  • tex_lookup_float3_2d
  • tex_texel_float4_2d
  • tex_lookup_float4_3d
  • tex_lookup_float3_3d
  • tex_texel_float4_3d
  • tex_lookup_float4_cube
  • tex_lookup_float3_cube
  • tex_resolution_2d

Except for the last one, these functions correspond directly to the functions described in section 20.3 "Standard library functions - Texture" in the MDL specification.

You pass your functions to the MDL API via an instance of the struct mi::neuraylib::Texture_handler_base when calling any of the mi::neuraylib::ITarget_code::execute*() functions. The mi::neuraylib::Texture_handler_base has a vtable parameter which contains function pointers matching the signature of the required functions. These function pointers need to point to your functions.

An example implementation (limited to 2D texture lookups for now) can be found in texture_support.h. To switch example_execution_native to the custom texturing code, please disable the define USE_BUILTIN_TEXTURE_RUNTIME on top of example_execution_native.cpp.

Example Source

Source Code Location: examples/mdl_sdk/execution_native/example_execution_native.cpp

/******************************************************************************
* Copyright 2020 NVIDIA Corporation. All rights reserved.
*****************************************************************************/
// examples/mdl_sdk/execution_native/example_execution_native.cpp
//
// Introduces the execution of generated code for compiled materials for
// the native (CPU) backend and shows how to manually bake a material
// sub-expression to a texture.
#include <iomanip>
#include <iostream>
#include <sstream>
#include <string>
#include "example_shared.h"
#include "texture_support.h"
#include <vector>
// Command line options structure.
struct Options {
// An result output file name.
std::string outputfile;
// The resolution of the display / image.
unsigned res_x, res_y;
// Whether class compilation should be used for the materials.
bool use_class_compilation;
// Whether the custom texture runtime should be used.
bool use_custom_tex_runtime;
// Whether derivative support should be enabled.
// This example does not support derivatives in combination with the custom texture runtime.
bool enable_derivatives;
// Material to use.
std::string material_name;
Options()
: outputfile("example_native.png")
, res_x(700)
, res_y(520)
, use_class_compilation(false)
, use_custom_tex_runtime(false)
, enable_derivatives(false)
{}
};
// The last row is always implied to be (0, 0, 0, 1).
const mi::Float32_3_4 identity(
1.0f, 0.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f
);
// Creates an instance of the given material.
void create_material_instance(
const char* material_name,
const char* instance_name)
{
// split module and material name
std::string module_name, material_simple_name;
if (!mi::examples::mdl::parse_cmd_argument_material_name(
material_name, module_name, material_simple_name, true))
exit_failure();
// Load the module.
mdl_impexp_api->load_module(transaction, module_name.c_str(), context);
if (!print_messages(context))
exit_failure("Loading module '%s' failed.", module_name.c_str());
// Get the database name for the module we loaded
factory->get_db_module_name(module_name.c_str()));
// attach the material name
std::string material_db_name =
std::string(module_db_name->get_c_str()) + "::" + material_simple_name;
// Get the material definition from the database
transaction->access<mi::neuraylib::IMaterial_definition>(material_db_name.c_str()));
if (!material_definition)
exit_failure("Accessing definition '%s' failed.", material_db_name.c_str());
// Create a material instance from the material definition with the default arguments.
mi::Sint32 result;
material_definition->create_material_instance(0, &result));
if (result != 0)
exit_failure("Instantiating '%s' failed.", material_db_name.c_str());
transaction->store(material_instance.get(), instance_name);
}
// Compiles the given material instance in the given compilation modes and stores it in the DB.
void compile_material_instance(
const char* instance_name,
const char* compiled_material_name,
bool class_compilation)
{
transaction->access<mi::neuraylib::IMaterial_instance>(instance_name));
mi::Uint32 flags = class_compilation
material_instance->create_compiled_material(flags, context));
check_success(print_messages(context));
transaction->store(compiled_material.get(), compiled_material_name);
}
// Generate and execute native CPU code for a subexpression of a given compiled material.
mi::neuraylib::ITarget_code const *generate_native(
const char* compiled_material_name,
const char* path,
const char* fname,
bool use_custom_tex_runtime,
bool enable_derivatives)
{
transaction->access<mi::neuraylib::ICompiled_material>(compiled_material_name));
check_success(be_native->set_option("num_texture_spaces", "1") == 0);
if (use_custom_tex_runtime)
check_success(be_native->set_option("use_builtin_resource_handler", "off") == 0);
if (enable_derivatives)
check_success(be_native->set_option("texture_runtime_with_derivs", "on") == 0);
// Generate the native code
be_native->translate_material_expression(
transaction, compiled_material.get(), path, fname, context));
check_success(print_messages(context));
check_success(code_native);
code_native->retain();
return code_native.get();
}
// Bake the material expression created with the native backend into a canvas with the given
// resolution.
mi::neuraylib::ICanvas *bake_expression_native(
mi::neuraylib::ITarget_code const *code_native,
mi::Uint32 width,
mi::Uint32 height)
{
// Create a canvas (with only one tile)
image_api->create_canvas("Rgb_fp", width, height));
// Setup MDL material state (with only one texture space)
mi::Float32_3_struct texture_coords[1] = { { 0.0f, 0.0f, 0.0f } };
mi::Float32_3_struct texture_tangent_u[1] = { { 1.0f, 0.0f, 0.0f } };
mi::Float32_3_struct texture_tangent_v[1] = { { 0.0f, 1.0f, 0.0f } };
/*normal=*/ { 0.0f, 0.0f, 1.0f },
/*geom_normal=*/ { 0.0f, 0.0f, 1.0f },
/*position=*/ { 0.0f, 0.0f, 0.0f },
/*animation_time=*/ 0.0f,
/*texture_coords=*/ texture_coords,
/*tangent_u=*/ texture_tangent_u,
/*tangent_v=*/ texture_tangent_v,
/*text_results=*/ nullptr,
/*ro_data_segment=*/ nullptr,
/*world_to_object=*/ &identity[0],
/*object_to_world=*/ &identity[0],
/*object_id=*/ 0,
/*meters_per_scene_unit=*/ 1.0f
};
// Provide a large enough buffer for any result type.
// In this case, we know, we will get a color which is a float3, so this is overkill.
union
{
int int_val;
float float_val;
double double_val;
mi::Float32_4_4_struct float4x4_val;
mi::Float64_3_struct double3_val;
mi::Float64_4_struct double4_val;
mi::Float64_4_4_struct double4x4_val;
} execute_result = { 0 };
// Calculate all expression values for a 2x2 quad around the center of the world
// and write them to the canvas.
mi::Float32_3_struct *data = static_cast<mi::Float32_3_struct *>(tile->get_data());
for (mi::Uint32 y = 0; y < height; ++y) {
for (mi::Uint32 x = 0; x < width; ++x) {
// Update state for the current pixel
float rel_x = float(x) / float(width);
float rel_y = float(y) / float(height);
mdl_state.position.x = 2.0f * rel_x - 1; // [-1, 1)
mdl_state.position.y = 2.0f * rel_y - 1; // [-1, 1)
texture_coords[0].x = rel_x; // [0, 1)
texture_coords[0].y = rel_y; // [0, 1)
// Evaluate sub-expression
check_success(
code_native->execute(0, mdl_state, tex_handler, nullptr, &execute_result) == 0);
// Apply gamma correction
execute_result.float3_val.x = powf(execute_result.float3_val.x, 1.f / 2.2f);
execute_result.float3_val.y = powf(execute_result.float3_val.y, 1.f / 2.2f);
execute_result.float3_val.z = powf(execute_result.float3_val.z, 1.f / 2.2f);
// Store result in texture
data[y * width + x] = execute_result.float3_val;
}
}
canvas->retain();
return canvas.get();
}
// Bake the material expression created with the native backend into a canvas with the given
// resolution with derivative support.
mi::neuraylib::ICanvas *bake_expression_native_with_derivs(
mi::neuraylib::ITarget_code const *code_native,
mi::Uint32 width,
mi::Uint32 height)
{
// Create a canvas (with only one tile)
image_api->create_canvas("Rgb_fp", width, height));
// Setup MDL material state (with only one texture space)
float step_x = 1.f / width;
float step_y = 1.f / height;
mi::neuraylib::tct_deriv_float3 texture_coords[1] = {
{
{ 0.0f, 0.0f, 0.0f }, // value component
{ step_x, 0.0f, 0.0f }, // dx component
{ 0.0f, step_y, 0.0f } // dy component
} };
mi::neuraylib::tct_float3 texture_tangent_u[1] = { { 1.0f, 0.0f, 0.0f } };
mi::neuraylib::tct_float3 texture_tangent_v[1] = { { 0.0f, 1.0f, 0.0f } };
/*normal=*/ { 0.0f, 0.0f, 1.0f },
/*geom_normal=*/ { 0.0f, 0.0f, 1.0f },
/*position=*/
{
{ 0.0f, 0.0f, 0.0f }, // value component
{ 2 * step_x, 0.0f, 0.0f }, // dx component
{ 0.0f, 2 * step_y, 0.0f } // dy component
},
/*animation_time=*/ 0.0f,
/*texture_coords=*/ texture_coords,
/*tangent_u=*/ texture_tangent_u,
/*tangent_v=*/ texture_tangent_v,
/*text_results=*/ nullptr,
/*ro_data_segment=*/ nullptr,
/*world_to_object=*/ &identity[0],
/*object_to_world=*/ &identity[0],
/*object_id=*/ 0,
/*meters_per_scene_unit=*/ 1.0f
};
// Provide a large enough buffer for any result type.
// In this case, we know, we will get a color which is a float3, so this is overkill.
union
{
int int_val;
float float_val;
double double_val;
mi::Float32_4_4_struct float4x4_val;
mi::Float64_3_struct double3_val;
mi::Float64_4_struct double4_val;
mi::Float64_4_4_struct double4x4_val;
} execute_result = { 0 };
// Calculate all expression values for a 2x2 quad around the center of the world
// and write them to the canvas.
mi::Float32_3_struct *data = static_cast<mi::Float32_3_struct *>(tile->get_data());
for (mi::Uint32 y = 0; y < height; ++y) {
for (mi::Uint32 x = 0; x < width; ++x) {
// Update state for the current pixel
float rel_x = x * step_x;
float rel_y = y * step_y;
mdl_state.position.val.x = 2.0f * rel_x - 1; // [-1, 1)
mdl_state.position.val.y = 2.0f * rel_y - 1; // [-1, 1)
texture_coords[0].val.x = rel_x; // [0, 1)
texture_coords[0].val.y = rel_y; // [0, 1)
// Evaluate sub-expression
check_success(code_native->execute(
0,
reinterpret_cast<mi::neuraylib::Shading_state_material &>(mdl_state),
reinterpret_cast<mi::neuraylib::Texture_handler_base *>(tex_handler),
nullptr,
&execute_result) == 0);
// Apply gamma correction
execute_result.float3_val.x = powf(execute_result.float3_val.x, 1.f / 2.2f);
execute_result.float3_val.y = powf(execute_result.float3_val.y, 1.f / 2.2f);
execute_result.float3_val.z = powf(execute_result.float3_val.z, 1.f / 2.2f);
// Store result in texture
data[y * width + x] = execute_result.float3_val;
}
}
canvas->retain();
return canvas.get();
}
// Prepare the textures for our own texture runtime.
bool prepare_textures(
std::vector<Texture>& textures,
const mi::neuraylib::ITarget_code* target_code)
{
for (mi::Size i = 1 /*skip invalid texture*/; i < target_code->get_texture_count(); ++i)
{
transaction->access<const mi::neuraylib::ITexture>(
target_code->get_texture(i)));
transaction->access<mi::neuraylib::IImage>(texture->get_image()));
char const *image_type = image->get_type();
if (image->is_uvtile()) {
std::cerr << "The example does not support uvtile textures!" << std::endl;
return false;
}
if (canvas->get_tiles_size_x() != 1 || canvas->get_tiles_size_y() != 1) {
std::cerr << "The example does not support tiled images!" << std::endl;
return false;
}
// For simplicity, the texture access functions are only implemented for float4 and gamma
// is pre-applied here (all images are converted to linear space).
// Convert to linear color space if necessary
if (texture->get_effective_gamma() != 1.0f) {
// Copy/convert to float4 canvas and adjust gamma from "effective gamma" to 1.
image_api->convert(canvas.get(), "Color"));
gamma_canvas->set_gamma(texture->get_effective_gamma());
image_api->adjust_gamma(gamma_canvas.get(), 1.0f);
canvas = gamma_canvas;
}
else if (strcmp(image_type, "Color") != 0 && strcmp(image_type, "Float32<4>") != 0) {
// Convert to expected format
canvas = image_api->convert(canvas.get(), "Color");
}
textures.push_back(Texture(canvas));
}
return true;
}
// Print command line usage to console and terminate the application.
void usage(char const *prog_name)
{
std::cout
<< "Usage: " << prog_name << " [options] [<material_name>]\n"
<< "Options:\n"
<< " --res <x> <y> resolution (default: 700x520)\n"
<< " --cc use class compilation\n"
<< " --cr use custom texture runtime\n"
<< " -d enable use of derivatives\n"
<< " (not supported in combination with --cr by this example)\n"
<< " -o <outputfile> image file to write result to\n"
<< " (default: example_native.png)\n"
<< " --mdl_path <path> mdl search path, can occur multiple times."
<< std::endl;
exit_failure();
}
//------------------------------------------------------------------------------
//
// Main function
//
//------------------------------------------------------------------------------
int MAIN_UTF8(int argc, char *argv[])
{
// Parse command line options
Options options;
mi::examples::mdl::Configure_options configure_options;
configure_options.add_example_search_path = false;
for (int i = 1; i < argc; ++i) {
char const *opt = argv[i];
if (opt[0] == '-') {
if (strcmp(opt, "-o") == 0 && i < argc - 1) {
options.outputfile = argv[++i];
} else if (strcmp(opt, "--res") == 0 && i < argc - 2) {
options.res_x = std::max(atoi(argv[++i]), 1);
options.res_y = std::max(atoi(argv[++i]), 1);
} else if (strcmp(opt, "--cc") == 0) {
options.use_class_compilation = true;
} else if (strcmp(opt, "--cr") == 0) {
options.use_custom_tex_runtime = true;
} else if (strcmp(opt, "-d") == 0) {
options.enable_derivatives = true;
} else if (strcmp(opt, "--mdl_path") == 0 && i < argc - 1) {
configure_options.additional_mdl_paths.push_back(argv[++i]);
} else {
std::cout << "Unknown option: \"" << opt << "\"" << std::endl;
usage(argv[0]);
}
} else
options.material_name = opt;
}
// Use default material, if none was provided via command line
if (options.material_name.empty()) {
configure_options.add_example_search_path = true;
options.material_name = "::nvidia::sdk_examples::tutorials::example_execution1";
}
// Access the MDL SDK
mi::base::Handle<mi::neuraylib::INeuray> neuray(mi::examples::mdl::load_and_get_ineuray());
if (!neuray.is_valid_interface())
exit_failure("Failed to load the SDK.");
// Configure the MDL SDK
if (!mi::examples::mdl::configure(neuray.get(), configure_options))
exit_failure("Failed to initialize the SDK.");
// Start the MDL SDK
mi::Sint32 ret = neuray->start();
if (ret != 0)
exit_failure("Failed to initialize the SDK. Result code: %d", ret);
{
// Create a transaction
mi::base::Handle<mi::neuraylib::IScope> scope(database->get_global_scope());
mi::base::Handle<mi::neuraylib::ITransaction> transaction(scope->create_transaction());
{
mdl_factory->create_execution_context());
// Load the MDL module and create a material instance
std::string instance_name = "material instance";
create_material_instance(
mdl_factory.get(),
transaction.get(),
mdl_impexp_api.get(),
context.get(),
options.material_name.c_str(),
instance_name.c_str());
// Compile the material instance in instance compilation mode
std::string instance_compilation_name
= std::string("instance compilation of ") + instance_name;
// Compile the material instance
std::string compilation_name
= std::string("compilation of ") + instance_name;
compile_material_instance(
transaction.get(),
context.get(),
instance_name.c_str(),
compilation_name.c_str(),
options.use_class_compilation);
// Generate target code for some material expression
generate_native(
transaction.get(),
mdl_backend_api.get(),
context.get(),
compilation_name.c_str(),
"surface.scattering.tint", // MDL expression path
"tint", // name of generated function
options.use_custom_tex_runtime,
options.enable_derivatives));
// Acquire image API needed to create a canvas for baking
std::vector<Texture> textures;
if (options.use_custom_tex_runtime) {
// Setup custom texture handler
check_success(prepare_textures(
textures, transaction.get(), image_api.get(), target_code.get()));
}
// Bake the expression into a canvas
if (options.enable_derivatives) {
Texture_handler_deriv tex_handler;
Texture_handler_deriv *tex_handler_ptr = nullptr;
if (options.use_custom_tex_runtime) {
tex_handler.vtable = &tex_deriv_vtable;
tex_handler.num_textures = target_code->get_texture_count() - 1;
tex_handler.textures = textures.data();
tex_handler_ptr = &tex_handler;
}
canvas = bake_expression_native_with_derivs(
image_api.get(), target_code.get(), tex_handler_ptr,
options.res_x, options.res_y);
} else {
Texture_handler tex_handler;
Texture_handler *tex_handler_ptr = nullptr;
if (options.use_custom_tex_runtime) {
tex_handler.vtable = &tex_vtable;
tex_handler.num_textures = target_code->get_texture_count() - 1;
tex_handler.textures = textures.data();
tex_handler_ptr = &tex_handler;
}
canvas = bake_expression_native(
image_api.get(), target_code.get(), tex_handler_ptr,
options.res_x, options.res_y);
}
// Export the canvas to an image on disk
mdl_impexp_api->export_canvas(options.outputfile.c_str(), canvas.get());
}
transaction->commit();
}
// Shut down the MDL SDK
if (neuray->shutdown() != 0)
exit_failure("Failed to shutdown the SDK.");
// Unload the MDL SDK
neuray = nullptr;
if (!mi::examples::mdl::unload())
exit_failure("Failed to unload the SDK.");
exit_success();
}
// Convert command line arguments to UTF8 on Windows
COMMANDLINE_TO_UTF8

Source Code Location: examples/mdl_sdk/execution_native/texture_support.h

/******************************************************************************
* Copyright 2020 NVIDIA Corporation. All rights reserved.
*****************************************************************************/
// This file contains the implementations and the vtable of the texture access functions.
#ifndef TEXTURE_SUPPORT_H
#define TEXTURE_SUPPORT_H
#include "example_shared.h"
#define USE_SMOOTHERSTEP_FILTER
typedef mi::neuraylib::Texture_handler_base Texture_handler_base;
typedef mi::neuraylib::Texture_handler_deriv_base Texture_handler_deriv_base;
// Custom structure representing an MDL texture
struct Texture
{
: canvas(c)
, ncomp(4)
{
// for now, we only support floating point rgba
check_success(strcmp(canvas->get_type(), "Color") == 0);
size.x = canvas->get_resolution_x();
size.y = canvas->get_resolution_y();
size.z = canvas->get_layers_size();
data = static_cast<const mi::Float32*> (tile->get_data());
}
mi::Float32 const *data; // texture data for fast access
mi::Uint32_3_struct size; // size of the texture
mi::Uint32 ncomp; // components per pixel
};
// The texture handler structure required by the MDL SDK with custom additional fields.
struct Texture_handler : Texture_handler_base {
// additional data for the texture access functions can be provided here
size_t num_textures; // the number of textures used by the material
// (without the invalid texture)
Texture const *textures; // the textures used by the material
// (without the invalid texture)
};
// The texture handler structure required by the MDL SDK with custom additional fields.
struct Texture_handler_deriv : Texture_handler_deriv_base {
// additional data for the texture access functions can be provided here
size_t num_textures; // the number of textures used by the material
// (without the invalid texture)
Texture const *textures; // the textures used by the material
// (without the invalid texture)
};
// Stores a float4 in a float[4] array.
inline static void store_result4(float res[4], const mi::Float32_4_struct &v)
{
res[0] = v.x;
res[1] = v.y;
res[2] = v.z;
res[3] = v.w;
}
// Stores a float in all elements of a float[4] array.
static inline void store_result4(float res[4], const float v)
{
res[0] = res[1] = res[2] = res[3] = v;
}
// Stores the given float values in a float[4] array.
static inline void store_result4(
float res[4], const float v0, const float v1, const float v2, const float v3)
{
res[0] = v0;
res[1] = v1;
res[2] = v2;
res[3] = v3;
}
// Stores a float4 in a float[3] array, ignoring v.w.
static inline void store_result3(float res[3], const mi::Float32_4_struct &v)
{
res[0] = v.x;
res[1] = v.y;
res[2] = v.z;
}
// Stores a float in all elements of a float[3] array.
static inline void store_result3(float res[3], const float v)
{
res[0] = res[1] = res[2] = v;
}
// Stores the given float values in a float[3] array.
static inline void store_result3(float res[3], const float v0, const float v1, const float v2)
{
res[0] = v0;
res[1] = v1;
res[2] = v2;
}
// ------------------------------------------------------------------------------------------------
// Textures
// ------------------------------------------------------------------------------------------------
static inline mi::Float32 u2f_rn(const mi::Uint32 u) {
return (mi::Float32) u;
}
static inline mi::Uint32 f2u_rz(const mi::Float32 f) {
return (mi::Uint32) f;
}
static inline mi::Sint64 f2ll_rz(const mi::Float32 f) {
return (mi::Sint64) f;
}
static inline mi::Sint64 f2ll_rd(const mi::Float32 f) {
}
mi::Uint32 texremap(
mi::Uint32 tex_size, Tex_wrap_mode wrap_mode, const mi::Sint32 crop_offset, float texf)
{
mi::Sint32 texi = 0;
const mi::Sint64 texil = f2ll_rz(texf);
const mi::Sint64 texsizel = mi::Sint64(tex_size);
if (mi::Uint64(f2ll_rd(texf)) >= mi::Uint64(tex_size)) {
// Wrap or clamp
texi = (int) std::min(std::max(texil, 0ll), (texsizel - 1ll));
// Repeat
else {
texi = mi::Sint32(texil % texsizel);
const mi::Sint32 s = mi::math::sign_bit(texf);
const mi::Sint64 d = texil / (mi::Sint64) tex_size;
const mi::Sint32 a =
((mi::Sint32) d^s) & 1;
const bool alternate = (a != 0);
if (alternate) // Flip negative tex
texi = -texi;
if (s != a) // "Otherwise" pad negative tex back to positive
texi += (mi::Sint32) tex_size - 1;
}
}
else texi = (int) texil;
// Crop
texi += crop_offset;
return mi::Uint32(texi);
}
void tex_lookup2D(
mi::Float32 res[4],
Texture const &tex,
const mi::Float32 uv[2],
const mi::Float32 crop_u[2],
const mi::Float32 crop_v[2])
{
const mi::Float32 crop_w = crop_u[1] - crop_u[0];
const mi::Float32 crop_h = crop_v[1] - crop_v[0];
const mi::Sint32_2 crop_offset(
f2u_rz(u2f_rn(tex.size.x-1) * crop_u[0]),
f2u_rz(u2f_rn(tex.size.y-1) * crop_v[0]));
const mi::Uint32_2 crop_texres(
std::max(f2u_rz(u2f_rn(tex.size.x) * crop_w), 1u),
std::max(f2u_rz(u2f_rn(tex.size.y) * crop_h), 1u));
const float U = uv[0] * crop_texres.x - 0.5f;
const float V = uv[1] * crop_texres.y - 0.5f;
const mi::Uint32 U0 = texremap(crop_texres.x, wrap_u, crop_offset[0], U);
const mi::Uint32 U1 = texremap(crop_texres.x, wrap_u, crop_offset[0], U+1.0f);
const mi::Uint32 V0 = texremap(crop_texres.y, wrap_v, crop_offset[1], V);
const mi::Uint32 V1 = texremap(crop_texres.y, wrap_v, crop_offset[1], V+1.0f);
const mi::Uint32 i00 = (tex.size.x * V0 + U0) * tex.ncomp;
const mi::Uint32 i01 = (tex.size.x * V0 + U1) * tex.ncomp;
const mi::Uint32 i10 = (tex.size.x * V1 + U0) * tex.ncomp;
const mi::Uint32 i11 = (tex.size.x * V1 + U1) * tex.ncomp;
mi::Float32 ufrac = U - mi::math::floor(U);
mi::Float32 vfrac = V - mi::math::floor(V);
#ifdef USE_SMOOTHERSTEP_FILTER
ufrac *= ufrac*ufrac*(ufrac*(ufrac*6.0f - 15.0f) + 10.0f); // smoother step
vfrac *= vfrac*vfrac*(vfrac*(vfrac*6.0f - 15.0f) + 10.0f);
#endif
mi::Float32_4(tex.data[i00 + 0], tex.data[i00 + 1], tex.data[i00 + 2], tex.data[i00 + 3]),
mi::Float32_4(tex.data[i01 + 0], tex.data[i01 + 1], tex.data[i01 + 2], tex.data[i01 + 3]),
ufrac);
mi::Float32_4(tex.data[i10 + 0], tex.data[i10 + 1], tex.data[i10 + 2], tex.data[i10 + 3]),
mi::Float32_4(tex.data[i11 + 0], tex.data[i11 + 1], tex.data[i11 + 2], tex.data[i11 + 3]),
ufrac);
store_result4(res, mi::math::lerp(c1, c2, vfrac));
}
void tex_lookup_float4_2d(
mi::Float32 result[4],
mi::Uint32 texture_idx,
const mi::Float32 coord[2],
const mi::Float32 crop_u[2],
const mi::Float32 crop_v[2])
{
Texture_handler const *self = static_cast<Texture_handler const *>(self_base);
if (texture_idx == 0 || texture_idx - 1 >= self->num_textures) {
// invalid texture returns zero
store_result4(result, 0.0f);
return;
}
Texture const &tex = self->textures[texture_idx - 1];
tex_lookup2D(result, tex, coord, wrap_u, wrap_v, crop_u, crop_v);
}
void tex_lookup_deriv_float4_2d(
mi::Float32 result[4],
mi::Uint32 texture_idx,
const tct_deriv_float2 *coord,
const mi::Float32 crop_u[2],
const mi::Float32 crop_v[2])
{
Texture_handler const *self = static_cast<Texture_handler const *>(self_base);
if (texture_idx == 0 || texture_idx - 1 >= self->num_textures) {
// invalid texture returns zero
store_result4(result, 0.0f);
return;
}
mi::Float32 uv[2] = { coord->val.x, coord->val.y };
Texture const &tex = self->textures[texture_idx - 1];
tex_lookup2D(result, tex, uv, wrap_u, wrap_v, crop_u, crop_v);
}
void tex_lookup_float3_2d(
mi::Float32 result[3],
mi::Uint32 texture_idx,
const mi::Float32 coord[2],
const mi::Float32 crop_u[2],
const mi::Float32 crop_v[2])
{
Texture_handler const *self = static_cast<Texture_handler const *>(self_base);
if (texture_idx == 0 || texture_idx - 1 >= self->num_textures) {
// invalid texture returns zero
store_result3(result, 0.0f);
return;
}
tex_lookup_float4_2d(c, self, texture_idx, coord, wrap_u, wrap_v, crop_u, crop_v);
result[0] = c[0];
result[1] = c[1];
result[2] = c[2];
}
void tex_lookup_deriv_float3_2d(
mi::Float32 result[3],
mi::Uint32 texture_idx,
const tct_deriv_float2 *coord,
const mi::Float32 crop_u[2],
const mi::Float32 crop_v[2])
{
Texture_handler const *self = static_cast<Texture_handler const *>(self_base);
if (texture_idx == 0 || texture_idx - 1 >= self->num_textures) {
// invalid texture returns zero
store_result3(result, 0.0f);
return;
}
mi::Float32 uv[2] = { coord->val.x, coord->val.y };
tex_lookup_float4_2d(c, self, texture_idx, uv, wrap_u, wrap_v, crop_u, crop_v);
result[0] = c[0];
result[1] = c[1];
result[2] = c[2];
}
void tex_texel_float4_2d(
mi::Float32 result[4],
mi::Uint32 texture_idx,
const mi::Sint32 coord[2],
const mi::Sint32 uv_tile[2])
{
Texture_handler const *self = static_cast<Texture_handler const *>(self_base);
if (texture_idx == 0 || texture_idx - 1 >= self->num_textures) {
// invalid texture returns zero
store_result3(result, 0.0f);
return;
}
Texture const &tex = self->textures[texture_idx - 1];
const mi::Uint32 idx = (tex.size.x * coord[1] + coord[0]) * tex.ncomp;
store_result4(result,
mi::Float32_4(tex.data[idx + 0], tex.data[idx + 1], tex.data[idx + 2], tex.data[idx + 3]));
}
void tex_lookup_float4_3d(
mi::Float32 result[4],
mi::Uint32 texture_idx,
const mi::Float32 coord[3],
const mi::Float32 crop_u[2],
const mi::Float32 crop_v[2],
const mi::Float32 crop_w[2])
{
result[0] = 0.0f;
result[1] = 0.0f;
result[2] = 0.0f;
result[3] = 1.0f;
}
void tex_lookup_float3_3d(
mi::Float32 result[3],
mi::Uint32 texture_idx,
const mi::Float32 coord[3],
const mi::Float32 crop_u[2],
const mi::Float32 crop_v[2],
const mi::Float32 crop_w[2])
{
result[0] = 0.0f;
result[1] = 0.0f;
result[2] = 0.0f;
}
void tex_texel_float4_3d(
mi::Float32 result[4],
mi::Uint32 texture_idx,
const mi::Sint32 coord[3])
{
result[0] = 0.0f;
result[1] = 0.0f;
result[2] = 0.0f;
result[3] = 1.0f;
}
void tex_lookup_float4_cube(
mi::Float32 result[4],
mi::Uint32 texture_idx,
const mi::Float32 coord[3])
{
result[0] = 0.0f;
result[1] = 0.0f;
result[2] = 0.0f;
result[3] = 1.0f;
}
void tex_lookup_float3_cube(
mi::Float32 result[3],
mi::Uint32 texture_idx,
const mi::Float32 coord[3])
{
result[0] = 0.0f;
result[1] = 0.0f;
result[2] = 0.0f;
}
void tex_resolution_2d(
mi::Sint32 result[2],
mi::Uint32 texture_idx,
const mi::Sint32 uv_tile[2])
{
Texture_handler const *self = static_cast<Texture_handler const *>(self_base);
if (texture_idx == 0 || texture_idx - 1 >= self->num_textures) {
// invalid texture returns zero
result[0] = 0;
result[1] = 0;
return;
}
Texture const &tex = self->textures[texture_idx - 1];
result[0] = tex.size.x;
result[1] = tex.size.y;
}
void tex_resolution_3d(
mi::Sint32 result[3],
mi::Uint32 texture_idx)
{
result[0] = 0;
result[1] = 0;
result[2] = 0;
}
bool tex_texture_isvalid(
mi::Uint32 texture_idx)
{
Texture_handler const *self = static_cast<Texture_handler const *>(self_base);
return texture_idx != 0 && texture_idx - 1 < self->num_textures;
}
// ------------------------------------------------------------------------------------------------
// Light Profiles (dummy functions)
// ------------------------------------------------------------------------------------------------
mi::Float32 df_light_profile_power(
const Texture_handler_base *self,
mi::Uint32 light_profile_idx)
{
return 0.0f;
}
mi::Float32 df_light_profile_maximum(
const Texture_handler_base *self,
mi::Uint32 light_profile_idx)
{
return 0.0f;
}
bool df_light_profile_isvalid(
const Texture_handler_base *self,
mi::Uint32 light_profile_idx)
{
return false;
}
mi::Float32 df_light_profile_evaluate(
const Texture_handler_base *self,
mi::Uint32 light_profile_idx,
const float theta_phi[2])
{
return 0.0f;
}
void df_light_profile_sample(
mi::Float32 result[3], // theta, phi, pdf
const Texture_handler_base *self,
mi::Uint32 light_profile_idx,
const float xi[3])
{
result[0] = 0.0f;
result[1] = 0.0f;
result[2] = 0.0f;
}
mi::Float32 df_light_profile_pdf(
const Texture_handler_base *self,
mi::Uint32 light_profile_idx,
const float theta_phi[2])
{
return 0.0f;
}
// ------------------------------------------------------------------------------------------------
// BSDF measurements (dummy functions)
// ------------------------------------------------------------------------------------------------
bool df_bsdf_measurement_isvalid(
const Texture_handler_base *self,
mi::Uint32 bsdf_measurement_index)
{
return false;
}
void df_bsdf_measurement_resolution(
mi::Uint32 result[3],
const Texture_handler_base *self,
mi::Uint32 bsdf_measurement_index,
{
result[0] = 0;
result[1] = 0;
result[2] = 0;
}
void df_bsdf_measurement_evaluate(
mi::Float32 result[3],
const Texture_handler_base *self,
mi::Uint32 bsdf_measurement_index,
const mi::Float32 theta_phi_in[2],
const mi::Float32 theta_phi_out[2],
{
result[0] = 0.0f;
result[1] = 0.0f;
result[2] = 0.0f;
}
void df_bsdf_measurement_sample(
mi::Float32 result[3],
const Texture_handler_base *self,
mi::Uint32 bsdf_measurement_index,
const mi::Float32 theta_phi_out[2],
const mi::Float32 xi[3],
{
result[0] = 0.0f;
result[1] = 0.0f;
result[2] = 0.0f;
}
mi::Float32 df_bsdf_measurement_pdf(
const Texture_handler_base *self,
mi::Uint32 bsdf_measurement_index,
const mi::Float32 theta_phi_in[2],
const mi::Float32 theta_phi_out[2],
{
return 0.0f;
}
void df_bsdf_measurement_albedos(
mi::Float32 result[4],
const Texture_handler_base *self,
mi::Uint32 bsdf_measurement_index,
const mi::Float32 theta_phi[2])
{
result[0] = 0.0f;
result[1] = 0.0f;
result[2] = 0.0f;
result[3] = 0.0f;
}
// ------------------------------------------------------------------------------------------------
// Scene data (dummy functions)
// ------------------------------------------------------------------------------------------------
bool scene_data_isvalid(
Texture_handler_base const *self_base,
Shading_state_material *state,
unsigned scene_data_id)
{
return false;
}
void scene_data_lookup_float4(
float result[4],
Texture_handler_base const *self_base,
Shading_state_material *state,
unsigned scene_data_id,
float const default_value[4],
bool uniform_lookup)
{
// just return default value
result[0] = default_value[0];
result[1] = default_value[1];
result[2] = default_value[2];
result[3] = default_value[3];
}
void scene_data_lookup_float3(
float result[3],
Texture_handler_base const *self_base,
Shading_state_material *state,
unsigned scene_data_id,
float const default_value[3],
bool uniform_lookup)
{
// just return default value
result[0] = default_value[0];
result[1] = default_value[1];
result[2] = default_value[2];
}
void scene_data_lookup_color(
float result[3],
Texture_handler_base const *self_base,
Shading_state_material *state,
unsigned scene_data_id,
float const default_value[3],
bool uniform_lookup)
{
// just return default value
result[0] = default_value[0];
result[1] = default_value[1];
result[2] = default_value[2];
}
void scene_data_lookup_float2(
float result[2],
Texture_handler_base const *self_base,
Shading_state_material *state,
unsigned scene_data_id,
float const default_value[2],
bool uniform_lookup)
{
// just return default value
result[0] = default_value[0];
result[1] = default_value[1];
}
float scene_data_lookup_float(
Texture_handler_base const *self_base,
Shading_state_material *state,
unsigned scene_data_id,
float const default_value,
bool uniform_lookup)
{
// just return default value
return default_value;
}
void scene_data_lookup_int4(
int result[4],
Texture_handler_base const *self_base,
Shading_state_material *state,
unsigned scene_data_id,
int const default_value[4],
bool uniform_lookup)
{
// just return default value
result[0] = default_value[0];
result[1] = default_value[1];
result[2] = default_value[2];
result[3] = default_value[3];
}
void scene_data_lookup_int3(
int result[3],
Texture_handler_base const *self_base,
Shading_state_material *state,
unsigned scene_data_id,
int const default_value[3],
bool uniform_lookup)
{
// just return default value
result[0] = default_value[0];
result[1] = default_value[1];
result[2] = default_value[2];
}
void scene_data_lookup_int2(
int result[2],
Texture_handler_base const *self_base,
Shading_state_material *state,
unsigned scene_data_id,
int const default_value[2],
bool uniform_lookup)
{
// just return default value
result[0] = default_value[0];
result[1] = default_value[1];
}
int scene_data_lookup_int(
Texture_handler_base const *self_base,
Shading_state_material *state,
unsigned scene_data_id,
int default_value,
bool uniform_lookup)
{
// just return default value
return default_value;
}
void scene_data_lookup_deriv_float4(
tct_deriv_arr_float_4 *result,
Texture_handler_base const *self_base,
Shading_state_material_with_derivs *state,
unsigned scene_data_id,
tct_deriv_arr_float_4 const *default_value,
bool uniform_lookup)
{
// just return default value
*result = *default_value;
}
void scene_data_lookup_deriv_float3(
tct_deriv_arr_float_3 *result,
Texture_handler_base const *self_base,
Shading_state_material_with_derivs *state,
unsigned scene_data_id,
tct_deriv_arr_float_3 const *default_value,
bool uniform_lookup)
{
// just return default value
*result = *default_value;
}
void scene_data_lookup_deriv_color(
tct_deriv_arr_float_3 *result,
Texture_handler_base const *self_base,
Shading_state_material_with_derivs *state,
unsigned scene_data_id,
tct_deriv_arr_float_3 const *default_value,
bool uniform_lookup)
{
// just return default value
*result = *default_value;
}
void scene_data_lookup_deriv_float2(
tct_deriv_arr_float_2 *result,
Texture_handler_base const *self_base,
Shading_state_material_with_derivs *state,
unsigned scene_data_id,
tct_deriv_arr_float_2 const *default_value,
bool uniform_lookup)
{
// just return default value
*result = *default_value;
}
void scene_data_lookup_deriv_float(
tct_deriv_float *result,
Texture_handler_base const *self_base,
Shading_state_material_with_derivs *state,
unsigned scene_data_id,
tct_deriv_float const *default_value,
bool uniform_lookup)
{
// just return default value
*result = *default_value;
}
// ------------------------------------------------------------------------------------------------
// Vtables
// ------------------------------------------------------------------------------------------------
tex_lookup_float4_2d,
tex_lookup_float3_2d,
tex_texel_float4_2d,
tex_lookup_float4_3d,
tex_lookup_float3_3d,
tex_texel_float4_3d,
tex_lookup_float4_cube,
tex_lookup_float3_cube,
tex_resolution_2d,
tex_resolution_3d,
tex_texture_isvalid,
df_light_profile_power,
df_light_profile_maximum,
df_light_profile_isvalid,
df_light_profile_evaluate,
df_light_profile_sample,
df_light_profile_pdf,
df_bsdf_measurement_isvalid,
df_bsdf_measurement_resolution,
df_bsdf_measurement_evaluate,
df_bsdf_measurement_sample,
df_bsdf_measurement_pdf,
df_bsdf_measurement_albedos,
scene_data_isvalid,
scene_data_lookup_float,
scene_data_lookup_float2,
scene_data_lookup_float3,
scene_data_lookup_float4,
scene_data_lookup_int,
scene_data_lookup_int2,
scene_data_lookup_int3,
scene_data_lookup_int4,
scene_data_lookup_color,
};
tex_lookup_deriv_float4_2d,
tex_lookup_deriv_float3_2d,
tex_texel_float4_2d,
tex_lookup_float4_3d,
tex_lookup_float3_3d,
tex_texel_float4_3d,
tex_lookup_float4_cube,
tex_lookup_float3_cube,
tex_resolution_2d,
tex_resolution_3d,
tex_texture_isvalid,
df_light_profile_power,
df_light_profile_maximum,
df_light_profile_isvalid,
df_light_profile_evaluate,
df_light_profile_sample,
df_light_profile_pdf,
df_bsdf_measurement_isvalid,
df_bsdf_measurement_resolution,
df_bsdf_measurement_evaluate,
df_bsdf_measurement_sample,
df_bsdf_measurement_pdf,
df_bsdf_measurement_albedos,
scene_data_isvalid,
scene_data_lookup_float,
scene_data_lookup_float2,
scene_data_lookup_float3,
scene_data_lookup_float4,
scene_data_lookup_int,
scene_data_lookup_int2,
scene_data_lookup_int3,
scene_data_lookup_int4,
scene_data_lookup_color,
scene_data_lookup_deriv_float,
scene_data_lookup_deriv_float2,
scene_data_lookup_deriv_float3,
scene_data_lookup_deriv_float4,
scene_data_lookup_deriv_color,
};
#endif
[Previous] [Up] [Next]