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.
#include <iomanip>
#include <iostream>
#include <sstream>
#include <string>
#include "example_shared.h"
#include "texture_support.h"
#include <vector>
struct Options {
unsigned res_x, res_y;
bool use_class_compilation;
bool use_custom_tex_runtime;
bool enable_derivatives;
Options()
: outputfile("example_native.png")
, res_x(700)
, res_y(520)
, use_class_compilation(false)
, use_custom_tex_runtime(false)
, enable_derivatives(false)
{}
};
1.0f, 0.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f
);
{
size_t p = material_name.
rfind(
"::");
return material_name.
substr(0, p);
}
void create_material_instance(
const char* material_name,
const char* instance_name)
{
std::string module_name = get_module_name(material_name);
check_success(mdl_compiler->
load_module(transaction, module_name.
c_str(), context) >= 0);
print_messages(context);
if (
strncmp(material_name,
"::", 2) == 0)
prefix = "mdl";
else
prefix = "mdl::";
material_definition->create_material_instance(0, &result));
check_success(result == 0);
transaction->
store(material_instance.get(), instance_name);
}
void compile_material_instance(
const char* instance_name,
const char* compiled_material_name,
bool class_compilation)
{
check_success(print_messages(context));
transaction->
store(compiled_material.get(), compiled_material_name);
}
const char* compiled_material_name,
const char* path,
const char* fname,
bool use_custom_tex_runtime,
bool enable_derivatives)
{
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);
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();
}
{
{ 0.0f, 0.0f, 1.0f },
{ 0.0f, 0.0f, 1.0f },
{ 0.0f, 0.0f, 0.0f },
0.0f,
texture_coords,
texture_tangent_u,
texture_tangent_v,
nullptr,
nullptr,
&identity[0],
&identity[0],
0
};
union
{
int int_val;
float float_val;
double double_val;
} execute_result = { 0 };
float rel_x = float(x) / float(width);
float rel_y = float(y) / float(height);
mdl_state.
position.x = 2.0f * rel_x - 1;
mdl_state.
position.y = 2.0f * rel_y - 1;
texture_coords[0].x = rel_x;
texture_coords[0].y = rel_y;
check_success(
code_native->
execute(0, mdl_state, tex_handler,
nullptr, &execute_result) == 0);
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);
data[y * width + x] = execute_result.float3_val;
}
}
}
{
float step_x = 1.f / width;
float step_y = 1.f / height;
{
{ 0.0f, 0.0f, 0.0f },
{ step_x, 0.0f, 0.0f },
{ 0.0f, step_y, 0.0f }
} };
{ 0.0f, 0.0f, 1.0f },
{ 0.0f, 0.0f, 1.0f },
{ 0.0f, 0.0f, 0.0f },
0.0f,
texture_coords,
texture_tangent_u,
texture_tangent_v,
nullptr,
nullptr,
&identity[0],
&identity[0],
0
};
union
{
int int_val;
float float_val;
double double_val;
} execute_result = { 0 };
float rel_x = x * step_x;
float rel_y = y * step_y;
mdl_state.
position.x = 2.0f * rel_x - 1;
mdl_state.
position.y = 2.0f * rel_y - 1;
texture_coords[0].val.x = rel_x;
texture_coords[0].val.y = rel_y;
check_success(code_native->
execute(
0,
reinterpret_cast<mi::neuraylib::Shading_state_material &>(mdl_state),
tex_handler,
nullptr,
&execute_result) == 0);
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);
data[y * width + x] = execute_result.float3_val;
}
}
}
bool prepare_textures(
{
{
char const *image_type = image->
get_type();
if (image->is_uvtile()) {
return false;
}
return false;
}
if (texture->get_effective_gamma() != 1.0f) {
gamma_canvas->set_gamma(texture->get_effective_gamma());
canvas = gamma_canvas;
}
else if (
strcmp(image_type,
"Color") != 0 &&
strcmp(image_type,
"Float32<4>") != 0) {
canvas = image_api->
convert(canvas.
get(),
"Color");
}
}
return true;
}
void usage(char const *prog_name)
{
<< "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."
keep_console_open();
}
int main(int argc, char *argv[])
{
Options options;
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) {
}
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) {
options.mdl_paths.push_back(argv[++i]);
} else {
usage(argv[0]);
}
} else
options.material_name = opt;
}
if (options.use_custom_tex_runtime && options.enable_derivatives) {
std::cout <<
"This example does not support using derivatives with the custom texture "
"runtime.\n";
usage(argv[0]);
}
if (options.material_name.empty()) {
options.mdl_paths.push_back(get_samples_mdl_root());
options.material_name = "::nvidia::sdk_examples::tutorials::example_execution1";
}
check_success(neuray.is_valid_interface());
for (
std::size_t i = 0; i < options.mdl_paths.size(); ++i)
check_success(mdl_compiler->
add_module_path(options.mdl_paths[i].c_str()) == 0);
check_start_success(result);
{
{
create_material_instance(
transaction.get(),
mdl_compiler.get(),
context.get(),
options.material_name.c_str(),
instance_name.c_str());
=
std::string(
"instance compilation of ") + instance_name;
compile_material_instance(
transaction.get(),
context.get(),
instance_name.c_str(),
compilation_name.
c_str(),
options.use_class_compilation);
generate_native(
transaction.get(),
mdl_compiler.get(),
context.get(),
compilation_name.
c_str(),
"surface.scattering.tint",
"tint",
options.use_custom_tex_runtime,
options.enable_derivatives));
Texture_handler tex_handler;
Texture_handler *tex_handler_ptr = nullptr;
if (options.use_custom_tex_runtime) {
check_success(prepare_textures(
textures, transaction.get(), image_api.get(), target_code.get()));
tex_handler.vtable = &tex_vtable;
tex_handler.textures = textures.
data();
tex_handler_ptr = &tex_handler;
}
if (options.enable_derivatives) {
canvas = bake_expression_native_with_derivs(
image_api.get(), target_code.get(), tex_handler_ptr,
options.res_x, options.res_y);
} else {
canvas = bake_expression_native(
image_api.get(), target_code.get(), tex_handler_ptr,
options.res_x, options.res_y);
}
}
}
mdl_compiler = 0;
check_success(neuray->shutdown() == 0);
neuray = 0;
check_success(unload());
keep_console_open();
return EXIT_SUCCESS;
}
#ifndef TEXTURE_SUPPORT_H
#define TEXTURE_SUPPORT_H
#define USE_SMOOTHERSTEP_FILTER
struct Texture
{
: canvas(c)
, ncomp(4)
{
data =
static_cast<const mi::Float32*
> (tile->get_data());
}
};
struct Texture_handler : Texture_handler_base {
size_t num_textures;
Texture const *textures;
};
{
res[0] = v.x;
res[1] = v.y;
res[2] = v.z;
res[3] = v.w;
}
static inline void store_result4(float res[4], const float v)
{
res[0] = res[1] = res[2] = res[3] = v;
}
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;
}
{
res[0] = v.x;
res[1] = v.y;
res[2] = v.z;
}
static inline void store_result3(float res[3], const float v)
{
res[0] = res[1] = res[2] = v;
}
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;
}
}
}
}
}
{
else {
const bool alternate = (a != 0);
if (alternate)
texi = -texi;
if (s != a)
}
}
else texi = (int) texil;
texi += crop_offset;
}
void tex_lookup2D(
Texture const &tex,
{
f2u_rz(u2f_rn(tex.size.x-1) * crop_u[0]),
f2u_rz(u2f_rn(tex.size.y-1) * crop_v[0]));
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;
#ifdef USE_SMOOTHERSTEP_FILTER
ufrac *= ufrac*ufrac*(ufrac*(ufrac*6.0f - 15.0f) + 10.0f);
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);
}
void tex_lookup_float4_2d(
{
Texture_handler const *self = static_cast<Texture_handler const *>(self_base);
if (texture_idx == 0 || texture_idx - 1 >= self->num_textures) {
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_float3_2d(
{
Texture_handler const *self = static_cast<Texture_handler const *>(self_base);
if (texture_idx == 0 || texture_idx - 1 >= self->num_textures) {
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_texel_float4_2d(
{
Texture_handler const *self = static_cast<Texture_handler const *>(self_base);
if (texture_idx == 0 || texture_idx - 1 >= self->num_textures) {
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(
{
result[0] = 0.0f;
result[1] = 0.0f;
result[2] = 0.0f;
result[3] = 1.0f;
}
void tex_lookup_float3_3d(
{
result[0] = 0.0f;
result[1] = 0.0f;
result[2] = 0.0f;
}
void tex_texel_float4_3d(
{
result[0] = 0.0f;
result[1] = 0.0f;
result[2] = 0.0f;
result[3] = 1.0f;
}
void tex_lookup_float4_cube(
{
result[0] = 0.0f;
result[1] = 0.0f;
result[2] = 0.0f;
result[3] = 1.0f;
}
void tex_lookup_float3_cube(
{
result[0] = 0.0f;
result[1] = 0.0f;
result[2] = 0.0f;
}
void tex_resolution_2d(
{
Texture_handler const *self = static_cast<Texture_handler const *>(self_base);
if (texture_idx == 0 || texture_idx - 1 >= self->num_textures) {
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;
}
const Texture_handler_base *self,
const float theta_phi[2])
{
return 0.0f;
}
void light_profile_sample(
const Texture_handler_base *self,
const float xi[3])
{
result[0] = 0.0f;
result[1] = 0.0f;
result[2] = 0.0f;
}
const Texture_handler_base *self,
const float theta_phi[2])
{
return 0.0f;
}
void bsdf_measurement_resolution(
const Texture_handler_base *self,
{
result[0] = 0;
result[1] = 0;
result[2] = 0;
}
void bsdf_measurement_evaluate(
const Texture_handler_base *self,
{
result[0] = 0.0f;
result[1] = 0.0f;
result[2] = 0.0f;
}
void bsdf_measurement_sample(
const Texture_handler_base *self,
{
result[0] = 0.0f;
result[1] = 0.0f;
result[2] = 0.0f;
}
const Texture_handler_base *self,
{
return 0.0f;
}
void bsdf_measurement_albedos(
const Texture_handler_base *self,
{
result[0] = 0.0f;
result[1] = 0.0f;
result[2] = 0.0f;
result[3] = 0.0f;
}
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,
light_profile_evaluate,
light_profile_sample,
light_profile_pdf,
bsdf_measurement_resolution,
bsdf_measurement_evaluate,
bsdf_measurement_sample,
bsdf_measurement_pdf,
bsdf_measurement_albedos
};
#endif