This example shows how an MDL material distilled to the UE4 target can be mapped to a predefined GLSL PBR shader to render a sphere in an OpenGL window.
#include <map>
#include <iomanip>
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include "example_shared.h"
#include "example_glsl_shared.h"
#include "example_distilling_shared.h"
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#define _USE_MATH_DEFINES
#if !defined(MI_PLATFORM_MACOSX) && !defined(MI_ARCH_ARM_64)
#define USE_SSBO
#endif
struct Options {
bool show_window;
bool bake;
int baking_resolution_x;
int baking_resolution_y;
float exposure;
std::vector<std::string> material_names;
std::string hdrfile;
std::string outputfile;
Options()
: show_window(true)
, bake(true)
, baking_resolution_x(2048)
, baking_resolution_y(2048)
, exposure(0.0f)
, hdrfile("nvidia/sdk_examples/resources/environment.hdr")
, outputfile("output.exr")
{}
};
struct Mdl_sdk_state
{
};
{
const char *pixel_type = canvas->get_type();
const char *target_type;
if (strcmp(pixel_type, "Rgba") == 0 ||
strcmp(pixel_type, "Rgbea") == 0 ||
strcmp(pixel_type, "Rgba_16") == 0 ||
strcmp(pixel_type, "Color") == 0)
target_type = "Color";
else if (
strcmp(pixel_type, "Sint8") == 0 ||
strcmp(pixel_type, "Sint32") == 0 ||
strcmp(pixel_type, "Float32") == 0)
target_type = "Float32";
else
target_type = "Rgb_fp";
const bool type_conversion = strcmp(pixel_type, target_type) != 0;
if (gamma == 1.0f && !type_conversion)
return canvas;
if (gamma != 1.0f) {
gamma_canvas->set_gamma(gamma);
canvas = gamma_canvas;
} else if (type_conversion) {
canvas = image_api->
convert(canvas.
get(), target_type);
}
return canvas;
}
const char* filename)
{
if (image->reset_file(filename) == 0)
{
std::string t = image->get_type(0, 0);
if (t == "Rgb" || t == "Rgba")
gamma = 2.2f;
result = adjust_canvas(
}
return result;
}
const char* texture_name)
{
if (!texture)
if (!image)
if (image->is_uvtile() || image->is_animated()) {
std::cerr << "The example does not support uvtile and/or animated textures!" << std::endl;
}
return adjust_canvas(image_api, canvas, texture->get_effective_gamma(0, 0));
}
const Mdl_sdk_state& state,
const char* filename,
GLuint texture,
GLuint w,
GLuint h)
{
void* data = tile->get_data();
glBindTexture(GL_TEXTURE_2D, texture);
glGetTexImage(GL_TEXTURE_2D, 0, GL_RGB, GL_FLOAT, data);
return state.mdl_impexp_api->export_canvas(filename, canvas.
get());
}
std::string get_color_default(
const std::string& name,
const mi::Color& value =
mi::Color(0.0f))
{
return
"vec3 " + name + "(State state) {\n"
" return vec3(" + to_string(value.r) + +"," +
to_string(value.g) + ", " + to_string(value.b) + ");\n"
"}\n";
}
std::string get_float_default(
const std::string& name,
mi::Float32 value = 0.0f)
{
return
"float " + name + "(State state) {\n"
" return float(" + to_string(value) + ");\n"
"}\n";
}
std::string get_normal_default(const std::string& name)
{
return
"vec3 " + name + "(State state) {\n"
" return state.normal;\n"
"}\n";
}
std::string get_vector_texture_access(
const std::string& name,
mi::Size index)
{
return
"vec3 " + name + "(State state) {\n"
" return texture(mdl_textures_2d[" +
to_string(index) + "], state.text_coords[0].rg).rgb;\n"
"}\n";
}
std::string get_normal_texture_access(
const std::string& name,
mi::Size index)
{
return
"vec3 " + name + "(State state) {\n"
" vec3 n = texture(mdl_textures_2d[" +
to_string(index) + "], state.text_coords[0].rg).rgb;\n"
"return normalize("
"state.tangent_u[0] * n.x + "
"state.tangent_v[0] * n.y + "
"state.normal * n.z);\n"
"}\n";
}
std::string get_float_texture_access(
const std::string& name,
mi::Size index)
{
return
"float " + name + "(State state) {\n"
" return texture(mdl_textures_2d[" +
to_string(index) + "], state.text_coords[0].rg).r;\n"
"}\n";
}
class Mdl_ue4
{
public:
virtual ~Mdl_ue4() {}
virtual const char* get_code() const = 0;
virtual mi::Size get_texture_count()
const = 0;
virtual mi::Size get_ro_data_segment_count()
const
{
return 0;
}
virtual const char* get_ro_data_segment_data(
mi::Size)
const
{
return 0;
}
{
return 0;
}
virtual const char* get_ro_data_segment_name(
mi::Size)
const
{
return 0;
}
protected:
const std::string& parameter_name, const std::string& path) = 0;
virtual void add_color_default(
virtual void add_float_default(
virtual void add_normal_default(const std::string& parameter_name) = 0;
void add_ue4_material_expressions(
{
lookup_call("surface.scattering", cm));
get_call_semantic(transaction, parent_call.get()));
bool has_clearcoat = false;
bool has_metallic = false;
bool is_metallic = false;
bool has_base_color = false;
bool has_specular = false;
bool has_roughness = false;
bool has_normal = false;
std::string path_prefix = "surface.scattering.";
{
add_material_expression("clearcoat_weight", path_prefix + "weight");
add_material_expression("clearcoat_color", path_prefix + "layer.tint");
add_material_expression("clearcoat_roughness", path_prefix + "layer.roughness_u");
add_material_expression("clearcoat_normal", path_prefix + "normal");
has_clearcoat = true;
parent_call = lookup_call("base", cm, parent_call.get());
semantic = get_call_semantic(transaction, parent_call.get());
path_prefix += "base.";
}
{
add_material_expression("normal", path_prefix + "normal");
has_normal = true;
parent_call = lookup_call("layer", cm, parent_call.get());
semantic = get_call_semantic(transaction, parent_call.get());
path_prefix += "layer.";
}
{
add_material_expression("metallic", path_prefix + "components.value1.weight");
add_material_expression("roughness",
path_prefix + "components.value1.component.roughness_u");
add_material_expression("base_color", path_prefix + "components.value1.component.tint");
has_roughness = true;
has_metallic = true;
has_base_color = true;
parent_call = lookup_call(
"components.value0.component", cm, parent_call.get());
semantic = get_call_semantic(transaction, parent_call.get());
path_prefix += "components.value0.component.";
}
{
add_material_expression("specular", path_prefix + "weight");
has_specular = true;
if (!has_roughness)
{
add_material_expression("roughness", path_prefix + "layer.roughness_u");
has_roughness = true;
}
parent_call = lookup_call("base", cm, parent_call.get());
semantic = get_call_semantic(transaction, parent_call.get());
path_prefix += "base.";
}
if (semantic ==
{
if (!has_metallic)
{
if (!has_base_color)
{
add_material_expression("base_color", path_prefix + "tint");
has_base_color = true;
}
if (!has_roughness)
{
add_material_expression("roughness", path_prefix + "roughness_u");
has_roughness = true;
}
is_metallic = true;
}
}
else if (semantic ==
{
if (!has_base_color)
{
add_material_expression("base_color", path_prefix + "tint");
has_base_color = true;
}
}
if (!has_normal)
add_material_expression("normal", "geometry.normal");
if (!has_base_color)
add_color_default("base_color");
if (!has_roughness)
add_float_default("roughness");
if (!has_metallic)
add_float_default("metallic", is_metallic ? 1.0f : 0.0f);
if (!has_specular)
add_float_default("specular");
if (!has_clearcoat)
{
add_color_default("clearcoat_color");
add_float_default("clearcoat_weight");
add_float_default("clearcoat_roughness");
add_normal_default("clearcoat_normal");
}
}
};
class Mdl_ue4_glsl : public Mdl_ue4
{
public:
: m_result(0)
, m_transaction(state.transaction)
, m_image_api(state.mdl_sdk->get_api_component<
mi::neuraylib::IImage_api>())
, m_context(state.mdl_factory->create_execution_context())
{
check_success(m_be_glsl);
check_success(m_be_glsl->set_option("num_texture_spaces", "1") == 0);
#ifdef USE_SSBO
check_success(m_be_glsl->set_option("glsl_version", "430") == 0);
#else
check_success(m_be_glsl->set_option("glsl_version", "330") == 0);
#endif
check_success(m_be_glsl->set_option("glsl_state_normal_mode", "field") == 0);
check_success(m_be_glsl->set_option("glsl_state_position_mode", "field") == 0);
check_success(m_be_glsl->set_option("glsl_state_texture_coordinate_mode", "field") == 0);
check_success(m_be_glsl->set_option("glsl_state_texture_tangent_u_mode", "field") == 0);
check_success(m_be_glsl->set_option("glsl_state_texture_tangent_v_mode", "field") == 0);
check_success(m_be_glsl->set_option("glsl_state_texture_space_max_mode", "field") == 0);
#ifdef USE_SSBO
check_success(m_be_glsl->set_option("glsl_max_const_data", "0") == 0);
check_success(m_be_glsl->set_option("glsl_place_uniforms_into_ssbo", "on") == 0);
#else
check_success(m_be_glsl->set_option("glsl_max_const_data", "1024") == 0);
check_success(m_be_glsl->set_option("glsl_place_uniforms_into_ssbo", "off") == 0);
#endif
#ifdef REMAP_NOISE_FUNCTIONS
check_success(m_be_glsl->set_option("glsl_remap_functions",
"_ZN4base12perlin_noiseEu6float4=noise_float4"
",_ZN4base12worley_noiseEu6float3fi=noise_worley"
",_ZN4base8mi_noiseEu6float3=noise_mi_float3"
",_ZN4base8mi_noiseEu4int3=noise_mi_int3") == 0);
#endif
m_link_unit = m_be_glsl->create_link_unit(m_transaction.get(), m_context.get());
add_ue4_material_expressions(m_transaction.get(), m_cm.get());
m_target_code = m_be_glsl->translate_link_unit(m_link_unit.get(), m_context.get());
check_success(print_messages(m_context.get()));
m_code = m_target_code->get_code();
#ifdef REMAP_NOISE_FUNCTIONS
m_code += read_text_file(get_executable_folder() + "/" + "noise_no_lut.glsl");
#endif
m_code += m_defaults;
add_texture_runtime();
}
virtual ~Mdl_ue4_glsl()
{
}
virtual void add_color_default(
{
m_defaults += get_color_default("get_" + parameter_name, value);
}
virtual void add_float_default(
{
m_defaults += get_float_default("get_" + parameter_name, value);
}
virtual void add_normal_default(const std::string& parameter_name)
{
m_defaults += get_normal_default("get_" + parameter_name);
}
const std::string& parameter_name, const std::string& path)
{
const std::string fct_name = "get_" + parameter_name;
mi::Sint32 result = m_link_unit->add_material_expression(
m_cm.get(), path.c_str(), fct_name.c_str(), m_context.get());
print_messages(m_context.get());
return result;
}
virtual const char* get_code() const
{
return m_code.c_str();
}
virtual mi::Size get_texture_count()
const
{
return m_target_code->get_texture_count();
}
{
load_image_from_db(
m_image_api.get(), m_transaction.get(), m_target_code->get_texture(index));
if (!canvas)
return nullptr;
canvas->retain();
}
virtual mi::Size get_ro_data_segment_count()
const
{
return m_target_code->get_ro_data_segment_count();
}
virtual const char* get_ro_data_segment_data(
mi::Size index)
const
{
return m_target_code->get_ro_data_segment_data(index);
}
{
return m_target_code->get_ro_data_segment_size(index);
}
virtual const char* get_ro_data_segment_name(
mi::Size index)
const
{
return m_target_code->get_ro_data_segment_name(index);
}
private:
std::string m_defaults;
std::string m_code;
m_cm;
m_context;
void add_texture_runtime()
{
if (m_target_code->get_texture_count() == 0)
return;
m_code += "\n";
m_code += "#define MAX_TEXTURES " + to_string(
m_target_code->get_texture_count() - 1) + "\n";
m_code += "uniform sampler2D mdl_textures_2d[MAX_TEXTURES];\n";
m_code +=
"int tex_width_2d(int tex, ivec2 uv_tile, float frame) {\n"
" if (tex == 0) return 0; // invalid texture\n"
" if (tex > MAX_TEXTURES) return 0;\n"
" return textureSize(mdl_textures_2d[tex-1], 0).x;\n"
"}\n"
"int tex_height_2d(int tex, ivec2 uv_tile, float frame) {\n"
" if (tex == 0) return 0; // invalid texture\n"
" if (tex > MAX_TEXTURES) return 0;\n"
" return textureSize(mdl_textures_2d[tex-1], 0).y;\n"
"}\n"
"vec3 tex_lookup_float3_2d("
"int tex, vec2 coord, int wrap_u, int wrap_v, vec2 crop_u, vec2 crop_v, float frame)\n"
"{\n"
" if (tex == 0) return vec3(0);\n"
" if (tex > MAX_TEXTURES) return vec3(0);\n"
" return texture(mdl_textures_2d[tex-1], coord).rgb;\n"
"}\n"
"vec3 tex_lookup_color_2d("
"int tex, vec2 coord, int wrap_u, int wrap_v, vec2 crop_u, vec2 crop_v, float frame)\n"
"{\n"
" if (tex == 0) return vec3(0);\n"
" if (tex > MAX_TEXTURES) return vec3(0);\n"
" return texture(mdl_textures_2d[tex-1], coord).rgb;\n"
"}\n"
"vec3 tex_texel_color_2d(int tex, ivec2 coord, ivec2 uv_tile, float frame)\n"
"{\n"
" if (tex == 0) return vec3(0);\n"
" if (tex > MAX_TEXTURES) return vec3(0);\n"
" return texelFetch(mdl_textures_2d[tex-1], coord, 0).rgb;\n"
"}\n";
std::string w_switch, h_switch;
for (
mi::Size i = 1; i < m_target_code->get_texture_count(); ++i)
{
m_target_code->get_texture(i)));
w_switch += " case " + to_string(i) + "u: return "
+ to_string(image->resolution_x(0, 0, 0)) + ";\n";
h_switch += " case " + to_string(i) + "u: return "
+ to_string(image->resolution_y(0, 0, 0)) + ";\n";
}
m_code +=
"int tex_width(uint tex, ivec2 uv_tile, float frame)"
"{\n"
" switch (tex) {\n"
" case 0u: return 0;\n"
+ w_switch +
" }\n"
"}\n"
"int tex_height(uint tex, ivec2 uv_tile, float frame)"
"{\n"
" switch (tex) {\n"
" case 0u: return 0;\n"
+ h_switch +
" }\n"
"}\n";
return;
}
};
struct Material_parameter
{
std::string pixel_type;
std::string bake_path;
Material_parameter(const std::string& pixel_type)
: canvas_index(~0u), pixel_type(pixel_type) { }
Material_parameter()
: canvas_index(~0u) { }
};
class Mdl_ue4_baker : public Mdl_ue4
{
public:
Mdl_ue4_baker(
const Mdl_sdk_state& state,
int baking_resolution_x, int baking_resolution_y)
: m_sdk_state(state)
, m_baking_resolution_x(baking_resolution_x)
, m_baking_resolution_y(baking_resolution_y)
{
m_material_parameters["base_color"] = Material_parameter("Rgb_fp");
m_material_parameters["metallic"] = Material_parameter("Float32");
m_material_parameters["specular"] = Material_parameter("Float32");
m_material_parameters["roughness"] = Material_parameter("Float32");
m_material_parameters["normal"] = Material_parameter("Float32<3>");
m_material_parameters["clearcoat_color"] = Material_parameter("Rgb_fp");
m_material_parameters["clearcoat_weight"] = Material_parameter("Float32");
m_material_parameters["clearcoat_roughness"] = Material_parameter("Float32");
m_material_parameters["clearcoat_normal"] = Material_parameter("Float32<3>");
add_ue4_material_expressions(m_sdk_state.transaction.get(), cm);
bake_expressions();
generate_glsl();
}
virtual ~Mdl_ue4_baker() { }
virtual void add_color_default(
{
Material_parameter_map::iterator param = m_material_parameters.find(parameter_name);
if (param == m_material_parameters.end())
return;
param->second.value = create_value(m_sdk_state.transaction.get(), "Color", value);
}
virtual void add_float_default(
{
Material_parameter_map::iterator param = m_material_parameters.find(parameter_name);
if (param == m_material_parameters.end())
return;
param->second.value = create_value(m_sdk_state.transaction.get(), "Float32", value);
}
virtual void add_normal_default(const std::string& parameter_name)
{
Material_parameter_map::iterator param = m_material_parameters.find(parameter_name);
if (param == m_material_parameters.end())
return;
param->second.bake_path = "geometry.normal";
}
const std::string& parameter_name, const std::string& path)
{
Material_parameter_map::iterator param = m_material_parameters.find(parameter_name);
if (param == m_material_parameters.end())
return -1;
param->second.bake_path = path;
return 0;
}
virtual const char* get_code() const
{
return m_code.c_str();
}
virtual mi::Size get_texture_count()
const
{
return m_textures.size() ? m_textures.size() + 1 : 0;
}
{
if (index < 1 || index > m_textures.size())
return nullptr;
m_textures[index-1]->
retain();
return m_textures[index-1].get();
}
private:
std::string m_code;
typedef std::map<std::string, Material_parameter> Material_parameter_map;
std::map<std::string, Material_parameter> m_material_parameters;
std::vector <mi::base::Handle<mi::neuraylib::ICanvas> > m_textures;
Mdl_sdk_state m_sdk_state;
int m_baking_resolution_x;
int m_baking_resolution_y;
void bake_expressions()
{
for (Material_parameter_map::iterator it = m_material_parameters.begin();
it != m_material_parameters.end(); ++it )
{
Material_parameter& param = it->second;
if (param.bake_path.empty())
continue;
check_success(baker.is_valid_interface());
if (baker->is_uniform())
{
if (param.pixel_type == "Rgb_fp")
param.value = create_value(
m_sdk_state.transaction.get(),
"Color",
mi::Color(0.f));
else if (param.pixel_type == "Float32")
param.value = create_value(m_sdk_state.transaction.get(), "Float32", 0.f);
else if (param.pixel_type == "Float32<3>")
param.value = create_value(
m_sdk_state.transaction.get(),
"Float32<3>",
mi::Float32_3(0.f));
else
{
std::cout << "Ignoring unsupported value type '" << param.pixel_type
<< "'" << std::endl;
continue;
}
mi::Sint32 result = baker->bake_constant(param.value.get());
check_success(result == 0);
}
else
{
m_baking_resolution_x, m_baking_resolution_y));
check_success(result == 0);
m_textures.push_back(canvas);
param.canvas_index = m_textures.size() - 1;
}
}
}
void generate_glsl()
{
m_code +=
"#version 330 core\n"
"struct State {\n"
" vec3 normal;\n"
" vec3 geom_normal;\n"
" vec3 position;\n"
" float animation_time;\n"
" vec3 text_coords[1];\n"
" vec3 tangent_u[1];\n"
" vec3 tangent_v[1];\n"
" int ro_data_segment_offset;\n"
" mat4 world_to_object;\n"
" mat4 object_to_world;\n"
" int object_id;\n"
" float meters_per_scene_unit;\n"
" int arg_block_offset;\n"
"};\n\n";
if (m_textures.size())
{
m_code += "#define MAX_TEXTURES " + to_string(
m_textures.size()) + "\n";
m_code += "uniform sampler2D mdl_textures_2d[MAX_TEXTURES];\n";
}
for (Material_parameter_map::iterator it = m_material_parameters.begin();
it != m_material_parameters.end(); ++it)
{
Material_parameter& param = it->second;
if (param.canvas_index == ~0u)
{
if (param.pixel_type == "Rgb_fp")
{
m_code += get_color_default("get_" + it->first, v);
}
else if (param.pixel_type == "Float32")
{
m_code += get_float_default("get_" + it->first, v);
}
else if (param.pixel_type == "Float32<3>")
{
m_code += get_normal_default("get_" + it->first);
}
else
check_success(!"unsupported pixel type");
}
else
{
if (param.pixel_type == "Rgb_fp")
{
m_code += get_vector_texture_access("get_" + it->first, param.canvas_index);
}
else if (param.pixel_type == "Float32")
{
m_code += get_float_texture_access("get_" + it->first, param.canvas_index);
}
else if (param.pixel_type == "Float32<3>")
{
m_code += get_normal_texture_access("get_" + it->first, param.canvas_index);
}
else
check_success(!"unsupported pixel type");
}
}
m_code += "\n";
}
};
struct Vertex {
Vertex(
: position(p), normal(n), tangent(t), binormal(b), tex_coord(uv)
{ }
};
static void handle_glfw_error(int error_code, const char* description)
{
std::cerr << "GLFW error (code: " << error_code << "): \"" << description << "\"\n";
}
static GLFWwindow *init_opengl(unsigned int w, unsigned int h, bool show_window)
{
printf("Setting GLFW err callback ...\n");
glfwSetErrorCallback(handle_glfw_error);
printf("Initializing GLFW ...\n");
check_success(glfwInit());
#ifdef USE_SSBO
printf("Setting GLSL 4.3 version hint ...\n");
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
#else
printf("Setting GLSL 3.3 version hint ...\n");
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
#endif
printf("Setting OpenGL profile hint ...\n");
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
printf("Setting OpenGL forward compatibility hint ...\n");
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
printf("Setting window visibility hint ...\n");
glfwWindowHint(GLFW_VISIBLE, show_window);
printf("Creating GLFW window ...\n");
GLFWwindow *window = glfwCreateWindow(
w, h, "MDL Distilling Example", nullptr, nullptr);
if (!window) {
std::cerr << "Error creating OpenGL window!" << std::endl;
terminate();
}
printf("Attach context to window ...\n");
glfwMakeContextCurrent(window);
printf("Initializing GLEW ...\n");
GLenum res = glewInit();
if (res != GLEW_OK) {
std::cerr << "GLEW error: " << glewGetErrorString(res) << std::endl;
terminate();
}
printf("Enabling depth test ...\n");
glEnable(GL_DEPTH_TEST);
printf("Setting texture cube map seamless ...\n");
glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS);
printf("Enable vsync ...\n");
glfwSwapInterval(1);
printf("Checking for OpenGL errors ...\n");
check_gl_success();
return window;
}
static void create_vertex_array(
GLuint& vao, GLuint& vbo, const GLvoid* vertices, GLsizei vertices_size)
{
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);
glGenBuffers(1, &vbo);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, vertices_size, vertices, GL_STATIC_DRAW);
}
static void create_index_buffer(GLuint& ebo, const GLvoid* indices, GLsizei indices_size)
{
glGenBuffers(1, &ebo);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices_size, indices, GL_STATIC_DRAW);
}
static GLuint create_gl_texture(GLenum target, GLsizei w, GLsizei h, GLenum format=GL_RGB,
const void* data=nullptr,
GLenum min_filter = GL_LINEAR,
GLenum mag_filter = GL_LINEAR,
GLenum wrap_s = GL_CLAMP_TO_EDGE,
GLenum wrap_t = GL_CLAMP_TO_EDGE,
GLenum wrap_r = GL_CLAMP_TO_EDGE,
int levels = 0)
{
GLuint id;
glGenTextures(1, &id);
glBindTexture(target, id);
GLenum int_format;
switch (format) {
case GL_RED:
int_format = GL_R32F;
break;
case GL_RGB:
int_format = GL_RGB32F;
break;
default:
case GL_RGBA:
int_format = GL_RGBA32F;
break;
}
if(target == GL_TEXTURE_CUBE_MAP)
for (unsigned int i = 0; i < 6; ++i)
{
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, int_format,
w, h, 0, format, GL_FLOAT, data);
}
else {
if (levels)
{
glTexStorage2D(GL_TEXTURE_2D, levels, int_format, w, h);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, w, h, format, GL_FLOAT, data);
glGenerateMipmap(GL_TEXTURE_2D);
}
else
glTexImage2D(target, 0, int_format,
w, h, 0, format, GL_FLOAT, data);
}
glTexParameteri(target, GL_TEXTURE_WRAP_S, wrap_s);
glTexParameteri(target, GL_TEXTURE_WRAP_T, wrap_t);
if (target == GL_TEXTURE_CUBE_MAP)
glTexParameteri(target, GL_TEXTURE_WRAP_R, wrap_r);
glTexParameteri(target, GL_TEXTURE_MIN_FILTER, min_filter);
glTexParameteri(target, GL_TEXTURE_MAG_FILTER, mag_filter);
check_gl_success();
return id;
}
static GLenum get_gl_format(const char *pixel_type)
{
if (strcmp(pixel_type, "Float32") == 0)
return GL_RED;
else if (strcmp(pixel_type, "Color") == 0)
return GL_RGBA;
else
return GL_RGB;
}
static GLuint load_gl_texture(
{
if (!canvas)
return static_cast<GLuint>(-1);
const GLenum format = get_gl_format(canvas->get_type());
return create_gl_texture(target, canvas->get_resolution_x(), canvas->get_resolution_y(),
format,
tile->get_data(),
levels ? GL_LINEAR_MIPMAP_LINEAR : GL_LINEAR,
GL_LINEAR, GL_REPEAT, GL_REPEAT, GL_REPEAT, levels);
}
static GLuint create_shader_program(
const std::string& vertex_program,
const std::string& fragment_program)
{
GLint success;
GLuint program = glCreateProgram();
add_shader(GL_VERTEX_SHADER, vertex_program, program);
add_shader(GL_FRAGMENT_SHADER, fragment_program, program);
glLinkProgram(program);
glGetProgramiv(program, GL_LINK_STATUS, &success);
if (!success) {
dump_program_info(program, "Error linking the shader program: ");
terminate();
}
check_gl_success();
return program;
}
class Shader_program
{
public:
Shader_program(const std::string& vertex_shader_file, const std::string& fragment_shader_file)
{
m_program = create_shader_program(
read_text_file(vertex_shader_file),
read_text_file(fragment_shader_file));
}
Shader_program() : m_program(-1) {
}
virtual ~Shader_program()
{
glDeleteProgram(m_program);
}
virtual void bind_textures() const
{
}
void make_current() const
{
glUseProgram(m_program);
}
void set_float(
const char* argument,
mi::Float32 value)
{
GLint location = glGetUniformLocation(m_program, argument);
if (location >= 0)
glUniform1f(location, value);
}
void set_int(
const char* argument,
mi::Uint32 value)
{
GLint location = glGetUniformLocation(m_program, argument);
if (location >= 0)
glUniform1i(location, value);
}
void set_vector3(
const char* argument,
const mi::Float32_3& value)
{
GLint location = glGetUniformLocation(m_program, argument);
if (location >= 0)
glUniform3fv(location, 1, value.
begin());
}
{
GLint location = glGetUniformLocation(m_program, argument);
if(location >= 0)
glUniformMatrix4fv(location, 1,
GL_FALSE, value.
begin());
}
void set_vertex_attrib_float(const char* arg, GLsizei size, GLsizei stride, GLsizei offset)
{
GLint location = glGetAttribLocation(m_program, arg);
if (location < 0)
return;
glEnableVertexAttribArray(location);
glVertexAttribPointer(
location, size, GL_FLOAT, GL_FALSE, stride,
reinterpret_cast<const void*>(size_t(offset)));
}
protected:
GLuint m_program;
private:
Shader_program(const Shader_program& other) = delete;
Shader_program& operator=(const Shader_program&) = delete;
};
class Mdl_pbr_shader : public Shader_program
{
public:
Mdl_pbr_shader(
const Mdl_sdk_state& state,
Mdl_ue4* mdl_ue4,
GLuint irradiance_map,
GLuint refl_map,
GLuint brdf_lut_map)
: m_irradiance_map(irradiance_map)
, m_refl_map(refl_map)
, m_brdf_lut_map(brdf_lut_map)
{
std::string fragment_code = mdl_ue4->get_code();
fragment_code += read_text_file(
mi::examples::io::get_executable_folder() + "/" + "example_distilling_glsl.frag");
#ifdef DUMP_GLSL
std::fstream file;
file.open("glsl_dump.frag", std::fstream::out);
file << fragment_code;
file.close();
#endif
m_program = create_shader_program(read_text_file(
mi::examples::io::get_executable_folder() + "/" + "example_distilling_glsl.vert"),
fragment_code);
make_current();
set_int("irradiance_map", 0);
set_int("refl_map", 1);
set_int("brdf_lut", 2);
set_mdl_readonly_data(mdl_ue4);
for (
mi::Size i = 1; i < mdl_ue4->get_texture_count(); ++i)
{
m_mdl_textures.push_back(
load_gl_texture(GL_TEXTURE_2D,
mi::base::make_handle<const mi::neuraylib::ICanvas>(mdl_ue4->get_texture(i))));
std::string name = "mdl_textures_2d[" + to_string(i-1) + "]";
}
delete mdl_ue4;
}
virtual ~Mdl_pbr_shader()
{
if (m_buffer_objects.size() > 0)
glDeleteBuffers(GLsizei(m_buffer_objects.size()), &m_buffer_objects[0]);
if (m_mdl_textures.size() > 0)
glDeleteTextures(GLsizei(m_mdl_textures.size()), &m_mdl_textures[0]);
check_gl_success();
}
virtual void bind_textures() const
{
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_CUBE_MAP, m_irradiance_map);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_CUBE_MAP, m_refl_map);
glActiveTexture(GL_TEXTURE2);
glBindTexture(GL_TEXTURE_2D, m_brdf_lut_map);
for (size_t i=0; i<m_mdl_textures.size(); ++i)
{
glActiveTexture(GLenum(GL_TEXTURE0 + i + 3));
glBindTexture(GL_TEXTURE_2D, m_mdl_textures[i]);
}
}
private:
void set_mdl_readonly_data(
const Mdl_ue4* mdl_ue4)
{
mi::Size num_uniforms = mdl_ue4->get_ro_data_segment_count();
if (num_uniforms == 0)
return;
#ifdef USE_SSBO
GLuint next_storage_block_binding = 0u;
m_buffer_objects.resize(num_uniforms);
glGenBuffers(GLsizei(num_uniforms), &m_buffer_objects[0]);
for (
mi::Size i = 0; i < num_uniforms; ++i) {
mi::Size segment_size = mdl_ue4->get_ro_data_segment_size(i);
char const* segment_data = mdl_ue4->get_ro_data_segment_data(i);
#ifdef DUMP_GLSL
std::cout << "Dump ro segment data " << i << " \""
<< mdl_ue4->get_ro_data_segment_name(i) << "\" (size = "
<< segment_size << "):\n" << std::hex;
for (int j = 0; j < 16 && j < segment_size; ++j) {
std::cout << "0x" << (unsigned int)(unsigned char) segment_data[j] << ", ";
}
std::cout << std::dec << std::endl;
#endif
glBindBuffer(GL_SHADER_STORAGE_BUFFER, m_buffer_objects[i]);
glBufferData(
GL_SHADER_STORAGE_BUFFER, GLsizeiptr(segment_size), segment_data, GL_STATIC_DRAW);
GLuint block_index = glGetProgramResourceIndex(
m_program, GL_SHADER_STORAGE_BLOCK,
mdl_ue4->get_ro_data_segment_name(i));
glShaderStorageBlockBinding(m_program, block_index, next_storage_block_binding);
glBindBufferBase(
GL_SHADER_STORAGE_BUFFER,
next_storage_block_binding,
m_buffer_objects[i]);
++next_storage_block_binding;
check_gl_success();
}
#else
std::vector<char const*> uniform_names;
for (
mi::Size i = 0; i < num_uniforms; ++i) {
#ifdef DUMP_GLSL
mi::Size segment_size = mdl_ue4->get_ro_data_segment_size(i);
const char* segment_data = mdl_ue4->get_ro_data_segment_data(i);
std::cout << "Dump ro segment data " << i << " \""
<< mdl_ue4->get_ro_data_segment_name(i) << "\" (size = "
<< segment_size << "):\n" << std::hex;
for (int i = 0; i < 16 && i < segment_size; ++i) {
std::cout << "0x" << (unsigned int)(unsigned char)segment_data[i] << ", ";
}
std::cout << std::dec << std::endl;
#endif
uniform_names.push_back(mdl_ue4->get_ro_data_segment_name(i));
}
std::vector<GLuint> uniform_indices(num_uniforms, 0);
glGetUniformIndices(
m_program, GLsizei(num_uniforms), &uniform_names[0], &uniform_indices[0]);
for (
mi::Size i = 0; i < num_uniforms; ++i) {
if (uniform_indices[i] == GL_INVALID_INDEX)
continue;
GLint uniform_type = 0;
GLuint index = GLuint(uniform_indices[i]);
glGetActiveUniformsiv(m_program, 1, &index, GL_UNIFORM_TYPE, &uniform_type);
#ifdef DUMP_GLSL
std::cout << "Uniform type of " << uniform_names[i]
<< ": 0x" << std::hex << uniform_type << std::dec << std::endl;
#endif
mi::Size segment_size = mdl_ue4->get_ro_data_segment_size(i);
const char* segment_data = mdl_ue4->get_ro_data_segment_data(i);
GLint uniform_location = glGetUniformLocation(m_program, uniform_names[i]);
switch (uniform_type) {
#define CASE_TYPE_BOOL(type, func, num) \
case type: { \
GLint *buf = new GLint[segment_size]; \
for (mi::Size j = 0; j < segment_size; ++j) \
buf[j] = GLint(segment_data[j]); \
func(uniform_location, GLsizei(segment_size / num), buf); \
delete[] buf; \
break; \
}
CASE_TYPE_BOOL(GL_BOOL, glUniform1iv, 1)
CASE_TYPE_BOOL(GL_BOOL_VEC2, glUniform2iv, 2)
CASE_TYPE_BOOL(GL_BOOL_VEC3, glUniform3iv, 3)
CASE_TYPE_BOOL(GL_BOOL_VEC4, glUniform4iv, 4)
#define CASE_TYPE(type, func, num, elemtype) \
case type: \
func(uniform_location, GLsizei(segment_size / (num * sizeof(elemtype))), \
(const elemtype*)segment_data); \
break
CASE_TYPE(GL_INT, glUniform1iv, 1, GLint);
CASE_TYPE(GL_INT_VEC2, glUniform2iv, 2, GLint);
CASE_TYPE(GL_INT_VEC3, glUniform3iv, 3, GLint);
CASE_TYPE(GL_INT_VEC4, glUniform4iv, 4, GLint);
CASE_TYPE(GL_FLOAT, glUniform1fv, 1, GLfloat);
CASE_TYPE(GL_FLOAT_VEC2, glUniform2fv, 2, GLfloat);
CASE_TYPE(GL_FLOAT_VEC3, glUniform3fv, 3, GLfloat);
CASE_TYPE(GL_FLOAT_VEC4, glUniform4fv, 4, GLfloat);
CASE_TYPE(GL_DOUBLE, glUniform1dv, 1, GLdouble);
CASE_TYPE(GL_DOUBLE_VEC2, glUniform2dv, 2, GLdouble);
CASE_TYPE(GL_DOUBLE_VEC3, glUniform3dv, 3, GLdouble);
CASE_TYPE(GL_DOUBLE_VEC4, glUniform4dv, 4, GLdouble);
#define CASE_TYPE_MAT(type, func, num, elemtype) \
case type: \
func(uniform_location, GLsizei(segment_size / (num * sizeof(elemtype))), \
false, (const elemtype*)segment_data); \
break
CASE_TYPE_MAT(GL_FLOAT_MAT2_ARB, glUniformMatrix2fv, 4, GLfloat);
CASE_TYPE_MAT(GL_FLOAT_MAT2x3, glUniformMatrix2x3fv, 6, GLfloat);
CASE_TYPE_MAT(GL_FLOAT_MAT3x2, glUniformMatrix3x2fv, 6, GLfloat);
CASE_TYPE_MAT(GL_FLOAT_MAT2x4, glUniformMatrix2x4fv, 8, GLfloat);
CASE_TYPE_MAT(GL_FLOAT_MAT4x2, glUniformMatrix4x2fv, 8, GLfloat);
CASE_TYPE_MAT(GL_FLOAT_MAT3_ARB, glUniformMatrix3fv, 9, GLfloat);
CASE_TYPE_MAT(GL_FLOAT_MAT3x4, glUniformMatrix3x4fv, 12, GLfloat);
CASE_TYPE_MAT(GL_FLOAT_MAT4x3, glUniformMatrix4x3fv, 12, GLfloat);
CASE_TYPE_MAT(GL_FLOAT_MAT4_ARB, glUniformMatrix4fv, 16, GLfloat);
CASE_TYPE_MAT(GL_DOUBLE_MAT2, glUniformMatrix2dv, 4, GLdouble);
CASE_TYPE_MAT(GL_DOUBLE_MAT2x3, glUniformMatrix2x3dv, 6, GLdouble);
CASE_TYPE_MAT(GL_DOUBLE_MAT3x2, glUniformMatrix3x2dv, 6, GLdouble);
CASE_TYPE_MAT(GL_DOUBLE_MAT2x4, glUniformMatrix2x4dv, 8, GLdouble);
CASE_TYPE_MAT(GL_DOUBLE_MAT4x2, glUniformMatrix4x2dv, 8, GLdouble);
CASE_TYPE_MAT(GL_DOUBLE_MAT3, glUniformMatrix3dv, 9, GLdouble);
CASE_TYPE_MAT(GL_DOUBLE_MAT3x4, glUniformMatrix3x4dv, 12, GLdouble);
CASE_TYPE_MAT(GL_DOUBLE_MAT4x3, glUniformMatrix4x3dv, 12, GLdouble);
CASE_TYPE_MAT(GL_DOUBLE_MAT4, glUniformMatrix4dv, 16, GLdouble);
default:
std::cerr << "Unsupported uniform type: 0x"
<< std::hex << uniform_type << std::dec << std::endl;
terminate();
break;
}
check_gl_success();
}
#endif
}
private:
GLuint m_irradiance_map;
GLuint m_refl_map;
GLuint m_brdf_lut_map;
std::vector<GLuint> m_mdl_textures;
std::vector<GLuint> m_buffer_objects;
};
class Mesh
{
public:
virtual void draw() = 0;
virtual ~Mesh() {}
virtual void bind_shader(Shader_program*)
{
}
};
class Sphere : public Mesh
{
public:
Sphere(const float radius, const unsigned int slices, const unsigned int stacks)
{
std::vector<unsigned int> indices;
std::vector<Vertex> vertices;
const float step_phi = (float) (2.0 * M_PI / slices);
const float step_theta = (float) (M_PI / stacks);
for (unsigned int i = 0; i <= stacks; ++i) {
const float theta = step_theta * float(i);
const float sin_t =
sin(theta);
const float cos_t =
cos(theta);
for (unsigned int j = 0; j <= slices; ++j) {
const float phi = step_phi * float(j);
const float sin_p =
sin(phi);
const float cos_p =
cos(phi);
2.0f * float(j) / float(slices), 1.0f - float(i) / float(stacks));
vertices.push_back(Vertex(p * radius, p, tangent_u, tangent_v, uv));
}
}
for (unsigned int i = 0; i < stacks; ++i) {
for (unsigned int j = 0; j < slices; ++j) {
const unsigned int p0 = i * (slices + 1) + j;
const unsigned int p1 = p0 + 1;
const unsigned int p2 = p0 + slices + 1;
const unsigned int p3 = p2 + 1;
indices.push_back(p0);
indices.push_back(p1);
indices.push_back(p2);
indices.push_back(p1);
indices.push_back(p3);
indices.push_back(p2);
}
}
create_vertex_array(m_vao, m_vbo, vertices.data(),
(GLsizei) (vertices.size() * sizeof(Vertex)));
create_index_buffer(m_ebo, indices.data(), (GLsizei) (
indices.size() * sizeof(unsigned int)));
m_nindices = (GLuint) indices.size();
check_gl_success();
}
virtual ~Sphere()
{
glDeleteVertexArrays(1, &m_vao);
glDeleteBuffers(1, &m_vbo);
glDeleteBuffers(1, &m_ebo);
}
virtual void bind_shader(Shader_program* program)
{
glBindBuffer(GL_ARRAY_BUFFER, m_vbo);
program->make_current();
program->set_vertex_attrib_float(
"Position", 3, sizeof(Vertex), 0);
program->set_vertex_attrib_float(
program->set_vertex_attrib_float(
program->set_vertex_attrib_float(
program->set_vertex_attrib_float(
}
virtual void draw()
{
glBindVertexArray(m_vao);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_ebo);
glDrawElements(
GL_TRIANGLES,
m_nindices,
GL_UNSIGNED_INT,
(void*)0
);
}
private:
GLuint m_vbo, m_vao, m_ebo;
GLuint m_nindices;
};
class Screen_aligned_quad : public Mesh
{
public:
Screen_aligned_quad()
{
static const float vertices1[] =
{
-1.f, -1.f, 1.0f,
1.f, -1.f, 1.0f,
-1.f, 1.f, 1.0f,
1.f, -1.f, 1.0f,
1.f, 1.f, 1.0f,
-1.f, 1.f, 1.0f
};
create_vertex_array(m_vao, m_vbo, vertices1, sizeof(vertices1));
check_gl_success();
}
virtual void bind_shader(Shader_program* program)
{
program->make_current();
program->set_vertex_attrib_float("Position", 3, sizeof(float) * 3, 0);
}
virtual void draw()
{
glDisable(GL_DEPTH_TEST);
glBindVertexArray(m_vao);
glDrawArrays(GL_TRIANGLES, 0, 36);
glEnable(GL_DEPTH_TEST);
}
virtual ~Screen_aligned_quad()
{
glDeleteVertexArrays(1, &m_vao);
glDeleteBuffers(1, &m_vbo);
}
private:
GLuint m_vao;
GLuint m_vbo;
};
{
return v * 3.1415926 / 180.;
}
)
{
x_scale = y_scale / aspect_ratio,
frustum_length = far_plane - near_plane;
p.xx = x_scale;
p.yy = y_scale;
p.zz = -((far_plane + near_plane) / frustum_length);
p.wz = -((2 * near_plane * far_plane) / frustum_length);
p.zw = -1;
return p;
}
static GLuint prefilter_glossy(GLsizei w, GLsizei h, GLuint env_tex_id, GLuint env_accel_tex_id)
{
unsigned int fbo, rbo;
glGenFramebuffers(1, &fbo);
glGenRenderbuffers(1, &rbo);
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
unsigned int cubemap_id = create_gl_texture(
GL_TEXTURE_CUBE_MAP, w, h, GL_RGB, nullptr, GL_LINEAR_MIPMAP_LINEAR);
glGenerateMipmap(GL_TEXTURE_CUBE_MAP);
Shader_program program(
mi::examples::io::get_executable_folder() + "/" + "screen_aligned_quad.vert",
mi::examples::io::get_executable_folder() + "/" + "prefilter_glossy.frag");
Screen_aligned_quad quad;
quad.bind_shader(&program);
program.make_current();
program.set_matrix("inv_proj", proj);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, env_tex_id);
program.set_int("env_tex", 0);
if (env_accel_tex_id) {
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, env_accel_tex_id);
program.set_int("env_accel_tex", 1);
}
for (unsigned int i = 0; i < 6; ++i)
const unsigned int miplevel_count = 5;
for (unsigned int mip = 0; mip < miplevel_count; ++mip)
{
const unsigned int mip_w = (
unsigned int) (w *
std::pow(0.5, mip));
const unsigned int mip_h = (
unsigned int) (h *
std::pow(0.5, mip));
glBindRenderbuffer(GL_RENDERBUFFER, rbo);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, mip_w, mip_h);
glViewport(0, 0, mip_w, mip_h);
float roughness = (float) mip / (float) (miplevel_count - 1);
program.set_float("roughness", roughness);
for (unsigned int i = 0; i < 6; ++i)
{
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, cubemap_id, mip);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
program.set_matrix("inv_mv", mv[i]);
quad.draw();
}
}
glBindFramebuffer(GL_FRAMEBUFFER, 0);
return cubemap_id;
}
static GLuint prefilter_diffuse(GLsizei w, GLsizei h, GLuint env_tex_id, GLuint env_accel_tex_id)
{
unsigned int fbo, rbo;
glGenFramebuffers(1, &fbo);
glGenRenderbuffers(1, &rbo);
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
glBindRenderbuffer(GL_RENDERBUFFER, rbo);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, w, h);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, rbo);
unsigned int cubemap_id = create_gl_texture(GL_TEXTURE_CUBE_MAP, w, h);
Shader_program program(
mi::examples::io::get_executable_folder() + "/" + "screen_aligned_quad.vert",
mi::examples::io::get_executable_folder() + "/" + "prefilter_diffuse.frag");
Screen_aligned_quad quad;
quad.bind_shader(&program);
program.make_current();
program.set_matrix("inv_proj", proj);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, env_tex_id);
program.set_int("env_tex", 0);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, env_accel_tex_id);
program.set_int("env_accel_tex", 1);
glViewport(0, 0, w, h);
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
for (unsigned int i = 0; i < 6; ++i)
{
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, cubemap_id, 0);
GLenum DrawBuffers[1] = {GL_COLOR_ATTACHMENT0};
glDrawBuffers(1, DrawBuffers);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
program.set_matrix("inv_mv", mv[i]);
quad.draw();
}
glBindFramebuffer(GL_FRAMEBUFFER, 0);
return cubemap_id;
}
GLuint create_offscreen_render_target(GLsizei w, GLsizei h, GLuint& out_fb, GLuint& out_rb)
{
unsigned int tex_id = create_gl_texture(GL_TEXTURE_2D, w, h);
glGenFramebuffers(1, &out_fb);
glGenRenderbuffers(1, &out_rb);
glBindFramebuffer(GL_FRAMEBUFFER, out_fb);
glBindRenderbuffer(GL_RENDERBUFFER, out_rb);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, w, h);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, out_rb);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tex_id, 0);
return tex_id;
}
GLuint integrate_brdf(GLsizei w, GLsizei h)
{
Shader_program program(
mi::examples::io::get_executable_folder() + "/" + "integrate_brdf.vert",
mi::examples::io::get_executable_folder() + "/" + "integrate_brdf.frag");
Screen_aligned_quad quad;
GLuint fbo=0, rbo=0;
GLuint tex_id = create_offscreen_render_target(w, h, fbo, rbo);
glViewport(0, 0, w, h);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
quad.bind_shader(&program);
quad.draw();
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glDeleteRenderbuffers(1, &rbo);
glDeleteFramebuffers(1, &fbo);
return tex_id;
}
struct Window_context
{
unsigned int width, height;
bool moving;
double move_start_x, move_start_y;
double move_dx, move_dy;
int zoom;
int material;
bool bake;
float exposure;
bool event;
Window_context()
: width(1024), height(1024)
, moving(false)
, move_start_x(0.f), move_start_y(0.f)
, move_dx(0.f), move_dy(0.f)
, zoom(0)
, material(0)
, bake(true)
, exposure(0.0f)
, event(false)
{
}
};
static void handle_scroll(GLFWwindow *window, double , double yoffset)
{
Window_context *ctx = static_cast<Window_context*>(glfwGetWindowUserPointer(window));
if (yoffset > 0.0) {
ctx->zoom++;
ctx->event = true;
}
else if (yoffset < 0.0) {
ctx->zoom--;
ctx->event = true;
}
}
void handle_key(GLFWwindow *window, int key, int , int action, int )
{
Window_context *ctx = static_cast<Window_context*>(glfwGetWindowUserPointer(window));
if (action == GLFW_PRESS) {
switch (key) {
case GLFW_KEY_ESCAPE:
glfwSetWindowShouldClose(window, GLFW_TRUE);
break;
case GLFW_KEY_RIGHT:
++ctx->material;
ctx->event = true;
break;
case GLFW_KEY_LEFT:
--ctx->material;
ctx->event = true;
break;
case GLFW_KEY_UP:
case GLFW_KEY_DOWN:
ctx->bake = !ctx->bake;
ctx->event = true;
break;
case GLFW_KEY_KP_SUBTRACT:
ctx->exposure --;
ctx->event = true;
break;
case GLFW_KEY_KP_ADD:
ctx->exposure ++;
ctx->event = true;
break;
default:
break;
}
}
}
static void handle_mouse_button(GLFWwindow *window, int button, int action, int )
{
Window_context *ctx = static_cast<Window_context*>(glfwGetWindowUserPointer(window));
if (button == GLFW_MOUSE_BUTTON_LEFT) {
if (action == GLFW_PRESS) {
ctx->moving = true;
glfwGetCursorPos(window, &ctx->move_start_x, &ctx->move_start_y);
}
else
ctx->moving = false;
}
}
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->event = true;
}
}
void handle_framebuffer_size(GLFWwindow* window, int width, int height)
{
Window_context *ctx = static_cast<Window_context*>(
glfwGetWindowUserPointer(window));
ctx->width = width;
ctx->height = height;
ctx->event = true;
glViewport(0, 0, width, height);
}
struct Env_accel {
unsigned int alias;
float q;
float pdf;
};
static float build_alias_map(
const float *data,
const unsigned int size,
Env_accel *accel)
{
float sum = 0.0f;
for (unsigned int i = 0; i < size; ++i)
sum += data[i];
for (unsigned int i = 0; i < size; ++i)
accel[i].q = (static_cast<float>(size) * 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[(accel[i].q < 1.0f) ? (s++) : (--large)] = accel[i].alias = i;
for (s = 0; s < large && large < size; ++s)
{
const unsigned int j = partition_table[s], k = partition_table[large];
accel[j].alias = k;
accel[k].q += accel[j].q - 1.0f;
large = (accel[k].q < 1.0f) ? (large + 1u) : large;
}
free(partition_table);
return sum;
}
static GLuint create_environment_accel_texture(
{
const float *pixels = static_cast<const float *>(tile->get_data());
Env_accel *env_accel = static_cast<Env_accel *>(malloc(rx * ry * sizeof(Env_accel)));
float *importance_data = static_cast<float *>(malloc(rx * ry * sizeof(float)));
float cos_theta0 = 1.0f;
const float step_phi = float(2.0 * M_PI) / float(rx);
const float step_theta = float(M_PI) / float(ry);
for (unsigned int y = 0; y < ry; ++y)
{
const float theta1 = 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 idx3 = idx * 3;
importance_data[idx] =
area * std::max(pixels[idx3], std::max(pixels[idx3 + 1], pixels[idx3 + 2]));
}
}
const float inv_env_integral = 1.0f / build_alias_map(importance_data, rx * ry, env_accel);
free(importance_data);
for (unsigned int i = 0; i < rx * ry; ++i) {
const unsigned int idx3 = i * 3;
env_accel[i].pdf =
std::max(pixels[idx3], std::max(pixels[idx3 + 1], pixels[idx3 + 2])) * inv_env_integral;
}
GLuint tex_id = create_gl_texture(GL_TEXTURE_2D, rx, ry,
GL_RGB, env_accel, GL_NEAREST, GL_NEAREST);
free(env_accel);
return tex_id;
}
void render_scene(
const Mdl_sdk_state& state,
& distilled_materials,
const Options& options)
{
check_success(distilled_materials.size());
Window_context window_context;
window_context.bake = options.bake;
window_context.exposure = options.exposure;
float exposure_scale =
static_cast<float>(
pow(2.0, options.exposure));
printf("Initializing OpenGL ...\n");
GLFWwindow *window = init_opengl(
window_context.width, window_context.height, options.show_window);
if (options.show_window)
{
printf("Setting window user pointer ...\n");
glfwSetWindowUserPointer(window, &window_context);
printf("Setting key callback ...\n");
glfwSetKeyCallback(window, handle_key);
printf("Setting framebuffer size callback ...\n");
glfwSetFramebufferSizeCallback(window, handle_framebuffer_size);
printf("Setting scroll callback ...\n");
glfwSetScrollCallback(window, handle_scroll);
printf("Setting cursor position callback ...\n");
glfwSetCursorPosCallback(window, handle_mouse_pos);
printf("Setting mouse button callback ...\n");
glfwSetMouseButtonCallback(window, handle_mouse_button);
}
printf("Initializing OpenGL done.\n");
printf("Generating maps ...\n");
load_image_from_file(image_api.get(), state.transaction.get(), options.hdrfile.c_str());
GLuint env_accel_tex_id = create_environment_accel_texture(env_tex_canvas);
GLuint env_tex_id = load_gl_texture(GL_TEXTURE_2D, env_tex_canvas);
GLuint iblmap_id = prefilter_diffuse(128, 128, env_tex_id, env_accel_tex_id);
GLuint refmap_id = prefilter_glossy(512, 512, env_tex_id, env_accel_tex_id);
GLuint brdflutid = integrate_brdf(512, 512);
env_tex_canvas = 0;
glDeleteTextures(1, &env_accel_tex_id);
printf("Generating maps done.\n");
glViewport(0, 0, window_context.width, window_context.height);
{
std::vector<Mdl_pbr_shader*> pbr_shaders(distilled_materials.size() * 2);
std::cout << "Generating shader " << window_context.material <<
" in " << (window_context.bake ? "baked" : "GLSL")
<< " mode ..." << std::endl;
int index = window_context.bake ? 0 : 1;
Mdl_ue4* mdl_ue4 = 0;
if (window_context.bake)
mdl_ue4 = new Mdl_ue4_baker(state, distilled_materials[0].get(),
options.baking_resolution_x, options.baking_resolution_y);
else
mdl_ue4 = new Mdl_ue4_glsl(state, distilled_materials[0].get());
Mdl_pbr_shader* sphere_shader = new Mdl_pbr_shader(
state, mdl_ue4,
iblmap_id, refmap_id, brdflutid);
pbr_shaders[0+index] = sphere_shader;
std::cout << "Generating shader done." << std::endl;
Sphere sphere(1.f, 64, 64);
sphere.bind_shader(sphere_shader);
Shader_program env_shader(
mi::examples::io::get_executable_folder() + "/" + "screen_aligned_quad.vert",
mi::examples::io::get_executable_folder() + "/" + "environment_sphere.frag");
Screen_aligned_quad quad;
quad.bind_shader(&env_shader);
float cam_dist = 3.f;
double phi = 0.0f, theta = (M_PI * 0.5);
mv.
lookat(cam_pos, cam_interest, cam_up);
inv_mv.transpose();
projection(
45.f, float(window_context.width) / float(window_context.height), 1.f, 100.f);
inv_proj.invert();
if(options.show_window)
while (!glfwWindowShouldClose(window))
{
if (window_context.event)
{
const int num_materials = int(distilled_materials.size());
window_context.material = window_context.material % num_materials;
if (window_context.material < 0)
window_context.material += num_materials;
index = window_context.bake ? 0 : 1;
sphere_shader =
pbr_shaders[window_context.material * 2 + index];
if (!sphere_shader)
{
std::cout << "Generating shader " << window_context.material <<
" in " << (index ? "GLSL" : "baked" )
<< " mode ..." << std::endl;
if (window_context.bake)
mdl_ue4 = new Mdl_ue4_baker(
state, distilled_materials[window_context.material].get(),
options.baking_resolution_x, options.baking_resolution_y);
else
mdl_ue4 = new Mdl_ue4_glsl(
state, distilled_materials[window_context.material].get());
sphere_shader = new Mdl_pbr_shader(
state, mdl_ue4, iblmap_id, refmap_id, brdflutid);
pbr_shaders[window_context.material * 2 + index] = sphere_shader;
std::cout << "Generating shader done." << std::endl;
}
sphere.bind_shader(sphere_shader);
exposure_scale =
static_cast<float>(
pow(2.0, window_context.exposure));
phi -= window_context.move_dx * 0.001 * M_PI;
theta -= window_context.move_dy * 0.001 * M_PI;
theta = std::max(theta, 0.05 * M_PI);
theta = std::min(theta, 0.95 * M_PI);
window_context.move_dx = window_context.move_dy = 0.0;
cam_pos.x = float(
sin(phi) *
sin(theta));
cam_pos.y = float(
cos(theta));
cam_pos.z = float(
cos(phi) *
sin(theta));
cam_up.x = float(-
sin(phi) *
cos(theta));
cam_up.y = float(
sin(theta));
cam_up.z = float(-
cos(phi) *
cos(theta));
const float dist = float(cam_dist *
pow(0.95,
double(window_context.zoom)));
cam_pos *= dist;
mv.
lookat(cam_pos, cam_interest, cam_up);
inv_mv = mv;
proj = projection(45.f,
float(window_context.width) / float(window_context.height), 1.f, 100.f);
inv_proj = proj;
window_context.event = false;
}
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
env_shader.make_current();
env_shader.set_float("exposure_scale", exposure_scale);
env_shader.set_matrix("inv_mv", inv_mv);
env_shader.set_matrix("inv_proj", inv_proj);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, env_tex_id);
quad.draw();
sphere_shader->make_current();
sphere_shader->set_float("exposure_scale", exposure_scale);
sphere_shader->set_matrix("m_view", mv);
sphere_shader->set_matrix("m_projection", proj);
sphere_shader->set_vector3("cam_position", cam_pos);
sphere_shader->bind_textures();
sphere.draw();
glfwSwapBuffers(window);
glfwPollEvents();
}
else
{
GLuint fbo = 0, rbo = 0;
GLuint fb = create_offscreen_render_target(
window_context.width, window_context.height, fbo, rbo);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
env_shader.make_current();
env_shader.set_float("exposure_scale", exposure_scale);
env_shader.set_matrix("inv_mv", inv_mv);
env_shader.set_matrix("inv_proj", inv_proj);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, env_tex_id);
quad.draw();
sphere_shader->make_current();
sphere_shader->set_float("exposure_scale", exposure_scale);
sphere_shader->set_matrix("m_view", mv);
sphere_shader->set_matrix("m_projection", proj);
sphere_shader->set_vector3("cam_position", cam_pos);
sphere_shader->bind_textures();
sphere.draw();
save_image(state, options.outputfile.c_str(), fb,
window_context.width, window_context.height);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
for (
mi::Size i = 0; i < pbr_shaders.size(); ++i)
delete pbr_shaders[i];
}
glDeleteTextures(1, &env_tex_id);
glDeleteTextures(1, &refmap_id);
glDeleteTextures(1, &iblmap_id);
glDeleteTextures(1, &brdflutid);
check_gl_success();
glfwDestroyWindow(window);
glfwTerminate();
}
const Mdl_sdk_state& state,
const std::string& target_model,
{
check_success(distiller_api);
distiller_api->distill_material(cm, target_model.c_str());
check_success(dm);
return dm;
}
const Mdl_sdk_state& state,
const std::string& material_name)
{
state.mdl_factory->create_execution_context());
std::string module_name, material_simple_name;
if (!mi::examples::mdl::parse_cmd_argument_material_name(
material_name, module_name, material_simple_name, true))
return nullptr;
state.mdl_impexp_api->load_module(state.transaction.get(), module_name.c_str(), context.get());
if (!print_messages(context.get()))
return nullptr;
state.mdl_factory->get_db_module_name(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)
return nullptr;
material_definition->create_function_call(nullptr));
if (!mat_inst)
return nullptr;
mat_inst2->create_compiled_material(
if (!print_messages(context.get()))
return nullptr;
if (!cm)
return nullptr;
return cm.get();
}
static void usage(const char *name)
{
std::cout
<< "usage: " << name << " [options] [<material_name1> ...]\n"
<< "-h print this text\n"
<< "--nowin don't open interactive display\n"
<< "--hdr <filename> HDR environment map (default: textures/environment.hdr)\n"
<< "-o <outputfile> image file to write result to (default: output.exr)\n"
<< "-e <exposure> exposure for interactive display (default: 0.0)\n"
<< "--no_baking do not bake UE4 material parameters to textures but generate"
" GLSL code\n"
<< "-r <w> <h> baking resolution (default: 2048x2048)\n"
<< "--mdl_path <path> mdl search path, can occur multiple times.\n";
exit(EXIT_FAILURE);
}
int MAIN_UTF8(int argc, char* argv[])
{
Options options;
mi::examples::mdl::Configure_options configure_options;
for (int i = 1; i < argc; ++i) {
const char *opt = argv[i];
if (opt[0] == '-') {
if (strcmp(opt, "--nowin") == 0) {
options.show_window = false;
} else if (strcmp(opt, "--mdl_path") == 0 && i < argc - 1) {
configure_options.additional_mdl_paths.push_back(argv[++i]);
} else if (strcmp(opt, "--hdr") == 0 && i < argc - 1) {
options.hdrfile = argv[++i];
} else if (strcmp(opt, "--no_baking") == 0) {
options.bake = false;
} else if (strcmp(opt, "-e") == 0 && i < argc - 1) {
options.exposure = static_cast<float>(atof(argv[++i]));
} else if (strcmp(opt, "-o") == 0 && i < argc - 1) {
options.outputfile = argv[++i];
} else if (strcmp(opt, "-r") == 0 && i < argc - 2) {
options.baking_resolution_x = atoi(argv[++i]);
options.baking_resolution_y = atoi(argv[++i]);
} else {
std::cout << "Unknown option: \"" << opt << "\"" << std::endl;
usage(argv[0]);
}
}
else
options.material_names.push_back(opt);
}
if(options.material_names.empty())
options.material_names.push_back(
"::nvidia::sdk_examples::tutorials_distilling::example_distilling2");
if (!neuray.is_valid_interface())
exit_failure("Failed to load the SDK.");
if (!mi::examples::mdl::configure(neuray.get(), configure_options))
exit_failure("Failed to initialize the SDK.");
exit_failure("Failed to load the mdl_distiller plugin.");
if (ret != 0)
exit_failure("Failed to initialize the SDK. Result code: %d", ret);
{
Mdl_sdk_state sdk_state;
sdk_state.mdl_sdk = neuray;
sdk_state.mdl_impexp_api = mdl_impexp_api;
sdk_state.mdl_backend_api = mdl_backend_api;
sdk_state.mdl_factory = mdl_factory;
sdk_state.transaction = scope->create_transaction();
{
std::vector<mi::base::Handle<const mi::neuraylib::ICompiled_material> >
distilled_materials;
for (
mi::Size i = 0; i < options.material_names.size(); ++i)
{
compile_material(
sdk_state,
options.material_names[i]));
check_success(cm.is_valid_interface());
printf("Distilling material %s ...\n", options.material_names[i].c_str());
distill_material(sdk_state, "ue4", cm.get()));
check_success(dm.is_valid_interface());
printf("Distilling material %s ... done.\n", options.material_names[i].c_str());
distilled_materials.push_back(dm);
}
render_scene(
sdk_state, distilled_materials, options);
}
sdk_state.transaction->commit();
}
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
Handle class template for interfaces, automatizing the lifetime control via reference counting.
Definition: handle.h:113
Standard RGBA color class with floating point elements and operations.
Definition: color.h:81
NxM-dimensional matrix class template of fixed dimensions.
Definition: matrix.h:367
Fixed-size math vector class template with generic operations.
Definition: vector.h:286
Abstract interface for a canvas represented by a rectangular array of tiles.
Definition: icanvas.h:89
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
This interface represents a function definition.
Definition: ifunction_definition.h:44
Semantics
All known semantics of functions definitions.
Definition: ifunction_definition.h:54
@ DS_INTRINSIC_DF_MICROFACET_GGX_VCAVITIES_BSDF
The df::microfacet_ggx_vcavities() function.
Definition: ifunction_definition.h:295
@ DS_INTRINSIC_DF_DIFFUSE_REFLECTION_BSDF
The df::diffuse_reflection_bsdf() function.
Definition: ifunction_definition.h:259
@ DS_INTRINSIC_DF_NORMALIZED_MIX
The df::normalized_mix() function.
Definition: ifunction_definition.h:274
@ DS_INTRINSIC_DF_CUSTOM_CURVE_LAYER
The df::custom_curve_layer() function.
Definition: ifunction_definition.h:278
@ DS_INTRINSIC_DF_WEIGHTED_LAYER
The df::weighted_layer() function.
Definition: ifunction_definition.h:276
This interface provides various utilities related to canvases and buffers.
Definition: iimage_api.h:72
virtual ICanvas * create_canvas(const char *pixel_type, Uint32 width, Uint32 height, Uint32 layers=1, bool is_cubemap=false, Float32 gamma=0.0f) const =0
Creates a canvas with given pixel type, resolution, and layers.
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 a material instance.
Definition: imaterial_instance.h:34
@ DEFAULT_OPTIONS
Default compilation options (e.g., instance compilation).
Definition: imaterial_instance.h:40
This interface can be used to obtain the MDL backends.
Definition: imdl_backend_api.h:56
@ MB_GLSL
Generate GLSL code.
Definition: imdl_backend_api.h:63
Provides access to various functionality related to MDL distilling.
Definition: imdl_distiller_api.h:47
Factory for various MDL interfaces and functions.
Definition: imdl_factory.h:53
API component for MDL related import and export operations.
Definition: imdl_impexp_api.h:43
This interface represents an MDL module.
Definition: imodule.h:634
Textures add image processing options to images.
Definition: itexture.h:68
A transaction provides a consistent view on the database.
Definition: itransaction.h:82
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.
#define MI_BASE_DLL_FILE_EXT
The operating system specific default filename extension for shared libraries (DLLs)
Definition: config.h:340
virtual Uint32 retain() const =0
Increments the reference count.
Handle<Interface> make_handle_dup(Interface *iptr)
Converts passed-in interface pointer to a handle, without taking interface over.
Definition: handle.h:439
Handle<Interface> make_handle(Interface *iptr)
Returns a handle that holds the interface pointer passed in as argument.
Definition: handle.h:428
Interface * get() const
Access to the interface. Returns 0 for an invalid interface.
Definition: handle.h:294
unsigned int Uint32
32-bit unsigned integer.
Definition: types.h:49
Uint64 Size
Unsigned integral type that is large enough to hold the size of all types.
Definition: types.h:112
double Float64
64-bit float.
Definition: types.h:52
float Float32
32-bit float.
Definition: types.h:51
signed int Sint32
32-bit signed integer.
Definition: types.h:46
Color tan(const Color &c)
Returns a color with the elementwise tangent of the color c.
Definition: color.h:816
Color sin(const Color &c)
Returns a color with the elementwise sine of the color c.
Definition: color.h:761
Color cos(const Color &c)
Returns a color with the elementwise cosine of the color c.
Definition: color.h:558
Color pow(const Color &a, const Color &b)
Returns the color a elementwise to the power of b.
Definition: color.h:719
T * begin()
Returns the pointer to the first matrix element.
Definition: matrix.h:405
bool invert()
Inverts this matrix and returns success or failure.
void transpose()
Transposes this matrix by exchanging rows and columns.
Definition: matrix.h:776
void lookat(const Vector<Float32, 3> &position, const Vector<Float32, 3> &target, const Vector<Float32, 3> &up)
Sets a transformation matrix based on a given center, a reference point, and a direction.
Matrix<T, COL, ROW> transpose(const Matrix<T, ROW, COL> &mat)
Returns the transpose of the matrix mat by exchanging rows and columns.
Definition: matrix.h:1194
T cross(const Vector_struct<T, 2> &lhs, const Vector_struct<T, 2> &rhs)
Returns the two-times-two determinant result for the two vectors lhs and rhs.
Definition: vector.h:1702
T * begin()
Returns the pointer to the first vector element.
Definition: vector.h:309
math::Vector<Float32, 3> Float32_3
Vector of three Float32.
Definition: vector_typedefs.h:90
@ BAKE_ON_GPU_WITH_CPU_FALLBACK
Prefer using the GPU for texture baking, use the CPU as fallback.
Definition: imdl_distiller_api.h:37
mi::Sint32 get_value(const mi::IData *data, T &value)
Simplifies reading the value of mi::IData into the corresponding classes from the base and math API.
Definition: set_get.h:341
Common namespace for APIs of NVIDIA Advanced Rendering Center GmbH.
Definition: example_derivatives.dox:5