This example introduces the distillation of mdl materials to a fixed target model and showcases how to bake material sub-expressions to a texture.
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
#include <map>
#include "example_shared.h"
#include "example_distilling_shared.h"
struct Material_parameter
{
Remap_func* remap_func;
Material_parameter() : remap_func(nullptr)
{
}
Material_parameter(
Remap_func* func = nullptr)
: value_type(value_type)
, remap_func(func)
{
}
};
void configure(
{
for(const auto& p : mdl_paths)
}
{
std::string module_name = get_module_name(material_name);
check_success(mdl_compiler->
load_module(transaction, module_name.
c_str(), context) >= 0);
print_messages(context);
const char *prefix = (material_name.
find(
"::") == 0) ?
"mdl" :
"mdl::";
material_db_name.
c_str()));
material_definition->create_material_instance(0, &result);
check_success(result == 0);
return material_instance;
}
bool class_compilation)
{
check_success(print_messages(context));
return compiled_material;
}
const char* target_model)
{
distiller_api->
distill_material(compiled_material, target_model,
nullptr, &result));
check_success(result == 0);
distilled_material->retain();
return distilled_material.get();
}
{
if(!canvas)
return;
{
data[i] = (data[i] + 1.f) * 0.5f;
}
}
{
if(canvas)
{
{
data[i] = 1.0f - data[i];
}
return;
}
if(value)
{
value->set_value(1.0f - f);
}
}
void setup_target_material(
Material& out_material)
{
lookup_call("surface.scattering", cm));
get_call_semantic(transaction, parent_call.get()));
if(target_model == "diffuse")
{
check_success(semantic ==
out_material["color"] = Material_parameter("Rgb_fp");
out_material["roughness"] = Material_parameter("Float32");
out_material["normal"] = Material_parameter("Float32<3>", remap_normal);
out_material["color"].bake_path = "surface.scattering.tint";
out_material["roughness"].bake_path = "surface.scattering.roughness";
out_material["normal"].bake_path = "geometry.normal";
}
else if(target_model == "diffuse_glossy")
{
out_material["diffuse_color"] = Material_parameter("Rgb_fp");
out_material["glossy_color"] = Material_parameter("Rgb_fp");
out_material["glossy_roughness"] = Material_parameter("Float32");
out_material["glossy_weight"] = Material_parameter("Float32");
out_material["ior"] = Material_parameter("Float32");
out_material["normal"] = Material_parameter("Float32<3>", remap_normal);
switch(semantic)
{
out_material["diffuse_color"].bake_path = "surface.scattering.tint";
break;
out_material["glossy_color"].bake_path = "surface.scattering.tint";
out_material["glossy_roughness"].bake_path = "surface.scattering.roughness_u";
break;
out_material["diffuse_color"].bake_path ="surface.scattering.base.tint";
out_material["glossy_color"].bake_path = "surface.scattering.layer.tint";
out_material["glossy_roughness"].bake_path = "surface.scattering.layer.roughness_u";
out_material["glossy_weight"].bake_path = "surface.scattering.weight";
out_material["ior"].bake_path = "surface.scattering.ior";
break;
default:
break;
}
out_material["normal"].bake_path = "geometry.normal";
}
else if (target_model == "ue4" || target_model == "transmissive_pbr")
{
out_material["base_color"] = Material_parameter("Rgb_fp");
out_material["metallic"] = Material_parameter("Float32");
out_material["specular"] = Material_parameter("Float32");
out_material["roughness"] = Material_parameter("Float32");
out_material["normal"] = Material_parameter("Float32<3>", remap_normal);
out_material["clearcoat_weight"] = Material_parameter("Float32");
out_material["clearcoat_roughness"] = Material_parameter("Float32");
out_material["clearcoat_normal"] = Material_parameter("Float32<3>", remap_normal);
out_material["opacity"] = Material_parameter("Float32");
bool is_transmissive_pbr = false;
if (target_model == "transmissive_pbr")
{
is_transmissive_pbr = true;
out_material["anisotropy"] = Material_parameter("Float32");
out_material["anisotropy_rotation"] = Material_parameter("Float32");
out_material["transparency"] = Material_parameter("Float32");
out_material["transmission_color"] = Material_parameter("Rgb_fp");
out_material["attenuation_color"] = Material_parameter("Rgb_fp");
out_material["attenuation_distance"] = Material_parameter("Float32");
out_material["subsurface_color"] = Material_parameter("Rgb_fp");
out_material["volume_ior"] = Material_parameter("Rgb_fp");
out_material["attenuation_color"].bake_path = "volume.absorption_coefficient.s.v.attenuation";
out_material["subsurface_color"].bake_path = "volume.absorption_coefficient.s.v.subsurface";
out_material["attenuation_distance"].bake_path = "volume.scattering_coefficient.s.v.distance";
out_material["volume_ior"].bake_path = "ior";
}
{
out_material["clearcoat_weight"].bake_path = path_prefix + "weight";
out_material["clearcoat_roughness"].bake_path = path_prefix + "layer.roughness_u";
out_material["clearcoat_normal"].bake_path = path_prefix + "normal";
parent_call = lookup_call("base", cm, parent_call.get());
semantic = get_call_semantic(transaction, parent_call.get());
path_prefix += "base.";
}
{
out_material["normal"].bake_path = path_prefix + "normal";
parent_call = lookup_call("layer", cm, parent_call.get());
semantic = get_call_semantic(transaction, parent_call.get());
path_prefix += "layer.";
}
{
out_material["metallic"].bake_path = path_prefix + "components.1.weight";
if (is_transmissive_pbr) {
out_material["roughness"].bake_path = path_prefix + "components.1.component.roughness_u.s.r.roughness";
out_material["anisotropy"].bake_path = path_prefix + "components.1.component.roughness_u.s.r.anisotropy";
out_material["anisotropy_rotation"].bake_path = path_prefix + "components.1.component.roughness_u.s.r.rotation";
}
else
out_material["roughness"].bake_path = path_prefix + "components.1.component.roughness_u";
out_material["base_color"].bake_path = path_prefix + "components.1.component.tint";
parent_call = lookup_call(
"components.0.component", cm, parent_call.get());
semantic = get_call_semantic(transaction, parent_call.get());
path_prefix += "components.0.component.";
}
{
out_material["specular"].bake_path = path_prefix + "weight";
if(out_material["roughness"].bake_path.empty())
out_material["roughness"].bake_path = path_prefix + "layer.roughness_u";
parent_call = lookup_call("base", cm, parent_call.get());
semantic = get_call_semantic(transaction, parent_call.get());
path_prefix += "base.";
}
{
check_success(is_transmissive_pbr);
out_material["transparency"].bake_path = path_prefix + "components.1.weight";
out_material["transmission_color"].bake_path = path_prefix + "components.1.component.tint";
parent_call = lookup_call("components.0.component", cm, parent_call.get());
semantic = get_call_semantic(transaction, parent_call.get());
path_prefix += "components.0.component.";
}
if(semantic ==
{
if(out_material["metallic"].bake_path.empty())
out_material["metallic"].value = create_value(transaction, "Float32", 1.0f);
if(out_material["roughness"].bake_path.empty())
out_material["roughness"].bake_path = path_prefix + "roughness_u";
if(out_material["base_color"].bake_path.empty())
out_material["base_color"].bake_path = path_prefix + "tint";
}
else if(semantic ==
{
if(out_material["base_color"].bake_path.empty())
out_material["base_color"].bake_path = path_prefix + "tint";
}
if(cutout.is_valid_interface())
out_material["opacity"].bake_path = "geometry.cutout_opacity";
}
else if (target_model == "specular_glossy")
{
out_material["base_color"] = Material_parameter("Rgb_fp");
out_material["f0"] = Material_parameter("Rgb_fp");
out_material["f0_color"] = Material_parameter("Rgb_fp");
out_material["f0_refl"] = Material_parameter("Float32");
out_material["f0_weight"] = Material_parameter("Float32");
out_material["glossiness"] = Material_parameter("Float32", rough_to_gloss);
out_material["opacity"] = Material_parameter("Float32");
out_material["normal_map"] = Material_parameter("Float32<3>", remap_normal);
switch(semantic)
{
out_material["base_color"].bake_path = "surface.scattering.tint";
out_material["f0_weight"].value = create_value(transaction, "Float32", 0.0f);
out_material[
"f0_color"].value = create_value(transaction,
"Color",
mi::Color(0.0f));
break;
out_material["f0_color"].bake_path = "surface.scattering.tint";
out_material["f0_refl"].value = create_value(transaction, "Float32", 1.0f);
out_material["f0_weight"].value = create_value(transaction, "Float32", 1.0f);
out_material["glossiness"].bake_path =
"surface.scattering.roughness_u";
break;
out_material["base_color"].bake_path ="surface.scattering.base.tint";
out_material["f0_color"].bake_path = "surface.scattering.layer.tint";
out_material["f0_refl"].bake_path = "surface.scattering.normal_reflectivity";
out_material["f0_weight"].bake_path = "surface.scattering.weight";
out_material["glossiness"].bake_path =
"surface.scattering.layer.roughness_u";
break;
default:
break;
}
out_material["normal_map"].bake_path = "geometry.normal";
out_material["opacity"].bake_path = "geometry.cutout_opacity";
}
}
void bake_target_material_inputs(
Material& out_material)
{
for(Material::iterator it = out_material.begin();
it != out_material.end(); ++it)
{
Material_parameter& param = it->second;
if(param.bake_path.empty())
continue;
cm, param.bake_path.c_str(), baker_resource));
check_success(baker.is_valid_interface());
if(baker->is_uniform())
{
if(param.value_type == "Rgb_fp")
{
}
else if(param.value_type == "Float32<3>")
{
}
else if(param.value_type == "Float32")
{
}
else
{
std::cout <<
"Ignoring unsupported value type '" << param.value_type
continue;
}
check_success(result == 0);
if(param.remap_func)
param.remap_func(value.
get());
param.value = value;
}
else
{
mi::Sint32 result = baker->bake_texture(canvas.get(), baking_samples);
check_success(result == 0);
if(param.remap_func)
param.remap_func(canvas.get());
param.texture = canvas;
}
}
}
template <typename T, typename U>
{
if(canvas)
{
out_array =static_cast<T*>(tile->get_data());
}
else if(value)
{
}
}
{
if(material["f0_weight"].value)
{
float v;
if(v==0.0f)
{
material[
"f0"].value = create_value(trans,
"Color",
mi::Color(0.0f));
material["f0"].texture = 0;
return;
}
}
mi::Uint32 rx = material[
"f0"].texture->get_resolution_x();
mi::Uint32 ry = material[
"f0"].texture->get_resolution_y();
init_value(material["f0"].texture.get(), nullptr,
f0, f0_color_value);
init_value(material["f0_color"].texture.get(), material["f0_color"].value.get(),
f0_color, f0_color_value);
init_value(material["f0_weight"].texture.get(), material["f0_weight"].value.get(),
f0_weight, f0_weight_value);
init_value(material["f0_refl"].texture.get(), material["f0_refl"].value.get(),
f0_refl, f0_refl_value);
{
const mi::Float32 t = (f0_weight ? f0_weight[i] : f0_weight_value) *
(f0_refl ? f0_refl[i] : f0_refl_value);
f0[i][0] = (f0_color ? f0_color[i][0] : f0_color_value[0]) * t;
f0[i][1] = (f0_color ? f0_color[i][1] : f0_color_value[1]) * t;
f0[i][2] = (f0_color ? f0_color[i][2] : f0_color_value[2]) * t;
}
}
void process_target_material(
const Material& material,
{
std::cout <<
"--------------------------------------------------------------------------------"
std::cout <<
"--------------------------------------------------------------------------------"
for(Material::const_iterator it = material.begin();
it != material.end(); ++it)
{
const Material_parameter& param = it->second;
std::cout <<
"Parameter: '" << param_name <<
"': ";
if(param.bake_path.empty())
{
std::cout <<
" no matching bake path found in target material."
if(param.value)
if(param.texture)
}
else
std::cout <<
"path '"<< param.bake_path <<
"' baked to ";
if(param.texture)
{
file_name << material_name << "-" << param_name << ".png";
check_success(
}
else if(param.value)
{
if(param.value_type == "Rgb_fp")
{
color->get_value(c);
<< c.
r <<
", " << c.
g <<
", " << c.
b <<
")."<< std::endl <<
std::endl;
}
else if(param.value_type == "Float32")
{
value->get_value(v);
}
else if(param.value_type == "Float32<3>")
{
value->get_value(v);
<< v.x <<
", " << v.y <<
", " << v.z <<
")."<< std::endl <<
std::endl;
}
}
<< "--------------------------------------------------------------------------------"
}
}
static void usage(const char *name)
{
<< "usage: " << name << " [options] [<material_name1> ...]\n"
<< "-h print this text\n"
<< "--target distilling target:diffuse|diffuse_glossy|ue4|transmissive_pbr|\n"
<< " specular_glossy (default: ue4)\n"
<< "--baker_resource baking device: gpu|cpu|gpu_with_cpu_fallback (default: cpu)\n"
<< "--samples baking samples (default: 4)\n"
<< "--mdl_path <path> mdl search path, can occur multiple times.\n";
}
int main(int argc, char* argv[])
{
for (int i = 1; i < argc; ++i) {
const char *opt = argv[i];
if (opt[0] == '-') {
if (
strcmp(opt,
"--mdl_path") == 0) {
if (i < argc - 1)
else
usage(argv[0]);
}
else if (
strcmp(opt,
"--target") == 0) {
if (i < argc - 1)
target_model = argv[++i];
else
usage(argv[0]);
}
else if (
strcmp(opt,
"--baker_resource") == 0) {
if (i < argc - 1) {
if (res == "gpu")
else if (res == "gpu_with_cpu_fallback")
else if (res != "cpu")
usage(argv[0]);
}
else
usage(argv[0]);
}
else if (
strcmp(opt,
"--samples") == 0) {
if (i < argc - 1)
else
usage(argv[0]);
}
else
usage(argv[0]);
}
else
}
if (material_names.
empty())
"::nvidia::sdk_examples::tutorials_distilling::example_distilling1");
check_success(neuray.is_valid_interface());
configure(mdl_compiler.get(), mdl_paths);
check_start_success(result);
{
for(const auto& material_name : material_names)
{
factory->create_execution_context());
create_material_instance(
transaction.get(),
mdl_compiler.get(),
context.get(),
material_name));
compile_material_instance(instance.get(), context.get(), false));
create_distilled_material(
distilling_api.get(),
compiled_material.get(),
Material out_material;
setup_target_material(
target_model,
transaction.get(),
distilled_material.get(),
out_material);
bake_target_material_inputs(
baker_resource,
baking_samples,
transaction.get(),
distilled_material.get(),
distilling_api.get(),
image_api.get(),
out_material);
if(target_model == "specular_glossy")
{
out_material["f0"].texture =
calculate_f0(transaction.get(), out_material);
}
process_target_material(
target_model,
get_material_name(material_name),
out_material,
mdl_compiler.get());
}
}
mdl_compiler = 0;
check_success(neuray->shutdown() == 0);
neuray = 0;
check_success(unload());
keep_console_open();
return EXIT_SUCCESS;
}