This example demonstrates how to generate target code for the native backend (CPU) from an MDL material and use the generated BSDF functions in a CPU based renderer to account for the surface.scattering
, emission
, thin_walled
and geometry.cutout_opacity
expressions of the material. The example implements a physically based path-tracer technique for rendering a single sphere with MDL materials. The sphere is illuminated by a point light and/or an HDR environment map as seen from a perspective camera. The MDL material is loaded from a module and compiled for native backend (CPU) in order to execute different BSDF target functions during renderings. The renderer takes advantage of multi-threading by assigning different spans of the framebuffer to the available logical cores.
To compile the source code, GLFW and GLEW are required. For detailed instructions, please refer to the Getting Started section.
#include <iomanip>
#include <iostream>
#define _USE_MATH_DEFINES
#include <sstream>
#include <string>
#include <thread>
#include <vector>
#include "example_shared.h"
#include "texture_support_native.h"
#include <GL/glew.h>
#define GLFW_INCLUDE_NONE
#include <GLFW/glfw3.h>
#define GL_DISPLAY_NATIVE
#include <utils/gl_display.h>
#if MI_PLATFORM_MACOSX
#include <sys/types.h>
#include <sys/sysctl.h>
#endif
#include "utils/profiling.h"
using namespace mi::examples::profiling;
#define USE_PARALLEL_RENDERING
static struct
{
const float DIRAC = -1.f;
const float PI = static_cast<float>(M_PI);
1.0f, 0.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f);
} Constants;
inline unsigned tea(unsigned N, unsigned val0, unsigned val1)
{
unsigned v0 = val0;
unsigned v1 = val1;
unsigned s0 = 0;
for (unsigned n = 0; n < N; n++)
{
s0 += 0x9e3779b9;
v0 += ((v1 << 4) + 0xa341316c) ^ (v1 + s0) ^ ((v1 >> 5) + 0xc8013ea4);
v1 += ((v0 << 4) + 0xad90777d) ^ (v0 + s0) ^ ((v0 >> 5) + 0x7e95761e);
}
return v0;
}
inline unsigned lcg(unsigned &prev)
{
const unsigned LCG_A = 1664525u;
const unsigned LCG_C = 1013904223u;
prev = (LCG_A * prev + LCG_C);
return prev & 0x00FFFFFF;
}
inline float rnd(unsigned &prev)
{
const unsigned next = lcg(prev);
return ((float)next / (float)0x01000000);
}
struct Window_context
{
bool mouse_event, key_event;
float env_intensity;
float omni_theta;
float omni_phi;
float omni_intensity;
int mouse_button;
int mouse_button_action;
int mouse_wheel_delta;
bool moving;
double move_start_x, move_start_y;
double move_dx, move_dy;
int zoom;
bool save_sreenshot;
Window_context()
: mouse_event(false)
, key_event(false)
, env_intensity(0.0f)
, omni_theta(0.0f)
, omni_phi(0.0f)
, omni_intensity(0.0f)
, mouse_button(0)
, mouse_button_action(0)
, mouse_wheel_delta(0)
, moving(false)
, move_start_x(0.0)
, move_start_y(0.0)
, move_dx(0.0)
, move_dy(0.0)
, zoom(0)
, save_sreenshot(false)
{}
static void handle_key(GLFWwindow *window, int key, int scancode, int action, int mods)
{
if (action == GLFW_PRESS)
{
Window_context *ctx = static_cast<Window_context*>(glfwGetWindowUserPointer(window));
if (mods&GLFW_MOD_CONTROL)
{
switch (key)
{
case GLFW_KEY_MINUS:
case GLFW_KEY_KP_SUBTRACT:
ctx->env_intensity -= 0.05f;
if (ctx->env_intensity < 0.f) ctx->env_intensity = 0.f;
ctx->key_event = true;
break;
case GLFW_KEY_KP_ADD:
case GLFW_KEY_EQUAL:
ctx->env_intensity += 0.05f;
ctx->key_event = true;
break;
}
}
else
{
switch (key)
{
case GLFW_KEY_ESCAPE:
glfwSetWindowShouldClose(window, GLFW_TRUE);
break;
case GLFW_KEY_DOWN:
ctx->omni_theta += 0.05f*Constants.PI;
ctx->key_event = true;
break;
case GLFW_KEY_UP:
ctx->omni_theta -= 0.05f*Constants.PI;
ctx->key_event = true;
break;
case GLFW_KEY_LEFT:
ctx->omni_phi -= 0.05f*Constants.PI;
ctx->key_event = true;
break;
case GLFW_KEY_RIGHT:
ctx->omni_phi += 0.05f*Constants.PI;
ctx->key_event = true;
break;
case GLFW_KEY_MINUS:
case GLFW_KEY_KP_SUBTRACT:
ctx->omni_intensity -= 1000.f;
if (ctx->omni_intensity < 0.f) ctx->omni_intensity = 0.f;
ctx->key_event = true;
break;
case GLFW_KEY_KP_ADD:
case GLFW_KEY_EQUAL:
ctx->omni_intensity += 1000.f;
ctx->key_event = true;
break;
case GLFW_KEY_ENTER:
ctx->save_sreenshot = true;
break;
default:
break;
}
}
}
ImGui_ImplGlfw_KeyCallback(window, key, scancode, action, mods);
}
static void handle_mouse_button(GLFWwindow *window, int button, int action, int mods)
{
Window_context *ctx = static_cast<Window_context*>(glfwGetWindowUserPointer(window));
ctx->mouse_button = button + 1;
ctx->mouse_button_action = action;
ImGui_ImplGlfw_MouseButtonCallback(window, button, action, mods);
}
static void handle_mouse_pos(GLFWwindow *window, double xpos, double ypos)
{
Window_context *ctx = static_cast<Window_context*>(glfwGetWindowUserPointer(window));
if (ctx->moving)
{
ctx->move_dx += xpos - ctx->move_start_x;
ctx->move_dy += ypos - ctx->move_start_y;
ctx->move_start_x = xpos;
ctx->move_start_y = ypos;
ctx->mouse_event = true;
}
}
static void handle_scroll(GLFWwindow *window, double xoffset, double yoffset)
{
Window_context *ctx = static_cast<Window_context*>(glfwGetWindowUserPointer(window));
if (yoffset > 0.0)
{
ctx->mouse_wheel_delta = 1;
ctx->mouse_event = true;
}
else if (yoffset < 0.0)
{
ctx->mouse_wheel_delta = -1;
ctx->mouse_event = true;
}
ImGui_ImplGlfw_ScrollCallback(window, xoffset, yoffset);
}
};
static inline float int_as_float(uint32_t v)
{
union
{
uint32_t bit;
float value;
} temp;
temp.bit = v;
return temp.value;
}
static inline uint32_t float_as_int(float v)
{
union
{
uint32_t bit;
float value;
} temp;
temp.value = v;
return temp.bit;
}
{
for (int i = 0; i < 3; ++i)
{
if (d[i] < min)
d[i] = min;
else if (d[i] > max)
d[i] = max;
}
}
{
return sqrtf(d.x * d.x + d.y * d.y + d.z * d.z);
}
{
return (a.x * b.x + a.y * b.y + a.z * b.z);
}
{
const float dotprod =
dot(d, d);
if (dotprod > 0.f)
{
const float inv_len = 1.0f / sqrtf(dotprod);
return mi::Float32_3(d.x * inv_len, d.y * inv_len, d.z * inv_len);
}
else
{
return d;
}
}
{
}
{
}
{
}
{
}
{
const float inv_s = 1.0f / s;
}
struct Options
{
bool no_gui;
size_t iterations;
std::string outputfile;
bool output_auxiliary;
unsigned res_x, res_y;
int max_ray_length;
std::string env_map;
float env_scale;
float cam_fov;
bool use_class_compilation;
bool use_custom_tex_runtime;
bool use_adapt_normal;
bool enable_derivatives;
std::string material_name;
Options()
: no_gui(false)
, iterations(100)
, outputfile("example_df_native.png")
, output_auxiliary(false)
, res_x(700)
, res_y(520)
, max_ray_length(6)
, env_map("nvidia/sdk_examples/resources/environment.hdr")
, env_scale(1.f)
, cam_pos(0.f, 0.f, 3.f)
, cam_fov(86.f)
, light_pos(10.f, 5.f, 0.f)
, light_intensity(1.0f, 0.902f, 0.502f)
, use_class_compilation(false)
, use_custom_tex_runtime(false)
, use_adapt_normal(false)
, enable_derivatives(false)
{}
};
enum VP_channel
{
VPCH_ILLUM = 0,
VPCH_ALBEDO,
VPCH_NORMAL,
VPCH_NB_CHANNELS
};
struct VP_buffers
{
VP_buffers()
: accum_buffer(nullptr)
, albedo_buffer(nullptr)
, normal_buffer(nullptr)
, aux_count(nullptr)
{}
~VP_buffers()
{
if (accum_buffer)
delete[] accum_buffer;
if (albedo_buffer)
delete[] albedo_buffer;
if (normal_buffer)
delete[] normal_buffer;
if (aux_count)
delete[] aux_count;
}
};
struct Isect_info
{
};
struct Render_context
{
int max_ray_length;
bool render_auxiliary;
bool use_derivatives;
struct Environment
{
float intensity;
struct Alias_map
{
unsigned int alias;
float q;
} *alias_map;
float inv_integral;
const float *map_pixels;
}env;
struct Camera
{
float focal;
float aspect;
float zoom;
} cam;
struct Omni
{
float distance;
float intensity;
} omni_light;
struct Sphere
{
float radius;
} sphere;
struct Cutout
{
bool is_constant;
float constant_opacity;
} cutout;
struct Thin_walled
{
bool is_constant;
bool is_thin_walled;
} thin_walled;
struct Ray
{
int level;
float last_pdf;
bool is_inside;
bool is_inside_cutout;
Ray()
: weight(1.f)
, level(0)
, last_pdf(-1.f)
, is_inside(false)
, is_inside_cutout(false)
{};
{
const float origin = 1.0f / 32.0f;
const float float_scale = 1.0f / 65536.0f;
const float int_scale = 256.0f;
static_cast<int>(int_scale * n.x),
static_cast<int>(int_scale * n.y),
static_cast<int>(int_scale * n.z));
int_as_float(float_as_int(p0.x) + ((p0.x < 0.0f) ? -of_i.x : of_i.x)),
int_as_float(float_as_int(p0.y) + ((p0.y < 0.0f) ? -of_i.y : of_i.y)),
int_as_float(float_as_int(p0.z) + ((p0.z < 0.0f) ? -of_i.z : of_i.z)));
p0.x =
abs(p0.x) < origin ? p0.x + float_scale * n.x : p_i.x;
p0.y =
abs(p0.y) < origin ? p0.y + float_scale * n.y : p_i.y;
p0.z =
abs(p0.z) < origin ? p0.z + float_scale * n.z : p_i.z;
}
};
Texture_handler *tex_handler;
Texture_handler_deriv *tex_handler_deriv;
mi::Size surface_emission_intensity_function_index;
mi::Size backface_emission_intensity_function_index;
Render_context(bool use_derivatives)
: use_derivatives(use_derivatives)
, target_code(nullptr)
, tex_handler(nullptr)
, tex_handler_deriv(nullptr)
{
max_ray_length = 6;
render_auxiliary = false;
env.intensity = 1.0f;
env.alias_map = nullptr;
omni_light.distance = 11.18f;
omni_light.intensity = 0.0f;
sphere.radius = 1.f;
cutout.is_constant = false;
cutout.constant_opacity = 1.f;
thin_walled.is_constant = true;
thin_walled.is_thin_walled = false;
shading_state.
tangent_u = Constants.tangent_u;
shading_state.
tangent_v = Constants.tangent_v;
shading_state_derivs.
tangent_u = Constants.tangent_u;
shading_state_derivs.
tangent_v = Constants.tangent_v;
}
void uninit()
{
if (env.alias_map) {
free(env.alias_map);
env.alias_map = nullptr;
}
target_code = nullptr;
}
inline void update_light(
float phi,
float theta,
float intensity)
{
omni_light.dir.x = sinf(theta) * sinf(phi);
omni_light.dir.y = cosf(theta);
omni_light.dir.z = sinf(theta) * cosf(phi);
omni_light.intensity = intensity;
}
inline void update_camera(
float phi,
float theta,
float base_dist,
int zoom)
{
cam.dir.x = -sinf(phi) * sinf(theta);
cam.dir.y = -cosf(theta);
cam.dir.z = -cosf(phi) * sinf(theta);
cam.right.x = cosf(phi);
cam.right.y = 0.0f;
cam.right.z = -sinf(phi);
cam.up.x = -sinf(phi) * cosf(theta);
cam.up.y = sinf(theta);
cam.up.z = -cosf(phi) * cosf(theta);
const float dist = base_dist * powf(0.95f, static_cast<float>(zoom));
cam.pos.x = -cam.dir.x * dist;
cam.pos.y = -cam.dir.y * dist;
cam.pos.z = -cam.dir.z * dist;
}
inline bool isect(const Ray &ray, const Sphere &sphere, Isect_info& isect_info)
{
float b = 2.f *
dot(oc, ray.dir);
float c =
dot(oc, oc) - sphere.radius * sphere.radius;
float disc = b * b - 4.f * c;
if (disc <= 0.f)
return false;
disc = sqrtf(disc);
float t = (-b - disc) * 0.5f;
if (t <= 0.f)
{
t = (-b + disc) * 0.5f;
if (t <= 0.f)
return false;
}
isect_info.pos = ray.p0 + ray.dir*t;
isect_info.normal = normalize(isect_info.pos - sphere.center);
const float phi = atan2f(isect_info.normal.x, isect_info.normal.z);
const float theta = acosf(isect_info.normal.y);
isect_info.uvw.x = phi / Constants.PI + 1.f;
isect_info.uvw.y = 1.f - theta / Constants.PI;
isect_info.uvw.z = 0.f;
const float pi_rad = Constants.PI*sphere.radius;
const float sp = sinf(phi);
const float cp = cosf(phi);
const float st = sinf(theta);
isect_info.tan_u.x = cp * st * pi_rad;
isect_info.tan_u.y = 0.f;
isect_info.tan_u.z = -sp * st * pi_rad;
isect_info.tan_u = normalize(isect_info.tan_u);
isect_info.tan_v.x = -sp * isect_info.normal.y * pi_rad;
isect_info.tan_v.y = st * pi_rad;
isect_info.tan_v.z = -cp * isect_info.normal.y * pi_rad;
isect_info.tan_v = normalize(isect_info.tan_v);
return true;
}
void build_alias_map()
{
env.alias_map = static_cast<Render_context::Environment::Alias_map *>(
malloc(rx * ry * sizeof(Render_context::Environment::Alias_map)));
float *importance_data = static_cast<float *>(malloc(rx * ry * sizeof(float)));
float cos_theta0 = 1.0f;
const float step_phi = 2.f * Constants.PI / static_cast<float>(rx);
const float step_theta = Constants.PI / static_cast<float>(ry);
for (unsigned int y = 0; y < ry; ++y)
{
const float theta1 = static_cast<float>(y + 1) * step_theta;
const float cos_theta1 =
std::cos(theta1);
const float area = (cos_theta0 - cos_theta1) * step_phi;
cos_theta0 = cos_theta1;
for (unsigned int x = 0; x < rx; ++x)
{
const unsigned int idx = y * rx + x;
const unsigned int idx4 = idx * 4;
importance_data[idx] =
area * std::max(env.map_pixels[idx4], std::max(env.map_pixels[idx4 + 1], env.map_pixels[idx4 + 2]));
}
}
size_t size = rx * ry;
float sum = 0.0f;
for (unsigned int i = 0; i < size; ++i)
sum += importance_data[i];
for (unsigned int i = 0; i < size; ++i)
env.alias_map[i].q = (static_cast<float>(size) * importance_data[i] / sum);
unsigned int *partition_table = static_cast<unsigned int *>(
malloc(size * sizeof(unsigned int)));
unsigned int s = 0u, large = size;
for (unsigned int i = 0; i < size; ++i)
partition_table[(env.alias_map[i].q < 1.0f) ? (s++) : (--large)] = env.alias_map[i].alias = i;
for (s = 0; s < large && large < size; ++s)
{
const unsigned int j = partition_table[s], k = partition_table[large];
env.alias_map[j].alias = k;
env.alias_map[k].q += env.alias_map[j].q - 1.0f;
large = (env.alias_map[k].q < 1.0f) ? (large + 1u) : large;
}
free(partition_table);
env.inv_integral = 1.0f / sum;
free(importance_data);
}
{
if (env.map.is_valid_interface())
{
const float u = atan2f(dir.z, dir.x) * (0.5f / Constants.PI) + 0.5f;
const float v = acosf(fmax(fminf(-dir.y, 1.0f), -1.0f)) / Constants.PI;
size_t x = mi::math::min(
static_cast<mi::Uint32>(u * env.map_size.x), env.map_size.x - 1u);
size_t y = mi::math::min(
static_cast<mi::Uint32>(v * env.map_size.y), env.map_size.y - 1u);
const float *pixel = env.map_pixels + ((y*env.map_size.x + x) * 4);
pdf = std::max(pixel[0], std::max(pixel[1], pixel[2])) * env.inv_integral;
return mi::Float32_3(pixel[0], pixel[1], pixel[2])*env.intensity;
}
else
{
pdf = 1.f;
return env.color*env.intensity;
}
}
{
xi.x = rnd(seed);
xi.y = rnd(seed);
xi.z = rnd(seed);
const unsigned int size = env.map_size.x * env.map_size.y;
const unsigned int idx =
mi::math::min(static_cast<unsigned>(xi.x * static_cast<float>(size)), size - 1);
unsigned int env_idx;
float xi_y = xi.y;
if (xi_y < env.alias_map[idx].q)
{
env_idx = idx;
xi_y /= env.alias_map[idx].q;
}
else
{
env_idx = env.alias_map[idx].alias;
xi_y = (xi_y - env.alias_map[idx].q) / (1.0f - env.alias_map[idx].q);
}
const unsigned int py = env_idx / env.map_size.x;
const unsigned int px = env_idx % env.map_size.x;
const float u = static_cast<float>(px + xi_y) / static_cast<float>(env.map_size.x);
const float phi = u * 2.0f * Constants.PI - Constants.PI;
const float sin_phi = sinf(phi);
const float cos_phi = cosf(phi);
const float step_theta = Constants.PI / static_cast<float>(env.map_size.y);
const float theta0 = static_cast<float>(py)* step_theta;
const float cos_theta = cosf(theta0) * (1.0f - xi.z) + cosf(theta0 + step_theta) * xi.z;
const float theta = acosf(cos_theta);
const float sin_theta = sinf(theta);
light_dir =
mi::Float32_3(cos_phi * sin_theta, -cos_theta, sin_phi * sin_theta);
const float v = theta / Constants.PI;
size_t x = mi::math::min(
static_cast<mi::Uint32>(u * env.map_size.x), env.map_size.x - 1u);
size_t y = mi::math::min(
static_cast<mi::Uint32>(v * env.map_size.y), env.map_size.y - 1u);
const float *pix = env.map_pixels + ((y*env.map_size.x + x) * 4);
light_pdf = mi::math::max(pix[0], mi::math::max(pix[1], pix[2])) * env.inv_integral;
}
{
float p_select_light = 1.0f;
if (omni_light.intensity > 0.f)
{
p_select_light = env.intensity > 0.0f ? 0.5f : 1.0f;
if (rnd(seed) <= p_select_light)
{
light_pdf = Constants.DIRAC;
light_dir = omni_light.dir*omni_light.distance - pos;
const float inv_distance2 = 1.0f /
dot(light_dir, light_dir);
light_dir *= sqrtf(inv_distance2);
return omni_light.color *
(omni_light.intensity * inv_distance2 * 0.25f / (Constants.PI * p_select_light));
}
p_select_light = (1.0f - p_select_light);
}
mi::Float32_3 radiance = sample_environment(light_dir, light_pdf, seed);
light_pdf *= p_select_light;
return radiance / light_pdf;
}
};
void create_material_instance(
const char* material_name,
const char* instance_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();
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());
if (!module)
exit_failure("Failed to access the loaded module.");
std::string material_db_name
= std::string(module_db_name->get_c_str()) + "::" + material_simple_name;
material_db_name = mi::examples::mdl::add_missing_material_signature(
module.get(), material_db_name);
if (material_db_name.empty())
exit_failure("Failed to find the material %s in the module %s.",
material_simple_name.c_str(), module_name.c_str());
if (!material_definition)
exit_failure("Accessing definition '%s' failed.", material_db_name.c_str());
material_definition->create_function_call(0, &result));
if (result != 0)
exit_failure("Instantiating '%s' failed.", material_db_name.c_str());
transaction->
store(material_instance.get(), instance_name);
}
void compile_material_instance(
const char* instance_name,
const char* compiled_material_name,
bool class_compilation)
{
#ifdef ADD_EXTRA_TIMERS
std::chrono::steady_clock::time_point t1 = std::chrono::steady_clock::now();
#endif
material_instance->create_compiled_material(flags, context));
check_success(print_messages(context));
transaction->
store(compiled_material.get(), compiled_material_name);
#ifdef ADD_EXTRA_TIMERS
std::chrono::steady_clock::time_point t2 = std::chrono::steady_clock::now();
std::chrono::duration<double> et = t2 - t1;
if(class_compilation)
printf("Material Class Compilation: %f seconds.\n", et.count());
else
printf("Material Instance Compilation: %f seconds.\n", et.count());
#endif
}
void generate_native(
Render_context& render_context,
const char* compiled_material_name,
bool use_custom_tex_runtime,
bool use_adapt_normal,
bool enable_derivatives)
{
Timing timing("generate target code");
#ifdef ADD_EXTRA_TIMERS
std::chrono::steady_clock::time_point t1 = std::chrono::steady_clock::now();
#endif
#ifdef ADD_EXTRA_TIMERS
std::chrono::steady_clock::time_point t2 = std::chrono::steady_clock::now();
#endif
render_context.cutout.is_constant =
compiled_material->get_cutout_opacity(&render_context.cutout.constant_opacity);
compiled_material->lookup_sub_expression("thin_walled"));
render_context.thin_walled.is_constant = false;
render_context.thin_walled.is_thin_walled = false;
{
render_context.thin_walled.is_constant = true;
render_context.thin_walled.is_thin_walled = thin_walled_bool->get_value();
}
bool need_backface_bsdf = false;
bool need_backface_edf = false;
bool need_backface_emission_intensity = false;
if (!render_context.thin_walled.is_constant || render_context.thin_walled.is_thin_walled)
{
need_backface_bsdf =
need_backface_edf =
need_backface_emission_intensity =
compiled_material->lookup_sub_expression("backface.scattering"));
compiled_material->lookup_sub_expression("backface.emission.emission"));
{
scattering_expr_constant->get_value());
emission_expr_constant->get_value());
{
need_backface_bsdf = false;
need_backface_edf = false;
need_backface_emission_intensity = false;
}
}
}
#ifdef ADD_EXTRA_TIMERS
std::chrono::steady_clock::time_point t3 = std::chrono::steady_clock::now();
#endif
#ifdef ADD_EXTRA_TIMERS
std::chrono::steady_clock::time_point t4 = std::chrono::steady_clock::now();
#endif
check_success(be_native->set_option("num_texture_spaces", "1") == 0);
check_success(be_native->set_option("num_texture_results", "128") == 0);
if (render_context.render_auxiliary)
check_success(be_native->set_option("enable_auxiliary", "on") == 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);
if (use_adapt_normal)
check_success(be_native->set_option("use_renderer_adapt_normal", "on") == 0);
#ifdef ADD_EXTRA_TIMERS
std::chrono::steady_clock::time_point t5 = std::chrono::steady_clock::now();
#endif
be_native->create_link_unit(transaction, context));
check_success(print_messages(context));
#ifdef ADD_EXTRA_TIMERS
std::chrono::steady_clock::time_point t6 = std::chrono::steady_clock::now();
#endif
std::vector<mi::neuraylib::Target_function_description> descs;
size_t backface_scattering_index = ~0;
if (need_backface_bsdf)
{
backface_scattering_index = descs.size();
}
size_t backface_edf_index = ~0;
if (need_backface_edf)
{
backface_edf_index = descs.size();
}
size_t backface_emission_intensity_index = ~0;
if (need_backface_emission_intensity)
{
backface_emission_intensity_index = descs.size();
}
size_t cutout_opacity_desc_index = ~0;
if (!render_context.cutout.is_constant)
{
cutout_opacity_desc_index = descs.size();
}
size_t thin_walled_desc_index = ~0;
if (!render_context.thin_walled.is_constant)
{
thin_walled_desc_index = descs.size();
}
#ifdef ADD_EXTRA_TIMERS
std::chrono::steady_clock::time_point t7 = std::chrono::steady_clock::now();
#endif
link_unit->add_material(
compiled_material.get(),
descs.data(), descs.size(),
context);
check_success(print_messages(context));
#ifdef ADD_EXTRA_TIMERS
std::chrono::steady_clock::time_point t8 = std::chrono::steady_clock::now();
#endif
be_native->translate_link_unit(link_unit.get(), context));
check_success(print_messages(context));
check_success(code_native);
#ifdef ADD_EXTRA_TIMERS
std::chrono::steady_clock::time_point t9 = std::chrono::steady_clock::now();
#endif
render_context.target_code = code_native;
render_context.init_function_index = descs[0].function_index;
render_context.surface_bsdf_function_index = descs[1].function_index;
render_context.surface_edf_function_index = descs[2].function_index;
render_context.surface_emission_intensity_function_index = descs[3].function_index;
render_context.backface_bsdf_function_index = need_backface_bsdf
? descs[backface_scattering_index].function_index : descs[1].function_index;
render_context.backface_edf_function_index = need_backface_edf
? descs[backface_edf_index].function_index : descs[2].function_index;
render_context.backface_emission_intensity_function_index = need_backface_emission_intensity
? descs[backface_emission_intensity_index].function_index : descs[3].function_index;
render_context.cutout_opacity_function_index = !render_context.cutout.is_constant
? descs[cutout_opacity_desc_index].function_index : ~0;
render_context.thin_walled_function_index = !render_context.thin_walled.is_constant
? descs[thin_walled_desc_index].function_index : ~0;
#ifdef ADD_EXTRA_TIMERS
std::chrono::steady_clock::time_point t10 = std::chrono::steady_clock::now();
#endif
#ifdef ADD_EXTRA_TIMERS
std::chrono::duration<double> et = t10 - t1;
printf("GTC |||| Total time : %f seconds.\n", et.count());
et = t2 - t1;
printf("GTC | Compiled material DB : %f seconds.\n", et.count());
et = t3 - t2;
printf("GTC | Mateial properties inspection : %f seconds.\n", et.count());
et = t4 - t3;
printf("GTC | Native backend : %f seconds.\n", et.count());
et = t5 - t4;
printf("GTC | Material flags : %f seconds.\n", et.count());
et = t6 - t5;
printf("GTC | Create link unit : %f seconds.\n", et.count());
et = t7 - t6;
printf("GTC | Material expressions selection: %f seconds.\n", et.count());
et = t8 - t7;
printf("GTC | Add material to link unit : %f seconds.\n", et.count());
et = t9 - t8;
printf("GTC | Translate link unit : %f seconds.\n", et.count());
et = t10 - t9;
printf("GTC | RC update : %f seconds.\n", et.count());
#endif
}
bool prepare_textures(
std::vector<Texture>& textures,
{
{
char const* image_type = image->get_type(0, 0);
if (image->is_uvtile() || image->is_animated()) {
std::cerr << "The example does not support uvtile and/or animated textures!" << std::endl;
return false;
}
if (texture->get_effective_gamma(0, 0) != 1.0f) {
image_api->
convert(canvas.get(),
"Color"));
gamma_canvas->set_gamma(texture->get_effective_gamma(0, 0));
canvas = gamma_canvas;
}
else if (strcmp(image_type, "Color") != 0 && strcmp(image_type, "Float32<4>") != 0) {
canvas = image_api->
convert(canvas.get(),
"Color");
}
textures.push_back(Texture(canvas));
}
return true;
}
bool trace_shadow(Render_context& rc, Render_context::Ray& shadow_ray, unsigned &seed)
{
Isect_info isect_info;
if (rc.isect(shadow_ray, rc.sphere, isect_info))
{
shading_state.
position = isect_info.pos;
shading_state.
normal = shadow_ray.is_inside ? -isect_info.normal : isect_info.normal;
float cutout_opacity = rc.cutout.constant_opacity;
if (!rc.cutout.is_constant)
{
rc.cutout_opacity_function_index,
shading_state,
rc.tex_handler,
nullptr,
&cutout_opacity);
assert(ret_code == 0 && "execute opacity function failed");
(void) ret_code;
}
return (cutout_opacity >= rnd(seed));
}
else
{
return false;
}
}
bool trace_ray(
mi::Float32_3 vp_sample[3], Render_context &rc, Render_context::Ray &ray,
unsigned &seed)
{
if (ray.level >= rc.max_ray_length)
return false;
ray.level++;
Isect_info isect_info;
if (rc.isect(ray, rc.sphere, isect_info))
{
if (rc.use_derivatives) {
isect_info.pos,
{ 0.0f, 0.0f, 0.0f },
{ 0.0f, 0.0f, 0.0f }
};
{
isect_info.uvw,
{ 0.0f, 0.0f, 0.0f },
{ 0.0f, 0.0f, 0.0f }
}
};
rc.shading_state_derivs.position = position;
rc.shading_state_derivs.normal = ray.is_inside ? -isect_info.normal : isect_info.normal;
rc.shading_state_derivs.geom_normal = rc.shading_state_derivs.normal;
rc.shading_state_derivs.text_coords = texture_coords;
rc.shading_state_derivs.tangent_u = &isect_info.tan_u;
rc.shading_state_derivs.tangent_v = &isect_info.tan_v;
rc.shading_state_derivs.text_results = text_results;
shading_state =
tex_handler =
} else {
rc.shading_state.position = isect_info.pos;
rc.shading_state.normal = ray.is_inside ? -isect_info.normal : isect_info.normal;
rc.shading_state.geom_normal = rc.shading_state.normal;
rc.shading_state.text_coords = &isect_info.uvw;
rc.shading_state.tangent_u = &isect_info.tan_u;
rc.shading_state.tangent_v = &isect_info.tan_v;
rc.shading_state.text_results = text_results;
shading_state = &rc.shading_state;
tex_handler = rc.tex_handler;
}
rc.use_derivatives ? rc.shading_state_derivs.normal : rc.shading_state.normal;
rc.use_derivatives ? rc.shading_state_derivs.geom_normal : rc.shading_state.geom_normal;
ret_code = rc.target_code->execute_init(
rc.init_function_index,
*shading_state,
tex_handler,
nullptr);
assert(ret_code == 0 && "execute_bsdf_init failed");
float cutout_opacity = rc.cutout.constant_opacity;
if (!rc.cutout.is_constant)
{
ret_code = rc.target_code->execute(
rc.cutout_opacity_function_index,
*shading_state,
tex_handler,
nullptr,
&cutout_opacity);
assert(ret_code == 0 && "execute opacity function failed");
}
if (cutout_opacity < rnd(seed))
{
ray.p0 = isect_info.pos;
ray.offset_ray(ray.is_inside_cutout ? isect_info.normal : -isect_info.normal);
ray.is_inside = !ray.is_inside;
ray.is_inside_cutout = !ray.is_inside_cutout;
ray.level--;
return trace_ray(vp_sample, rc, ray, seed);
}
else
{
bool is_thin_walled = rc.thin_walled.is_thin_walled;
if (!rc.thin_walled.is_constant)
{
ret_code = rc.target_code->execute(
rc.thin_walled_function_index,
*shading_state,
tex_handler,
nullptr,
&is_thin_walled);
assert(ret_code == 0 && "execute thin_walled function failed");
}
{
uint64_t edf_function_index = (is_thin_walled && ray.is_inside) ?
rc.backface_edf_function_index : rc.surface_edf_function_index;
mi::neuraylib::Edf_evaluate_data<mi::neuraylib::DF_HSM_NONE> eval_data;
eval_data.k1 = -ray.dir;
ret_code = rc.target_code->execute_edf_evaluate(
edf_function_index + 1,
&eval_data,
*shading_state,
tex_handler,
nullptr);
assert(ret_code == 0 && "execute_edf_evaluate failed");
if (eval_data.pdf > 1.e-6f)
{
uint64_t emission_intensity_function_index = (is_thin_walled && ray.is_inside)
? rc.backface_emission_intensity_function_index
: rc.surface_emission_intensity_function_index;
ret_code = rc.target_code->execute(
emission_intensity_function_index,
*shading_state,
tex_handler,
nullptr,
&intensity);
assert(ret_code == 0 && "execute emission intensity function failed");
vp_sample
[VPCH_ILLUM] +=
static_cast<mi::Float32_3>(eval_data.edf)*intensity*ray.weight;
}
}
uint64_t surface_bsdf_function_index = (is_thin_walled && ray.is_inside)
? rc.backface_bsdf_function_index : rc.surface_bsdf_function_index;
if (rc.render_auxiliary && ray.level == 1)
{
mi::neuraylib::Bsdf_auxiliary_data<mi::neuraylib::DF_HSM_NONE> aux_data;
if (ray.is_inside && !is_thin_walled)
{
}
else
{
}
aux_data.k1 = -ray.dir;
ret_code = rc.target_code->execute_bsdf_auxiliary(
surface_bsdf_function_index + 3,
&aux_data,
*shading_state,
tex_handler,
nullptr);
assert(ret_code == 0 && "execute_bsdf_auxiliary failed");
vp_sample[VPCH_ALBEDO] = aux_data.albedo_diffuse + aux_data.albedo_glossy;
vp_sample[VPCH_NORMAL] = aux_data.normal;
}
{
float light_pdf = 0.f;
mi::Float32_3 radiance_over_pdf = rc.sample_lights(isect_info.pos, light_dir, light_pdf, seed);
bool light_culled = !(
(ray.level < rc.max_ray_length) &&
(light_pdf != 0.0f) &&
((
dot(normal, light_dir) > 0.f) != (ray.is_inside && !ray.is_inside_cutout)) );
if (!light_culled && ray.is_inside_cutout)
{
Render_context::Ray shadow_ray = ray;
shadow_ray.p0 = isect_info.pos;
shadow_ray.offset_ray(-isect_info.normal);
shadow_ray.dir = normalize(light_dir);
light_culled = trace_shadow(rc, shadow_ray, seed);
}
if (!light_culled)
{
mi::neuraylib::Bsdf_evaluate_data<mi::neuraylib::DF_HSM_NONE> eval_data;
if (ray.is_inside && !is_thin_walled)
{
}
else
{
}
eval_data.k1 = -ray.dir;
eval_data.k2 = light_dir;
ret_code = rc.target_code->execute_bsdf_evaluate(
surface_bsdf_function_index + 1,
&eval_data,
*shading_state,
tex_handler,
nullptr);
assert(ret_code == 0 && "execute_bsdf_evaluate failed");
if (eval_data.pdf > 1.e-6f)
{
const float mis_weight = (light_pdf == Constants.DIRAC)
? 1.0f : light_pdf / (light_pdf + eval_data.pdf);
vp_sample[VPCH_ILLUM] += (eval_data.bsdf_diffuse + eval_data.bsdf_glossy)*(radiance_over_pdf * ray.weight)* mis_weight;
}
}
}
{
if (ray.is_inside && !is_thin_walled)
{
}
else
{
}
sample_data.
k1 = -ray.dir;
sample_data.
xi.x = rnd(seed);
sample_data.
xi.y = rnd(seed);
sample_data.
xi.z = rnd(seed);
sample_data.
xi.w = rnd(seed);
ret_code = rc.target_code->execute_bsdf_sample(
surface_bsdf_function_index,
&sample_data,
*shading_state,
tex_handler,
nullptr);
assert(ret_code == 0 && "execute_bsdf_sample failed");
if (sample_data.
event_type != mi::neuraylib:
:BSDF_EVENT_ABSORB)
{
if ((sample_data.
event_type & mi::neuraylib:
:BSDF_EVENT_SPECULAR) != 0)
ray.last_pdf = -1.0f;
else
ray.last_pdf = sample_data.
pdf;
ray.p0 = isect_info.pos;
ray.dir = normalize(sample_data.
k2);
if (sample_data.
event_type & mi::neuraylib:
:BSDF_EVENT_TRANSMISSION)
{
ray.is_inside = !ray.is_inside;
}
else
{
ray.offset_ray(geom_normal);
}
trace_ray(scat_color, rc, ray, seed);
vp_sample[VPCH_ILLUM] += scat_color[VPCH_ILLUM];
}
}
return true;
}
}
else
{
float pdf = 1.f;
vp_sample[VPCH_ILLUM] = rc.evaluate_environment(pdf, ray.dir)*ray.weight;
if (ray.level > 1 && ray.last_pdf > 0.f)
{
if (rc.omni_light.intensity > 0.f)
pdf *= 0.5f;
vp_sample[VPCH_ILLUM] *= ray.last_pdf / (ray.last_pdf + pdf);
}
return false;
}
}
void render_scene(
Render_context rc,
size_t frame_nb,
VP_buffers *vp_buffers,
unsigned char* dst,
size_t ymin,
size_t ymax,
size_t width,
size_t height,
unsigned char channels)
{
if (ymax > height)
ymax = height;
Render_context::Ray ray;
size_t pixel_offset = ymin * width * channels;
size_t vp_idx = ymin * width;
for (size_t y = ymin; y < ymax; ++y)
{
unsigned seed = tea(16, y*width, frame_nb);
for (size_t x = 0; x < width; ++x, ++vp_idx)
{
float x_rnd = rnd(seed);
float y_rnd = rnd(seed);
(x + x_rnd)*rc.cam.inv_res.x,
(y + y_rnd)*rc.cam.inv_res.y);
float r = (2.0f * screen_pos.x - 1.0f);
float u = (2.0f * screen_pos.y - 1.0f);
ray.p0 = rc.cam.pos;
ray.dir = normalize(rc.cam.dir * rc.cam.focal +
rc.cam.right * r + rc.cam.up * (rc.cam.aspect * u));
ray.weight = 1.f;
ray.is_inside = false;
ray.is_inside_cutout = false;
ray.level = 0;
ray.last_pdf = -1.f;
bool ray_hit = trace_ray(vp_sample, rc, ray, seed);
if (frame_nb == 1)
{
vp_buffers->accum_buffer[vp_idx] = vp_sample[VPCH_ILLUM];
if(rc.render_auxiliary)
{
vp_buffers->aux_count[vp_idx] = 0;
if (ray_hit)
{
vp_buffers->aux_count[vp_idx]++;
vp_buffers->albedo_buffer[vp_idx] = vp_sample[VPCH_ALBEDO];
vp_buffers->normal_buffer[vp_idx] = vp_sample[VPCH_NORMAL];
}
else
{
}
}
}
else
{
vp_buffers->accum_buffer[vp_idx] =
(vp_buffers->accum_buffer[vp_idx] * static_cast<float>(frame_nb - 1) + vp_sample[VPCH_ILLUM]) * (1.f / frame_nb);
vp_sample[VPCH_ILLUM] = vp_buffers->accum_buffer[vp_idx];
if (ray_hit && rc.render_auxiliary)
{
vp_buffers->aux_count[vp_idx]++;
vp_buffers->albedo_buffer[vp_idx] =
(vp_buffers->albedo_buffer[vp_idx] * static_cast<float>(vp_buffers->aux_count[vp_idx] - 1) + vp_sample[VPCH_ALBEDO]) * (1.f / vp_buffers->aux_count[vp_idx]);
vp_sample[VPCH_ALBEDO] = vp_buffers->albedo_buffer[vp_idx];
vp_buffers->normal_buffer[vp_idx] =
(vp_buffers->normal_buffer[vp_idx] * static_cast<float>(vp_buffers->aux_count[vp_idx] - 1) + vp_sample[VPCH_NORMAL]) * (1.f / vp_buffers->aux_count[vp_idx]);
vp_sample[VPCH_NORMAL] = normalize(vp_buffers->normal_buffer[vp_idx]);
}
}
if (dst)
{
vp_sample[VPCH_ILLUM].x = powf(vp_sample[VPCH_ILLUM].x, 1.f / 2.2f);
vp_sample[VPCH_ILLUM].y = powf(vp_sample[VPCH_ILLUM].y, 1.f / 2.2f);
vp_sample[VPCH_ILLUM].z = powf(vp_sample[VPCH_ILLUM].z, 1.f / 2.2f);
vp_sample[VPCH_ILLUM] *= 255.f;
clamp(vp_sample
[VPCH_ILLUM], 0.f, 255.f);
dst[pixel_offset++] = static_cast<unsigned char>(vp_sample[VPCH_ILLUM].x);
dst[pixel_offset++] = static_cast<unsigned char>(vp_sample[VPCH_ILLUM].y);
dst[pixel_offset++] = static_cast<unsigned char>(vp_sample[VPCH_ILLUM].z);
dst[pixel_offset++] = 255u;
}
}
}
}
static void save_screenshot(
const unsigned int width,
const unsigned int height,
const std::string &filename,
{
image_api->create_canvas("Float32<3>", width, height));
memcpy(tile->get_data(), image_buffer, width*height *
sizeof(
mi::Float32_3));
mdl_impexp_api->export_canvas(filename.c_str(), canvas.get(), 100U, true);
}
void usage(char const *prog_name)
{
std::cout
<< "Usage: " << prog_name << " [options] [<material_name>]\n"
<< "Options:\n"
<< " -h|--help print this text and exit\n"
<< " -v|--version print the MDL SDK version string and exit\n"
<< " --res <x> <y> resolution (default: 700x520)\n"
<< " --hdr <filename> environment map\n"
<< " (default: nvidia/sdk_examples/resources/environment.hdr)\n"
<< " --cc use class compilation\n"
<< " --cr use custom texture runtime\n"
<< " --an use adapt normal function\n"
<< " -d enable use of derivatives\n"
<< " --nogui don't open interactive display\n"
<< " --spp samples per pixel (default: 100) for output image when nogui\n"
<< " -o <outputfile> image file to write result to\n"
<< " (default: example_native.png)\n"
<< " -oaux output albedo and normal auxiliary buffers.\n"
<< " -p|--mdl_path <path> mdl search path, can occur multiple times\n"
<< "\n"
<< "Viewport controls:\n"
<< " Mouse Camera rotation, zoom\n"
<< " Arrow keys, (+/-) Omni-light rotation, intensity\n"
<< " CTRL + (+/-) Environment intensity\n"
<< " ENTER Screenshot\n"
<< std::endl;
exit_failure();
}
int MAIN_UTF8(int argc, char *argv[])
{
Options options;
mi::examples::mdl::Configure_options configure_options;
configure_options.add_example_search_path = false;
bool print_version_and_exit = false;
for (int i = 1; i < argc; ++i)
{
char const *opt = argv[i];
if (opt[0] == '-')
{
if (strcmp(opt, "--nogui") == 0)
{
options.no_gui = true;
}
else if (strcmp(opt, "--spp") == 0 && i < argc - 1)
{
options.iterations = std::max(atoi(argv[++i]), 1);
}
else if (strcmp(opt, "-o") == 0 && i < argc - 1)
{
options.outputfile = argv[++i];
}
else if (strcmp(opt, "-oaux") == 0)
{
options.output_auxiliary = true;
}
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, "--max_path_length") == 0 && i < argc - 1)
{
options.max_ray_length = std::max(atoi(argv[++i]), 0);
}
else if (strcmp(opt, "--hdr") == 0 && i < argc - 1)
{
options.env_map = argv[++i];
}
else if (strcmp(opt, "--hdr_scale") == 0 && i < argc - 1)
{
options.env_scale = static_cast<float>(atof(argv[++i]));
}
else if (strcmp(opt, "-f") == 0 && i < argc - 1)
{
options.cam_fov = static_cast<float>(atof(argv[++i]));
}
else if (strcmp(opt, "--pos") == 0 && i < argc - 3)
{
options.cam_pos.x = static_cast<float>(atof(argv[++i]));
options.cam_pos.y = static_cast<float>(atof(argv[++i]));
options.cam_pos.z = static_cast<float>(atof(argv[++i]));
}
else if (strcmp(opt, "-l") == 0 && i < argc - 6)
{
options.light_pos.x = static_cast<float>(atof(argv[++i]));
options.light_pos.y = static_cast<float>(atof(argv[++i]));
options.light_pos.z = static_cast<float>(atof(argv[++i]));
options.light_intensity.x = static_cast<float>(atof(argv[++i]));
options.light_intensity.y = static_cast<float>(atof(argv[++i]));
options.light_intensity.z = static_cast<float>(atof(argv[++i]));
}
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, "--an") == 0)
{
options.use_adapt_normal = true;
}
else if (strcmp(opt, "-d") == 0)
{
options.enable_derivatives = true;
}
else if ((strcmp(opt, "--mdl_path") == 0 || strcmp(opt, "-p") == 0) &&
i < argc - 1)
{
configure_options.additional_mdl_paths.push_back(argv[++i]);
}
else if (strcmp(opt, "-v") == 0 || strcmp(opt, "--version") == 0)
{
print_version_and_exit = true;
}
else
{
if (strcmp(opt, "-h") != 0 && strcmp(opt, "--help") != 0)
std::cout << "Unknown option: \"" << opt << "\"" << std::endl;
usage(argv[0]);
}
}
else
{
options.material_name = opt;
}
}
Render_context rc(options.enable_derivatives);
configure_options.add_example_search_path = true;
if (options.material_name.empty())
options.material_name = "::nvidia::sdk_examples::tutorials::example_df";
if (!neuray.is_valid_interface())
exit_failure("Failed to load the SDK.");
if (print_version_and_exit)
{
fprintf(stdout, "%s\n", version->get_string());
version = nullptr;
neuray = nullptr;
if (!mi::examples::mdl::unload())
exit_failure("Failed to unload the SDK.");
exit_success();
}
if (!mi::examples::mdl::configure(neuray.get(), configure_options))
exit_failure("Failed to initialize the SDK.");
if (ret != 0)
exit_failure("Failed to initialize the SDK. Result code: %d", ret);
rc.render_auxiliary = options.output_auxiliary;
{
{
std::string instance_name = "material instance";
create_material_instance(
mdl_factory.get(),
transaction.get(),
context.get(),
options.material_name.c_str(),
instance_name.c_str());
std::string instance_compilation_name
= std::string("instance compilation of ") + instance_name;
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_native(
rc,
transaction.get(),
mdl_backend_api.get(),
context.get(),
compilation_name.c_str(),
options.use_custom_tex_runtime,
options.use_adapt_normal,
options.enable_derivatives);
}
std::vector<Texture> textures;
Texture_handler tex_handler = { 0, 0, 0 };
Texture_handler_deriv tex_handler_deriv = { 0, 0, 0 };
if (options.enable_derivatives)
{
if (options.use_custom_tex_runtime)
{
check_success(prepare_textures(
textures, transaction.get(), image_api.
get(), rc.target_code.get()));
tex_handler_deriv.vtable = &tex_deriv_vtable;
tex_handler_deriv.num_textures = rc.target_code->get_texture_count() - 1;
tex_handler_deriv.textures = textures.data();
rc.tex_handler_deriv = &tex_handler_deriv;
}
else if (options.use_adapt_normal)
{
tex_only_adapt_normal_vtable_deriv.m_adapt_normal = adapt_normal;
tex_handler_deriv.vtable = &tex_only_adapt_normal_vtable_deriv;
rc.tex_handler_deriv = &tex_handler_deriv;
}
}
else
{
if (options.use_custom_tex_runtime)
{
check_success(prepare_textures(
textures, transaction.get(), image_api.
get(), rc.target_code.get()));
tex_handler.vtable = &tex_vtable;
tex_handler.num_textures = rc.target_code->get_texture_count() - 1;
tex_handler.textures = textures.data();
rc.tex_handler = &tex_handler;
}
else if (options.use_adapt_normal)
{
tex_only_adapt_normal_vtable.m_adapt_normal = adapt_normal;
tex_handler.vtable = &tex_only_adapt_normal_vtable;
rc.tex_handler = &tex_handler;
}
}
Window_context window_context;
size_t window_width = 0, window_height = 0;
size_t frame_nb = 0;
VP_buffers vp_buffers;
std::string filename_base, filename_ext;
size_t dot_pos = options.outputfile.rfind('.');
if (dot_pos == std::string::npos)
{
filename_base = options.outputfile;
}
else
{
filename_base = options.outputfile.substr(0, dot_pos);
filename_ext = options.outputfile.substr(dot_pos);
}
#ifdef USE_PARALLEL_RENDERING
#ifdef MI_PLATFORM_WINDOWS
SYSTEM_INFO sysinfo;
GetSystemInfo(&sysinfo);
const int num_threads = sysinfo.dwNumberOfProcessors;
#elif MI_PLATFORM_MACOSX
int num_threads;
size_t len = sizeof(num_threads);
sysctlbyname("hw.logicalcpu", &num_threads, &len, NULL, 0);
#else
const int num_threads = sysconf(_SC_NPROCESSORS_ONLN);
#endif
std::cout << "Rendering on " << num_threads << " threads.\n";
#endif
rc.max_ray_length = options.max_ray_length;
window_context.env_intensity = rc.env.intensity = options.env_scale;
check_success(image->reset_file(options.env_map.c_str()) == 0);
rc.env.map = image->get_canvas(0, 0, 0);
rc.env.map_size.x = rc.env.map->get_resolution_x();
rc.env.map_size.y = rc.env.map->get_resolution_y();
char const *image_type = image->get_type(0, 0);
if (strcmp(image_type, "Color") != 0 && strcmp(image_type, "Float32<4>") != 0)
rc.env.map = image_api->convert(rc.env.map.get(), "Color");
rc.env.map_pixels = reinterpret_cast<const float*>(
rc.build_alias_map();
rc.omni_light.intensity = std::max(std::max(options.light_intensity.x, options.light_intensity.y), options.light_intensity.y);
if (rc.omni_light.intensity > 0.f)
rc.omni_light.color = options.light_intensity / rc.omni_light.intensity;
else
rc.omni_light.color = options.light_intensity;
rc.omni_light.distance =
length(options.light_pos);
rc.omni_light.dir = normalize(options.light_pos);
window_context.omni_phi = atan2f(rc.omni_light.dir.x, rc.omni_light.dir.z);
window_context.omni_theta = acosf(rc.omni_light.dir.y);
window_context.omni_intensity = rc.omni_light.intensity;
rc.update_light(window_context.omni_phi, window_context.omni_theta, window_context.omni_intensity);
float base_dist =
length(options.cam_pos);
float theta, phi;
phi = atan2f(inv_dir.x, inv_dir.z);
theta = acosf(inv_dir.y);
rc.cam.focal = 1.0f / tanf(options.cam_fov * Constants.PI / 360.f);
rc.update_camera(phi, theta, base_dist, window_context.zoom);
if (options.no_gui)
{
window_width = options.res_x;
window_height = options.res_y;
frame_nb = 0;
if (vp_buffers.accum_buffer)
delete[] vp_buffers.accum_buffer;
vp_buffers.accum_buffer =
new mi::Float32_3[window_width*window_height];
if(options.output_auxiliary)
{
if (vp_buffers.albedo_buffer)
delete[] vp_buffers.albedo_buffer;
if (vp_buffers.normal_buffer)
delete[] vp_buffers.normal_buffer;
if (vp_buffers.aux_count)
delete[] vp_buffers.aux_count;
vp_buffers.albedo_buffer =
new mi::Float32_3[window_width*window_height];
vp_buffers.normal_buffer =
new mi::Float32_3[window_width*window_height];
vp_buffers.aux_count =
new mi::Uint32[window_width * window_height];
}
rc.cam.inv_res.x = 1.0f / static_cast<float>(window_width);
rc.cam.inv_res.y = 1.0f / static_cast<float>(window_height);
rc.cam.aspect = static_cast<float>(window_height)
/ static_cast<float>(window_width);
{
Timing timing("rendering");
while (frame_nb < options.iterations)
{
frame_nb++;
#ifdef USE_PARALLEL_RENDERING
std::vector<std::thread> threads;
size_t lpt = window_height / num_threads +
(window_height % num_threads != 0 ? 1 : 0);
for (int i = 0; i < num_threads; ++i)
threads.push_back(std::thread(render_scene, rc, frame_nb, &vp_buffers,
nullptr, lpt * i, lpt * (i + 1), window_width, window_height, 4));
for (int i = 0; i < num_threads; ++i)
threads[i].join();
threads.clear();
#else
render_scene(rc, frame_nb, &vp_buffers,
nullptr, 0, window_height, window_width, window_height, 4);
#endif
}
}
save_screenshot(vp_buffers.accum_buffer, window_width, window_height,
filename_base + filename_ext, image_api, mdl_impexp_api);
if (options.output_auxiliary)
{
save_screenshot(vp_buffers.albedo_buffer, window_width, window_height,
filename_base + "_albedo" + filename_ext, image_api, mdl_impexp_api);
save_screenshot(vp_buffers.normal_buffer, window_width, window_height,
filename_base + "_normal" + filename_ext, image_api, mdl_impexp_api);
}
}
else
{
if (!glfwInit())
exit(EXIT_FAILURE);
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
mi::examples::mdl::GL_window::Description window_desc;
window_desc.width = options.res_x;
window_desc.height = options.res_y;
window_desc.title = "MDL Native Rendering";
window_desc.no_gui = false;
mi::examples::mdl::GL_window gl_window(window_desc);
gl_window.set_window_user_pointer(&window_context);
gl_window.set_key_callback(Window_context::handle_key);
gl_window.set_mouse_button_callback(Window_context::handle_mouse_button);
gl_window.set_cursor_pos_callback(Window_context::handle_mouse_pos);
gl_window.set_scroll_callback(Window_context::handle_scroll);
if (GLEW_OK != glewInit())
exit(EXIT_FAILURE);
glfwSwapInterval(1);
mi::examples::mdl::GL_display gl_display(window_desc.width, window_desc.height);
mi::examples::gui::Root* gui = window_desc.no_gui ? nullptr : gl_window.get_gui();
if (gui)
{
gui->initialize();
}
while (gl_window.update())
{
if (window_width != gl_window.get_width() || window_height != gl_window.get_height())
{
window_width = gl_window.get_width();
window_height = gl_window.get_height();
frame_nb = 0;
if (vp_buffers.accum_buffer)
delete[] vp_buffers.accum_buffer;
vp_buffers.accum_buffer =
new mi::Float32_3[window_width*window_height];
if (options.output_auxiliary)
{
if (vp_buffers.albedo_buffer)
delete[] vp_buffers.albedo_buffer;
if (vp_buffers.normal_buffer)
delete[] vp_buffers.normal_buffer;
if (vp_buffers.aux_count)
delete[] vp_buffers.aux_count;
vp_buffers.albedo_buffer =
new mi::Float32_3[window_width*window_height];
vp_buffers.normal_buffer =
new mi::Float32_3[window_width*window_height];
vp_buffers.aux_count =
new mi::Uint32[window_width * window_height];
}
rc.cam.inv_res.x = 1.0f / static_cast<float>(window_width);
rc.cam.inv_res.y = 1.0f / static_cast<float>(window_height);
rc.cam.aspect = static_cast<float>(window_height)
/ static_cast<float>(window_width);
}
if (window_context.key_event && !ImGui::GetIO().WantCaptureMouse)
{
rc.env.intensity = window_context.env_intensity;
rc.update_light(window_context.omni_phi, window_context.omni_theta, window_context.omni_intensity);
}
if (window_context.save_sreenshot && !ImGui::GetIO().WantCaptureMouse)
{
save_screenshot(vp_buffers.accum_buffer, window_width, window_height,
filename_base + filename_ext, image_api, mdl_impexp_api);
if (options.output_auxiliary)
{
save_screenshot(vp_buffers.albedo_buffer, window_width, window_height,
filename_base + "_albedo" + filename_ext, image_api, mdl_impexp_api);
save_screenshot(vp_buffers.normal_buffer, window_width, window_height,
filename_base + "_normal" + filename_ext, image_api, mdl_impexp_api);
}
}
if (window_context.mouse_button - 1 == GLFW_MOUSE_BUTTON_LEFT)
{
if (window_context.mouse_button_action == GLFW_PRESS &&
!ImGui::GetIO().WantCaptureMouse)
{
window_context.moving = true;
glfwGetCursorPos(gl_window.get_window(), &window_context.move_start_x, &window_context.move_start_y);
}
else
{
window_context.moving = false;
}
}
if (window_context.mouse_wheel_delta && !ImGui::GetIO().WantCaptureMouse)
{
window_context.zoom += window_context.mouse_wheel_delta;
}
if (window_context.mouse_event && !ImGui::GetIO().WantCaptureMouse)
{
phi -= static_cast<float>(window_context.move_dx) * 0.001f * Constants.PI;
theta -= static_cast<float>(window_context.move_dy) * 0.001f * Constants.PI;
if (theta < 0.f)
theta = 0.f;
else if (theta > Constants.PI)
theta = Constants.PI;
window_context.move_dx = window_context.move_dy = 0.0;
rc.update_camera(phi, theta, base_dist, window_context.zoom);
}
if (window_context.key_event || window_context.mouse_event)
frame_nb = 0;
window_context.key_event = false;
window_context.mouse_event = false;
window_context.mouse_wheel_delta = 0;
window_context.mouse_button = 0;
window_context.save_sreenshot = false;
++frame_nb;
gl_display.resize(window_width, window_height);
if (gui)
{
gui->new_frame();
gui->update( nullptr);
mi::examples::gui::Event e = gui->process_event();
while (e.is_valid())
{
e = gui->process_event();
}
}
unsigned char* dst_image_data = gl_display.map();
#ifdef USE_PARALLEL_RENDERING
std::vector<std::thread> threads;
size_t lpt = window_height / num_threads +
(window_height % num_threads != 0 ? 1 : 0);
for (int i = 0; i < num_threads; ++i)
threads.push_back(std::thread(render_scene, rc, frame_nb, &vp_buffers,
dst_image_data, lpt*i, lpt*(i + 1), window_width, window_height, 4));
for (int i = 0; i < num_threads; ++i)
threads[i].join();
threads.clear();
#else
render_scene(rc, frame_nb, &vp_buffers,
dst_image_data, 0, window_height, window_width, window_height, 4);
#endif
gl_display.unmap();
gl_display.update_display();
if (gui)
gui->render(nullptr);
gl_window.present_back_buffer();
}
glfwTerminate();
}
image = nullptr;
}
rc.uninit();
if (neuray->shutdown() != 0)
exit_failure("Failed to shutdown the SDK.");
neuray = nullptr;
if (!mi::examples::mdl::unload())
exit_failure("Failed to unload the SDK.");
exit_success();
}
COMMANDLINE_TO_UTF8
NxM-dimensional matrix class template of fixed dimensions.
Definition: matrix.h:367
This interface represents a compiled material.
Definition: icompiled_material.h:94
This interface is used to interact with the distributed database.
Definition: idatabase.h:289
A constant expression.
Definition: iexpression.h:94
@ EK_CONSTANT
A constant expression. See mi::neuraylib::IExpression_constant.
Definition: iexpression.h:53
This interface represents a function definition.
Definition: ifunction_definition.h:44
This interface provides various utilities related to canvases and buffers.
Definition: iimage_api.h:49
virtual ITile * convert(const ITile *tile, const char *pixel_type) const =0
Converts a tile to a different pixel type.
virtual void adjust_gamma(ITile *tile, Float32 old_gamma, Float32 new_gamma) const =0
Sets the gamma value of a tile and adjusts the pixel data accordingly.
This interface represents a pixel image file.
Definition: iimage.h:65
This interface represents a material instance.
Definition: imaterial_instance.h:34
@ CLASS_COMPILATION
Selects class compilation instead of instance compilation.
Definition: imaterial_instance.h:41
@ DEFAULT_OPTIONS
Default compilation options (e.g., instance compilation).
Definition: imaterial_instance.h:40
virtual IMdl_backend * get_backend(Mdl_backend_kind kind)=0
Returns an MDL backend generator.
virtual IMdl_execution_context * create_execution_context()=0
Creates an execution context.
virtual const IString * get_db_module_name(const char *mdl_name)=0
Returns the DB name for the MDL name of a module (or file path for MDLE modules).
virtual Sint32 load_module(ITransaction *transaction, const char *argument, IMdl_execution_context *context=0)=0
Loads an MDL module from disk (or a builtin module) into the database.
This interface represents an MDL module.
Definition: imodule.h:611
Represents target code of an MDL backend.
Definition: imdl_backend.h:769
Textures add image processing options to images.
Definition: itexture.h:68
virtual const base::IInterface * access(const char *name)=0
Retrieves an element from the database.
virtual base::IInterface * create(const char *type_name, Uint32 argc=0, const base::IInterface *argv[]=0)=0
Creates an object of the type type_name.
virtual Sint32 commit()=0
Commits the transaction.
virtual Sint32 store(base::IInterface *db_element, const char *name, Uint8 privacy=LOCAL_SCOPE)=0
Stores the element db_element in the database under the name name and with the privacy level privacy.
A value of type boolean.
Definition: ivalue.h:106
@ VK_INVALID_DF
An invalid distribution function value. See mi::neuraylib::IValue_invalid_df.
Definition: ivalue.h:60
Abstract interface for accessing version information.
Definition: iversion.h:19
Handle<Interface> make_handle(Interface *iptr)
Returns a handle that holds the interface pointer passed in as argument.
Definition: handle.h:428
Interface * get() const
Access to the interface. Returns 0 for an invalid interface.
Definition: handle.h:294
unsigned int Uint32
32-bit unsigned integer.
Definition: types.h:49
Uint64 Size
Unsigned integral type that is large enough to hold the size of all types.
Definition: types.h:112
signed int Sint32
32-bit signed integer.
Definition: types.h:46
Sint32 dot(Sint32 a, Sint32 b)
Returns the inner product (a.k.a. dot or scalar product) of two integers.
Definition: function.h:1088
Float32 length(Float32 a)
Returns the Euclidean norm of the scalar a (its absolute value).
Definition: function.h:1114
Bbox<T, DIM> operator+(const Bbox<T, DIM> &bbox, T value)
Returns a bounding box that is the bbox increased by a constant value at each face,...
Definition: bbox.h:470
Bbox<T, DIM> operator/(const Bbox<T, DIM> &bbox, T divisor)
Returns a bounding box that is a version of bbox divided by divisor, i.e., bbox.max and bbox....
Definition: bbox.h:518
Bbox<T, DIM> operator-(const Bbox<T, DIM> &bbox, T value)
Returns a bounding box that is the bbox shrunk by a constant value at each face, i....
Definition: bbox.h:486
Bbox<T, DIM> operator*(const Bbox<T, DIM> &bbox, T factor)
Returns a bounding box that is a version of bbox scaled by factor, i.e., bbox.max and bbox....
Definition: bbox.h:502
Color clamp(const Color &c, const Color &low, const Color &high)
Returns the color c elementwise clamped to the range [low, high].
Definition: color.h:522
Color abs(const Color &c)
Returns a color with the elementwise absolute values of the color c.
Definition: color.h:471
Color cos(const Color &c)
Returns a color with the elementwise cosine of the color c.
Definition: color.h:558
math::Matrix<Float32, 3, 4> Float32_3_4
3 x 4 matrix of Float32.
Definition: matrix_typedefs.h:244
math::Vector<Float32, 3> Float32_3
Vector of three Float32.
Definition: vector_typedefs.h:90
virtual const char * get_texture(Size index) const =0
Returns the name of a texture resource used by the target code.
tct_traits<true>::tct_derivable_float3 tct_deriv_float3
A float3 with derivatives.
Definition: target_code_types.h:132
virtual Size get_texture_count() const =0
Returns the number of texture resources used by the target code.
#define MI_NEURAYLIB_BSDF_USE_MATERIAL_IOR
The calling code can mark the x component of an IOR field in *_data with MI_NEURAYLIB_BSDF_USE_MATERI...
Definition: target_code_types.h:758
@ SLOT_BACKFACE_EMISSION_INTENSITY
Slot backface.emission.intensity.
Definition: icompiled_material.h:34
@ SLOT_SURFACE_EMISSION_INTENSITY
Slot surface.emission.intensity.
Definition: icompiled_material.h:30
@ SLOT_BACKFACE_EMISSION_EDF_EMISSION
Slot backface.emission.emission.
Definition: icompiled_material.h:33
@ SLOT_SURFACE_EMISSION_EDF_EMISSION
Slot surface.emission.emission.
Definition: icompiled_material.h:29
Generic storage class template for math vector representations storing DIM elements of type T.
Definition: vector.h:135
Input and output structure for BSDF sampling data.
Definition: target_code_types.h:761
tct_float4 xi
input: pseudo-random sample numbers in range [0, 1)
Definition: target_code_types.h:767
tct_float pdf
output: pdf (non-projected hemisphere)
Definition: target_code_types.h:768
tct_float3 ior2
mutual input: IOR other side
Definition: target_code_types.h:763
tct_float3 k1
mutual input: outgoing direction
Definition: target_code_types.h:764
tct_float3 k2
output: incoming direction
Definition: target_code_types.h:766
Bsdf_event_type event_type
output: the type of event for the generated sample
Definition: target_code_types.h:770
tct_float3 ior1
mutual input: IOR current medium
Definition: target_code_types.h:762
tct_float3 bsdf_over_pdf
output: bsdf * dot(normal, k2) / pdf
Definition: target_code_types.h:769
The MDL material state structure inside the MDL SDK is a representation of the renderer state as defi...
Definition: target_code_types.h:210
tct_float3 normal
The result of state::normal().
Definition: target_code_types.h:217
char const * ro_data_segment
A pointer to a read-only data segment.
Definition: target_code_types.h:271
tct_int object_id
The result of state::object_id().
Definition: target_code_types.h:291
traits::tct_derivable_float3 position
The result of state::position().
Definition: target_code_types.h:225
tct_float4 const * world_to_object
A 4x4 transformation matrix in row-major order transforming from world to object coordinates.
Definition: target_code_types.h:278
tct_float3 const * tangent_v
An array containing the results of state::texture_tangent_v(i).
Definition: target_code_types.h:248
tct_float4 const * object_to_world
A 4x4 transformation matrix in row-major order transforming from object to world coordinates.
Definition: target_code_types.h:285
traits::tct_derivable_float3 const * text_coords
An array containing the results of state::texture_coordinate(i).
Definition: target_code_types.h:234
tct_float animation_time
The result of state::animation_time().
Definition: target_code_types.h:229
tct_float3 geom_normal
The result of state::geometry_normal().
Definition: target_code_types.h:221
tct_float3 const * tangent_u
An array containing the results of state::texture_tangent_u(i).
Definition: target_code_types.h:241
tct_float meters_per_scene_unit
The result of state::meters_per_scene_unit().
Definition: target_code_types.h:296
tct_float4 * text_results
The texture results lookup table.
Definition: target_code_types.h:256
The texture handler structure that is passed to the texturing functions.
Definition: target_code_types.h:712
The runtime for bitmap texture access for the generated target code can optionally be implemented in ...
Definition: target_code_types.h:344