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 <list>
#include "example_shared.h"
#include "texture_support_native.h"
#if MI_PLATFORM_MACOSX
#include <sys/types.h>
#include <sys/sysctl.h>
#endif
#include <imgui.h>
#include <imgui_impl_glfw.h>
#define GL_DISPLAY_NATIVE
#include "utils/gl_display.h"
#include "utils/profiling.h"
using namespace mi::examples::profiling;
#define terminate() \
do { \
glfwTerminate(); \
exit_failure(); \
} while (0)
#define WINDOW_TITLE "MDL-SDK df_native Example"
#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);
}
};
struct Enum_value
{
std::string name;
int value;
Enum_value(const std::string& name, int value)
: name(name), value(value)
{
}
};
struct Enum_type_info
{
std::vector<Enum_value> values;
void add(const std::string& name, int value)
{
values.push_back(Enum_value(name, value));
}
};
class Param_info
{
public:
enum Param_kind
{
PK_UNKNOWN,
PK_FLOAT,
PK_FLOAT2,
PK_FLOAT3,
PK_COLOR,
PK_ARRAY,
PK_BOOL,
PK_INT,
PK_ENUM,
PK_STRING,
PK_TEXTURE,
PK_LIGHT_PROFILE,
PK_BSDF_MEASUREMENT
};
Param_info(
char const* name,
char const* display_name,
char const* group_name,
Param_kind kind,
Param_kind array_elem_kind,
char* data_ptr,
const Enum_type_info* enum_info = nullptr)
: m_index(index)
, m_name(name)
, m_display_name(display_name)
, m_group_name(group_name)
, m_kind(kind)
, m_array_elem_kind(array_elem_kind)
, m_array_size(array_size)
, m_array_pitch(array_pitch)
, m_data_ptr(data_ptr)
, m_range_min(-100), m_range_max(100)
, m_enum_info(enum_info)
{
}
template<typename T>
T& data() { return *reinterpret_cast<T*>(m_data_ptr); }
template<typename T>
const T& data() const { return *reinterpret_cast<const T*>(m_data_ptr); }
const char*& display_name() { return m_display_name; }
const char* display_name() const { return m_display_name; }
const char*& group_name() { return m_group_name; }
const char* group_name() const { return m_group_name; }
Param_kind kind() const { return m_kind; }
Param_kind array_elem_kind() const { return m_array_elem_kind; }
mi::Size array_size()
const {
return m_array_size; }
mi::Size array_pitch()
const {
return m_array_pitch; }
float& range_min() { return m_range_min; }
float range_min() const { return m_range_min; }
float& range_max() { return m_range_max; }
float range_max() const { return m_range_max; }
const Enum_type_info* enum_info() const { return m_enum_info; }
private:
char const* m_name;
char const* m_display_name;
char const* m_group_name;
Param_kind m_kind;
Param_kind m_array_elem_kind;
char* m_data_ptr;
float m_range_min, m_range_max;
const Enum_type_info* m_enum_info;
};
class Material_info
{
public:
Material_info()
{}
void add_sorted_by_group(
const Param_info&
info)
{
bool group_found = false;
if (
info.group_name() !=
nullptr)
{
for (std::list<Param_info>::iterator it = params().begin(); it != params().end(); ++it)
{
const bool same_group =
it->group_name() !=
nullptr && strcmp(it->group_name(),
info.group_name()) == 0;
if (group_found && !same_group)
{
m_params.insert(it,
info);
return;
}
if (same_group)
group_found = true;
}
}
m_params.push_back(
info);
}
void add_enum_type(const std::string name, std::shared_ptr<Enum_type_info> enum_info)
{
enum_types[name] = enum_info;
}
const Enum_type_info* get_enum_type(const std::string name)
{
Enum_type_map::const_iterator it = enum_types.find(name);
if (it != enum_types.end())
return it->second.get();
return nullptr;
}
char const* get_name() const { return m_name.c_str(); }
void set_name(char const* name) { m_name = name; }
std::list<Param_info>& params() { return m_params; }
private:
std::string m_name;
std::list<Param_info> m_params;
typedef std::map<std::string, std::shared_ptr<Enum_type_info> > Enum_type_map;
Enum_type_map enum_types;
};
class String_constant_table
{
typedef std::map<std::string, unsigned> String_map;
public:
{
get_all_strings(target_code);
}
unsigned get_id_for_string(const char* name)
{
String_map::const_iterator it(m_string_constants_map.find(name));
if (it != m_string_constants_map.end())
return it->second;
unsigned n_id = unsigned(m_string_constants_map.size() + 1);
m_string_constants_map[name] = n_id;
m_strings.reserve((n_id + 63) & ~63);
m_strings.push_back(name);
size_t l = strlen(name);
if (l > m_max_len)
m_max_len = l;
return n_id;
}
size_t get_max_length() const { return m_max_len; }
const char* get_string(unsigned id)
{
if (id == 0 || id - 1 >= m_strings.size())
return nullptr;
return m_strings[id - 1].c_str();
}
private:
void get_all_strings(
{
m_max_len = 0;
m_strings.reserve(target_code->get_string_constant_count());
for (
mi::Size i = 1, n = target_code->get_string_constant_count(); i < n; ++i)
{
const char* s = target_code->get_string_constant(i);
size_t l = strlen(s);
if (l > m_max_len)
m_max_len = l;
m_string_constants_map[s] = (unsigned)i;
m_strings.push_back(s);
}
}
private:
String_map m_string_constants_map;
std::vector<std::string> m_strings;
size_t m_max_len;
};
class Resource_table
{
typedef std::map<std::string, unsigned> Resource_id_map;
public:
enum Kind
{
RESOURCE_TEXTURE,
RESOURCE_LIGHT_PROFILE,
RESOURCE_BSDF_MEASUREMENT
};
Resource_table(
Kind kind)
: m_max_len(0u)
{
read_resources(target_code, transaction, kind);
}
size_t get_max_length() const { return m_max_len; }
std::vector<std::string> const& get_urls() const { return m_urls; }
private:
void read_resources(
Kind kind)
{
m_urls.push_back("<unset>");
switch (kind)
{
case RESOURCE_TEXTURE:
for (
mi::Size i = 1, n = target_code->get_texture_count(); i < n; ++i)
{
const char* s = target_code->get_texture(i);
char const* url = nullptr;
if (char const* img = tex->get_image())
{
url = image->get_filename(0, 0);
}
if (url == nullptr)
url = s;
size_t l = strlen(url);
if (l > m_max_len)
m_max_len = l;
m_resource_map[s] = (unsigned)i;
m_urls.push_back(url);
}
break;
case RESOURCE_LIGHT_PROFILE:
for (
mi::Size i = 1, n = target_code->get_light_profile_count(); i < n; ++i)
{
const char* s = target_code->get_light_profile(i);
char const* url = lp->get_filename();
if (url == nullptr)
url = s;
size_t l = strlen(url);
if (l > m_max_len)
m_max_len = l;
m_resource_map[s] = (unsigned)i;
m_urls.push_back(url);
}
break;
case RESOURCE_BSDF_MEASUREMENT:
for (
mi::Size i = 1, n = target_code->get_bsdf_measurement_count(); i < n; ++i)
{
const char* s = target_code->get_bsdf_measurement(i);
char const* url = bm->get_filename();
if (url == nullptr)
url = s;
size_t l = strlen(url);
if (l > m_max_len)
m_max_len = l;
m_resource_map[s] = (unsigned)i;
m_urls.push_back(url);
}
break;
}
}
private:
Resource_id_map m_resource_map;
std::vector<std::string> m_urls;
size_t m_max_len;
};
static bool handle_resource(
Param_info& param,
Resource_table const& res_table)
{
bool changed = false;
std::vector<std::string> const& urls = res_table.get_urls();
int id = param.data<int>();
std::string cur_url = urls[id];
if (ImGui::BeginCombo(param.display_name(), cur_url.c_str()))
{
for (size_t i = 0, n = urls.size(); i < n; ++i)
{
const std::string& name = urls[i];
bool is_selected = (cur_url == name);
if (ImGui::Selectable(name.c_str(), is_selected))
{
param.data<int>() = int(i);
changed = true;
}
if (is_selected)
ImGui::SetItemDefaultFocus();
}
ImGui::EndCombo();
}
return changed;
}
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 enable_bsdf_flags;
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(1024)
, res_y(1024)
, 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)
, enable_bsdf_flags(false)
, allowed_scatter_mode(
mi::neuraylib:
:DF_FLAGS_ALLOW_REFLECT_AND_TRANSMIT)
, use_class_compilation(true)
, 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)
, bsdf_data_flags(
mi::neuraylib:
:DF_FLAGS_ALLOW_REFLECT_AND_TRANSMIT)
, 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;
}
env.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& mdl_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());
mdl_name = std::string(material_definition->get_mdl_name());
material_definition->create_function_call(0, &result));
if (result != 0)
exit_failure("Instantiating '%s' failed.", material_db_name.c_str());
material_parameter_annotations = material_definition->get_parameter_annotations();
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
context->
set_option(
"target_type", standard_material_type.get());
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,
bool enable_bsdf_flags)
{
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);
if (enable_bsdf_flags)
check_success(be_native->set_option("libbsdf_flags_in_bsdf_data", "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));
mi::Size argument_block_index = descs[0].argument_block_index;
#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;
if (argument_block_index !=
mi::Size(-1))
{
code_native->get_argument_block(argument_block_index));
render_context.argument_block = readonly_argument_block->clone();
render_context.argument_block_index = argument_block_index;
}
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
}
void collect_material_arguments_info(
Material_info& mat_info,
Render_context& rc,
const char* compiled_material_name,
{
rc.target_code->get_argument_block_layout(rc.argument_block_index));
rc.argument_block);
char* arg_block_data = arg_block != nullptr ? arg_block->get_data() : nullptr;
for (
mi::Size j = 0, num_params = cur_mat->get_parameter_count(); j < num_params; ++j)
{
const char* name = cur_mat->get_parameter_name(j);
if (name == nullptr) continue;
Param_info::Param_kind param_kind = Param_info::PK_UNKNOWN;
Param_info::Param_kind param_array_elem_kind = Param_info::PK_UNKNOWN;
const Enum_type_info* enum_type = nullptr;
switch (kind)
{
param_kind = Param_info::PK_FLOAT;
break;
param_kind = Param_info::PK_COLOR;
break;
param_kind = Param_info::PK_BOOL;
break;
param_kind = Param_info::PK_INT;
break;
{
val->get_type());
val_type->get_element_type());
{
switch (val_type->get_size())
{
case 2: param_kind = Param_info::PK_FLOAT2; break;
case 3: param_kind = Param_info::PK_FLOAT3; break;
default: assert(false && "Vector Size invalid or unhandled.");
}
}
}
break;
{
val->get_type());
val_type->get_element_type());
switch (elem_type->get_kind())
{
param_array_elem_kind = Param_info::PK_FLOAT;
break;
param_array_elem_kind = Param_info::PK_COLOR;
break;
param_array_elem_kind = Param_info::PK_BOOL;
break;
param_array_elem_kind = Param_info::PK_INT;
break;
{
elem_type.get_interface<
val_type->get_element_type());
{
switch (val_type->get_size())
{
case 2:
param_array_elem_kind = Param_info::PK_FLOAT2;
break;
case 3:
param_array_elem_kind = Param_info::PK_FLOAT3;
break;
default:
assert(false && "Vector Size invalid or unhandled.");
}
}
}
break;
default:
assert(false && "Array element type invalid or unhandled.");
}
if (param_array_elem_kind != Param_info::PK_UNKNOWN)
{
param_kind = Param_info::PK_ARRAY;
param_array_size = val_type->get_size();
if (param_array_size > 1)
{
layout->get_nested_state(j));
layout->get_nested_state(1, array_state));
mi::Size start_offset = layout->get_layout(
kind, param_size, array_state);
mi::Size next_offset = layout->get_layout(
kind, param_size, next_elem_state);
param_array_pitch = next_offset - start_offset;
}
}
}
break;
{
val->get_type());
const Enum_type_info*
info = mat_info.get_enum_type(val_type->get_symbol());
{
std::shared_ptr<Enum_type_info> p(new Enum_type_info());
for (
mi::Size i = 0, n = val_type->get_size(); i < n; ++i)
p->add(val_type->get_value_name(i), val_type->get_value_code(i));
mat_info.add_enum_type(val_type->get_symbol(), p);
}
param_kind = Param_info::PK_ENUM;
}
break;
param_kind = Param_info::PK_STRING;
break;
param_kind = Param_info::PK_TEXTURE;
break;
param_kind = Param_info::PK_LIGHT_PROFILE;
break;
param_kind = Param_info::PK_BSDF_MEASUREMENT;
break;
default:
continue;
}
mi::Size offset = layout->get_layout(kind2, param_size, state);
check_success(kind == kind2);
Param_info param_info(
j,
name,
name,
nullptr,
param_kind,
param_array_elem_kind,
param_array_size,
param_array_pitch,
arg_block_data + offset,
enum_type);
if (anno_block)
{
annos.get_annotation_index("::anno::soft_range(float,float)");
{
anno_index = annos.get_annotation_index("::anno::hard_range(float,float)");
}
{
annos.get_annotation_param_value(anno_index, 0, param_info.range_min());
annos.get_annotation_param_value(anno_index, 1, param_info.range_max());
}
anno_index = annos.get_annotation_index("::anno::display_name(string)");
{
annos.get_annotation_param_value(anno_index, 0, param_info.display_name());
}
anno_index = annos.get_annotation_index("::anno::in_group(string)");
{
annos.get_annotation_param_value(anno_index, 0, param_info.group_name());
}
}
mat_info.add_sorted_by_group(param_info);
}
}
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,
rc.argument_block.get(),
&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,
rc.argument_block.get());
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,
rc.argument_block.get(),
&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,
rc.argument_block.get(),
&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,
rc.argument_block.get());
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,
rc.argument_block.get(),
&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;
aux_data.flags = rc.bsdf_data_flags;
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;
eval_data.flags = rc.bsdf_data_flags;
ret_code = rc.target_code->execute_bsdf_evaluate(
surface_bsdf_function_index + 1,
&eval_data,
*shading_state,
tex_handler,
rc.argument_block.get());
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);
sample_data.
flags = rc.bsdf_data_flags;
ret_code = rc.target_code->execute_bsdf_sample(
surface_bsdf_function_index,
&sample_data,
*shading_state,
tex_handler,
rc.argument_block.get());
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));
option_force_default_gamma->set_value(true);
export_options->insert("force_default_gamma", option_force_default_gamma.get());
mdl_impexp_api->export_canvas(filename.c_str(), canvas.get(), export_options.get());
}
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: 1024x1024)\n"
<< " --hdr <filename> environment map\n"
<< " (default: nvidia/sdk_examples/resources/environment.hdr)\n"
<< " --nocc don't compile the material using class compilation\n"
<< " --cr use custom texture runtime\n"
<< " --allowed_scatter_mode <m> limits the allowed scatter mode to \"none\", \"reflect\", "
<< "\"transmit\" or \"reflect_and_transmit\" (default: restriction disabled)\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, "--nocc") == 0)
{
options.use_class_compilation = false;
}
else if (strcmp(opt, "--cr") == 0)
{
options.use_custom_tex_runtime = true;
}
else if (strcmp(opt, "--allowed_scatter_mode") == 0 && i < argc - 1)
{
options.enable_bsdf_flags = true;
char const *mode = argv[++i];
if (strcmp(mode, "none") == 0) {
} else if (strcmp(mode, "reflect") == 0) {
options.allowed_scatter_mode = mi::neuraylib::DF_FLAGS_ALLOW_REFLECT;
} else if (strcmp(mode, "transmit") == 0) {
options.allowed_scatter_mode = mi::neuraylib::DF_FLAGS_ALLOW_TRANSMIT;
} else if (strcmp(mode, "reflect_and_transmit") == 0) {
options.allowed_scatter_mode =
mi::neuraylib::DF_FLAGS_ALLOW_REFLECT_AND_TRANSMIT;
} else {
std::cout << "Unknown allowed_scatter_mode: \"" << mode << "\"" << std::endl;
usage(argv[0]);
}
}
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;
rc.bsdf_data_flags = options.allowed_scatter_mode;
{
Material_info mat_info;
{
std::string mdl_name;
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(),
material_parameter_annotations,
mdl_name);
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(
mdl_factory.get(),
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,
options.enable_bsdf_flags);
mat_info.set_name(mdl_name.c_str());
collect_material_arguments_info(
mat_info,
rc,
transaction.get(),
compilation_name.c_str(),
material_parameter_annotations.
get());
}
String_constant_table constant_table(rc.target_code);
Resource_table texture_table(rc.target_code, transaction, Resource_table::RESOURCE_TEXTURE);
Resource_table lp_table(rc.target_code, transaction, Resource_table::RESOURCE_LIGHT_PROFILE);
Resource_table bm_table(rc.target_code, transaction, Resource_table::RESOURCE_BSDF_MEASUREMENT);
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 = -1, window_height = -1;
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, factory, image_api, mdl_impexp_api);
if (options.output_auxiliary)
{
save_screenshot(vp_buffers.albedo_buffer, window_width, window_height,
filename_base + "_albedo" + filename_ext, factory, image_api, mdl_impexp_api);
save_screenshot(vp_buffers.normal_buffer, window_width, window_height,
filename_base + "_normal" + filename_ext, factory, image_api, mdl_impexp_api);
}
}
else
{
std::string version_string;
check_success(glfwInit());
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);
version_string = "#version 330 core";
GLFWwindow* window = glfwCreateWindow(options.res_x, options.res_y, WINDOW_TITLE, nullptr, nullptr);
if (!window)
{
std::cerr << "Error creating OpenGL window!" << std::endl;
terminate();
}
glfwMakeContextCurrent(window);
GLenum res = glewInit();
if (res != GLEW_OK)
{
std::cerr << "GLEW error: " << glewGetErrorString(res) << std::endl;
terminate();
}
glfwSwapInterval(0);
check_success(glGetError() == GL_NO_ERROR);
glfwSetWindowUserPointer(window, &window_context);
glfwSetKeyCallback(window, Window_context::handle_key);
glfwSetScrollCallback(window, Window_context::handle_scroll);
glfwSetCursorPosCallback(window, Window_context::handle_mouse_pos);
glfwSetMouseButtonCallback(window, Window_context::handle_mouse_button);
glfwSetCharCallback(window, ImGui_ImplGlfw_CharCallback);
IMGUI_CHECKVERSION();
ImGui::CreateContext();
ImGui_ImplGlfw_InitForOpenGL(window, false);
ImGui_ImplOpenGL3_Init(version_string.c_str());
ImGui::GetIO().IniFilename = nullptr;
ImGui::StyleColorsDark();
ImGui::GetStyle().Alpha = 0.7f;
ImGui::GetStyle().ScaleAllSizes(1.f);
mi::examples::mdl::GL_display* gl_display = new mi::examples::mdl::GL_display(options.res_x, options.res_y);
std::chrono::duration<double> state_update_time(0.0);
std::chrono::duration<double> render_time(0.0);
std::chrono::duration<double> display_time(0.0);
char stats_text[128];
int last_update_frames = -1;
auto last_update_time = std::chrono::steady_clock::now();
const std::chrono::duration<double> update_min_interval(0.5);
while (true)
{
std::chrono::time_point<std::chrono::steady_clock> t0 =
std::chrono::steady_clock::now();
if (glfwWindowShouldClose(window))
break;
glfwPollEvents();
int nwidth, nheight;
glfwGetFramebufferSize(window, &nwidth, &nheight);
if (window_width != nwidth || window_height != nheight)
{
window_width = nwidth;
window_height = nheight;
gl_display->resize(window_width, window_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_width == 0 || window_height == 0)
{
glfwWaitEvents();
continue;
}
ImGui_ImplOpenGL3_NewFrame();
ImGui_ImplGlfw_NewFrame();
ImGui::NewFrame();
ImGui::SetNextWindowPos(ImVec2(10, 100), ImGuiCond_FirstUseEver);
ImGui::SetNextWindowSize(
ImVec2(360.f, 600.f),
ImGuiCond_FirstUseEver);
ImGui::Begin("Settings");
ImGui::SetWindowFontScale(1.f);
ImGui::PushItemWidth(-200.f);
if (options.use_class_compilation)
ImGui::Text("CTRL + Click to manually enter numbers");
else
ImGui::Text("Parameter editing requires class compilation.");
ImGui::Dummy(ImVec2(0.0f, 3.0f));
ImGui::Text("Material parameters");
ImGui::Separator();
ImGui::Text("%s", mat_info.get_name());
bool changed = false;
const char* group_name = nullptr;
int id = 0;
for (std::list<Param_info>::iterator it = mat_info.params().begin(),
end = mat_info.params().end(); it != end; ++it, ++id)
{
Param_info& param = *it;
ImGui::PushID(id);
if ((!param.group_name() != !group_name) ||
(param.group_name() &&
(!group_name || strcmp(group_name, param.group_name()) != 0)))
{
ImGui::Separator();
if (param.group_name() != nullptr)
ImGui::Text("%s", param.group_name());
group_name = param.group_name();
}
switch (param.kind())
{
case Param_info::PK_FLOAT:
changed |= ImGui::SliderFloat(
param.display_name(),
¶m.data<float>(),
param.range_min(),
param.range_max());
break;
case Param_info::PK_FLOAT2:
changed |= ImGui::SliderFloat2(
param.display_name(),
¶m.data<float>(),
param.range_min(),
param.range_max());
break;
case Param_info::PK_FLOAT3:
changed |= ImGui::SliderFloat3(
param.display_name(),
¶m.data<float>(),
param.range_min(),
param.range_max());
break;
case Param_info::PK_COLOR:
changed |= ImGui::ColorEdit3(
param.display_name(),
¶m.data<float>());
break;
case Param_info::PK_BOOL:
changed |= ImGui::Checkbox(
param.display_name(),
¶m.data<bool>());
break;
case Param_info::PK_INT:
changed |= ImGui::SliderInt(
param.display_name(),
¶m.data<int>(),
int(param.range_min()),
int(param.range_max()));
break;
case Param_info::PK_ARRAY:
{
ImGui::Text("%s", param.display_name());
ImGui::Indent(16.0f);
char* ptr = ¶m.data<char>();
for (
mi::Size i = 0, n = param.array_size(); i < n; ++i)
{
std::string idx_str = std::to_string(i);
switch (param.array_elem_kind())
{
case Param_info::PK_FLOAT:
changed |= ImGui::SliderFloat(
idx_str.c_str(),
reinterpret_cast<float*>(ptr),
param.range_min(),
param.range_max());
break;
case Param_info::PK_FLOAT2:
changed |= ImGui::SliderFloat2(
idx_str.c_str(),
reinterpret_cast<float*>(ptr),
param.range_min(),
param.range_max());
break;
case Param_info::PK_FLOAT3:
changed |= ImGui::SliderFloat3(
idx_str.c_str(),
reinterpret_cast<float*>(ptr),
param.range_min(),
param.range_max());
break;
case Param_info::PK_COLOR:
changed |= ImGui::ColorEdit3(
idx_str.c_str(),
reinterpret_cast<float*>(ptr));
break;
case Param_info::PK_BOOL:
changed |= ImGui::Checkbox(
param.display_name(),
reinterpret_cast<bool*>(ptr));
break;
case Param_info::PK_INT:
changed |= ImGui::SliderInt(
param.display_name(),
reinterpret_cast<int*>(ptr),
int(param.range_min()),
int(param.range_max()));
break;
default:
assert(false && "Array element type invalid or unhandled.");
}
ptr += param.array_pitch();
}
ImGui::Unindent(16.0f);
}
break;
case Param_info::PK_ENUM:
{
int value = param.data<int>();
std::string curr_value;
const Enum_type_info*
info = param.enum_info();
for (
size_t i = 0, n =
info->values.size(); i < n; ++i)
{
if (
info->values[i].value == value)
{
curr_value =
info->values[i].name;
break;
}
}
if (ImGui::BeginCombo(param.display_name(), curr_value.c_str()))
{
for (
size_t i = 0, n =
info->values.size(); i < n; ++i)
{
const std::string& name =
info->values[i].name;
bool is_selected = (curr_value == name);
if (ImGui::Selectable(
info->values[i].name.c_str(), is_selected))
{
param.data<
int>() =
info->values[i].value;
changed = true;
}
if (is_selected)
ImGui::SetItemDefaultFocus();
}
ImGui::EndCombo();
}
}
break;
case Param_info::PK_STRING:
{
std::vector<char> buf;
size_t max_len = constant_table.get_max_length();
max_len = max_len > 63 ? max_len + 1 : 64;
buf.resize(max_len);
unsigned curr_index = param.data<unsigned>();
const char* opt = constant_table.get_string(curr_index);
strcpy(buf.data(), opt != nullptr ? opt : "");
if (ImGui::InputText(
param.display_name(),
buf.data(), buf.size(),
ImGuiInputTextFlags_EnterReturnsTrue))
{
unsigned id = constant_table.get_id_for_string(buf.data());
param.data<unsigned>() = id;
changed = true;
}
}
break;
case Param_info::PK_TEXTURE:
changed |= handle_resource(param, texture_table);
break;
case Param_info::PK_LIGHT_PROFILE:
changed |= handle_resource(param, lp_table);
break;
case Param_info::PK_BSDF_MEASUREMENT:
changed |= handle_resource(param, bm_table);
break;
case Param_info::PK_UNKNOWN:
break;
}
ImGui::PopID();
}
ImGui::PopItemWidth();
ImGui::End();
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, factory, image_api, mdl_impexp_api);
if (options.output_auxiliary)
{
save_screenshot(vp_buffers.albedo_buffer, window_width, window_height,
filename_base + "_albedo" + filename_ext, factory, image_api,
mdl_impexp_api);
save_screenshot(vp_buffers.normal_buffer, window_width, window_height,
filename_base + "_normal" + filename_ext, factory, 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(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 || changed)
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;
auto t1 = std::chrono::steady_clock::now();
state_update_time += t1 - t0;
t0 = t1;
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
t1 = std::chrono::steady_clock::now();
render_time += t1 - t0;
t0 = t1;
gl_display->unmap();
gl_display->update_display();
t1 = std::chrono::steady_clock::now();
display_time += t1 - t0;
ImGui::SetNextWindowPos(ImVec2(10, 10));
ImGui::Begin("##notitle", nullptr,
ImGuiWindowFlags_NoDecoration |
ImGuiWindowFlags_AlwaysAutoResize |
ImGuiWindowFlags_NoSavedSettings |
ImGuiWindowFlags_NoFocusOnAppearing |
ImGuiWindowFlags_NoNav);
++last_update_frames;
if (t1 - last_update_time > update_min_interval || last_update_frames == 0)
{
typedef std::chrono::duration<double, std::milli> durationMs;
snprintf(stats_text, sizeof(stats_text),
"%5.1f fps\n\n"
"state update: %8.1f ms\n"
"render: %8.1f ms\n"
"display: %8.1f ms\n",
last_update_frames / std::chrono::duration<double>(
t1 - last_update_time).count(),
(durationMs(state_update_time) / last_update_frames).count(),
(durationMs(render_time) / last_update_frames).count(),
(durationMs(display_time) / last_update_frames).count());
last_update_time = t1;
last_update_frames = 0;
state_update_time = render_time = display_time =
std::chrono::duration<double>::zero();
}
ImGui::TextUnformatted(stats_text);
ImGui::End();
ImGui::Render();
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
glfwSwapBuffers(window);
}
delete gl_display;
gl_display = nullptr;
ImGui_ImplOpenGL3_Shutdown();
ImGui_ImplGlfw_Shutdown();
ImGui::DestroyContext();
glfwDestroyWindow(window);
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
This interface represents bool.
Definition: inumber.h:122
This interface represents maps, i.e., a key-value based data structure.
Definition: imap.h:41
NxM-dimensional matrix class template of fixed dimensions.
Definition: matrix.h:367
A wrapper around the interfaces for MDL annotations.
Definition: annotation_wrapper.h:37
An ordered collection of annotation blocks identified by name or index.
Definition: iexpression.h:603
virtual const IAnnotation_block * get_annotation_block(Size index) const =0
Returns the annotation block for index, or NULL if there is no such block.
A scene element that stores measured BSDF data.
Definition: ibsdf_measurement.h:39
This interface represents a compiled material.
Definition: icompiled_material.h:97
This interface is used to interact with the distributed database.
Definition: idatabase.h:289
A constant expression.
Definition: iexpression.h:96
@ EK_CONSTANT
A constant expression. See mi::neuraylib::IExpression_constant.
Definition: iexpression.h:55
This API component allows the creation, assignment, and cloning of instances of types.
Definition: ifactory.h:35
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:72
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:66
This interface represents light profiles.
Definition: ilightprofile.h:73
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 Sint32 set_option(const char *name, const char *value)=0
Sets a string option.
virtual IMdl_execution_context * create_execution_context()=0
Creates an execution context.
virtual IType_factory * create_type_factory(ITransaction *transaction)=0
Returns an MDL type factory for the given transaction.
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:634
Represents target code of an MDL backend.
Definition: imdl_backend.h:783
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.
@ SID_MATERIAL
The "::material" struct type.
Definition: itype.h:484
The type of kind vector.
Definition: itype.h:399
@ TK_BOOL
The boolean type. See mi::neuraylib::IType_bool.
Definition: itype.h:158
@ TK_COLOR
The color type. See mi::neuraylib::IType_color.
Definition: itype.h:174
@ TK_VECTOR
A vector type. See mi::neuraylib::IType_vector.
Definition: itype.h:170
@ TK_INT
The integer type. See mi::neuraylib::IType_int.
Definition: itype.h:160
@ TK_FLOAT
The float type. See mi::neuraylib::IType_float.
Definition: itype.h:164
A value of type array.
Definition: ivalue.h:403
A value of type boolean.
Definition: ivalue.h:106
A value of type enum.
Definition: ivalue.h:144
A value of type vector.
Definition: ivalue.h:309
Kind
The possible kinds of values.
Definition: ivalue.h:36
@ VK_TEXTURE
A texture value. See mi::neuraylib::IValue_texture.
Definition: ivalue.h:62
@ VK_LIGHT_PROFILE
A light_profile value. See mi::neuraylib::IValue_light_profile.
Definition: ivalue.h:64
@ VK_ARRAY
An array value. See mi::neuraylib::IValue_array.
Definition: ivalue.h:56
@ VK_FLOAT
A float value. See mi::neuraylib::IValue_float.
Definition: ivalue.h:44
@ VK_BSDF_MEASUREMENT
A bsdf_measurement value. See mi::neuraylib::IValue_bsdf_measurement.
Definition: ivalue.h:66
@ VK_ENUM
An enum value. See mi::neuraylib::IValue_enum.
Definition: ivalue.h:42
@ VK_INT
An integer value. See mi::neuraylib::IValue_int.
Definition: ivalue.h:40
@ VK_VECTOR
A vector value. See mi::neuraylib::IValue_vector.
Definition: ivalue.h:50
@ VK_INVALID_DF
An invalid distribution function value. See mi::neuraylib::IValue_invalid_df.
Definition: ivalue.h:60
@ VK_BOOL
A boolean value. See mi::neuraylib::IValue_bool.
Definition: ivalue.h:38
@ VK_STRING
A string value. See mi::neuraylib::IValue_string.
Definition: ivalue.h:48
@ VK_COLOR
A color value. See mi::neuraylib::IValue_color.
Definition: ivalue.h:54
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
std::basic_ostream<C, T> & info(std::basic_ostream<C, T> &ostream)
Manipulator for mi::base::Log_stream.
Definition: ilogger.h:580
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:1081
Float32 length(Float32 a)
Returns the Euclidean norm of the scalar a (its absolute value).
Definition: function.h:1107
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
Df_flags
Flags controlling the calculation of DF results.
Definition: target_code_types.h:761
@ DF_FLAGS_NONE
allows nothing -> black
Definition: target_code_types.h:762
@ 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
Common namespace for APIs of NVIDIA Advanced Rendering Center GmbH.
Definition: example_derivatives.dox:5
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:773
tct_float4 xi
input: pseudo-random sample numbers in range [0, 1)
Definition: target_code_types.h:779
tct_float pdf
output: pdf (non-projected hemisphere)
Definition: target_code_types.h:780
Df_flags flags
input: flags controlling calculation of result (optional depending on backend options)
Definition: target_code_types.h:785
tct_float3 ior2
mutual input: IOR other side
Definition: target_code_types.h:775
tct_float3 k1
mutual input: outgoing direction
Definition: target_code_types.h:776
tct_float3 k2
output: incoming direction
Definition: target_code_types.h:778
Bsdf_event_type event_type
output: the type of event for the generated sample
Definition: target_code_types.h:782
tct_float3 ior1
mutual input: IOR current medium
Definition: target_code_types.h:774
tct_float3 bsdf_over_pdf
output: bsdf * dot(normal, k2) / pdf
Definition: target_code_types.h:781
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
Structure representing the state during traversal of the nested layout.
Definition: imdl_backend.h:693
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