This example describes the API of the code generated by the "GLSL" backend for compiled materials and shows how a renderer can call this generated code to evaluate sub-expressions of multiple materials using OpenGL.
#include <iomanip>
#include <iostream>
#include <string>
#include <vector>
#include "example_shared.h"
#include "example_glsl_shared.h"
#include <GL/glew.h>
#include <GLFW/glfw3.h>
char const* vertex_shader_filename = "example_execution_glsl.vert";
char const* fragment_shader_filename = "example_execution_glsl.frag";
struct Options {
bool no_window;
bool use_ssbo;
bool remap_noise_functions;
std::string outputfile;
int material_pattern;
unsigned res_x, res_y;
Options()
: no_window(false)
#if defined(MI_PLATFORM_MACOSX) || defined(MI_ARCH_ARM_64)
, use_ssbo(false)
#else
, use_ssbo(true)
#endif
, remap_noise_functions(true)
, outputfile("output.png")
, material_pattern(7)
, res_x(1024)
, res_y(768)
{
}
};
struct Vertex {
};
static void handle_glfw_error(int error_code, const char* description)
{
std::cerr << "GLFW error (code: " << error_code << "): \"" << description << "\"\n";
}
static GLFWwindow *init_opengl(Options const &options)
{
glfwSetErrorCallback(handle_glfw_error);
check_success(glfwInit());
if (options.use_ssbo) {
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
} else {
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);
if (options.no_window)
glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE);
GLFWwindow *window = glfwCreateWindow(
options.res_x, options.res_y,
"MDL SDK GLSL Execution Example - Switch pattern with keys 1 - 7", 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(1);
check_gl_success();
return window;
}
static std::string generate_glsl_switch_func(
{
std::string src =
"#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"
"int get_mdl_num_mat_subexprs() { return " +
to_string(target_code->get_callable_function_count()) +
"; }\n"
"\n";
std::string switch_func =
"vec3 mdl_mat_subexpr(int id, State state) {\n"
" switch(id) {\n";
for (size_t i = 0, num_target_codes = target_code->get_callable_function_count();
i < num_target_codes;
++i)
{
std::string func_name(target_code->get_callable_function(i));
src += target_code->get_callable_function_prototype(
i, mi::neuraylib::ITarget_code::SL_GLSL);
src += '\n';
switch_func += " case " + to_string(i) + ": return " + func_name + "(state);\n";
}
switch_func +=
" default: return vec3(0);\n"
" }\n"
"}\n";
return src + "\n" + switch_func;
}
static GLuint create_shader_program(
bool use_ssbo,
bool remap_noise_functions,
unsigned max_materials,
unsigned max_textures,
{
GLint success;
GLuint program = glCreateProgram();
add_shader(GL_VERTEX_SHADER,
read_text_file(
mi::examples::io::get_executable_folder() + "/" + vertex_shader_filename), program);
std::stringstream sstr;
sstr << (use_ssbo ? "#version 430 core\n" : "#version 330 core\n");
sstr << "#define MAX_MATERIALS " << to_string(max_materials) << "\n";
sstr << "#define MAX_TEXTURES " << to_string(max_textures) << "\n";
sstr << read_text_file(
mi::examples::io::get_executable_folder() + "/" + fragment_shader_filename);
add_shader(GL_FRAGMENT_SHADER, sstr.str() , program);
std::string code(target_code->get_code());
if (remap_noise_functions) {
code.append(read_text_file(
mi::examples::io::get_executable_folder() + "/" + "noise_no_lut.glsl"));
}
add_shader(GL_FRAGMENT_SHADER, code, program);
std::string glsl_switch_func = generate_glsl_switch_func(target_code);
#ifdef DUMP_GLSL
std::cout << "Dumping GLSL code for the \"mdl_mat_subexpr\" switch function:\n\n"
<< glsl_switch_func << std::endl;
#endif
add_shader(GL_FRAGMENT_SHADER, glsl_switch_func.c_str(), program);
glLinkProgram(program);
glGetProgramiv(program, GL_LINK_STATUS, &success);
if (!success) {
dump_program_info(program, "Error linking the shader program: ");
terminate();
}
glUseProgram(program);
check_gl_success();
return program;
}
static GLuint create_quad(GLuint program, GLuint* vertex_buffer)
{
static Vertex const vertices[6] = {
{ { -1.f, -1.f, 0.0f }, { 0.f, 0.f } },
{ { 1.f, -1.f, 0.0f }, { 1.f, 0.f } },
{ { -1.f, 1.f, 0.0f }, { 0.f, 1.f } },
{ { 1.f, -1.f, 0.0f }, { 1.f, 0.f } },
{ { 1.f, 1.f, 0.0f }, { 1.f, 1.f } },
{ { -1.f, 1.f, 0.0f }, { 0.f, 1.f } }
};
glGenBuffers(1, vertex_buffer);
glBindBuffer(GL_ARRAY_BUFFER, *vertex_buffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
GLuint vertex_array;
glGenVertexArrays(1, &vertex_array);
glBindVertexArray(vertex_array);
GLint pos_index = glGetAttribLocation(program, "Position");
GLint tex_coord_index = glGetAttribLocation(program, "TexCoord");
check_success(pos_index >= 0 && tex_coord_index >= 0);
glEnableVertexAttribArray(pos_index);
glVertexAttribPointer(
pos_index, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), 0);
glEnableVertexAttribArray(tex_coord_index);
glVertexAttribPointer(
tex_coord_index, 2, GL_FLOAT, GL_FALSE,
check_gl_success();
return vertex_array;
}
class Material_opengl_context
{
public:
Material_opengl_context(GLuint program, bool use_ssbo)
: m_program(program)
, m_use_ssbo(use_ssbo)
, m_next_storage_block_binding(0)
{}
~Material_opengl_context();
bool prepare_material_data(
bool set_material_data(unsigned max_textures);
private:
bool prepare_texture(
GLuint texture_array);
private:
GLuint m_program;
bool m_use_ssbo;
std::vector<GLuint> m_texture_objects;
std::vector<int> m_material_texture_starts;
std::vector<GLuint> m_buffer_objects;
GLuint m_next_storage_block_binding;
};
Material_opengl_context::~Material_opengl_context()
{
if (m_buffer_objects.size() > 0)
glDeleteBuffers(GLsizei(m_buffer_objects.size()), &m_buffer_objects[0]);
if (m_texture_objects.size() > 0)
glDeleteTextures(GLsizei(m_texture_objects.size()), &m_texture_objects[0]);
check_gl_success();
}
void Material_opengl_context::set_mdl_readonly_data(
{
mi::Size num_uniforms = target_code->get_ro_data_segment_count();
if (num_uniforms == 0) return;
if (m_use_ssbo) {
size_t cur_buffer_offs = m_buffer_objects.size();
m_buffer_objects.insert(m_buffer_objects.end(), num_uniforms, 0);
glGenBuffers(GLsizei(num_uniforms), &m_buffer_objects[cur_buffer_offs]);
for (
mi::Size i = 0; i < num_uniforms; ++i) {
mi::Size segment_size = target_code->get_ro_data_segment_size(i);
char const *segment_data = target_code->get_ro_data_segment_data(i);
#ifdef DUMP_GLSL
std::cout << "Dump ro segment data " << i << " \""
<< target_code->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[cur_buffer_offs + i]);
glBufferData(
GL_SHADER_STORAGE_BUFFER, GLsizeiptr(segment_size), segment_data, GL_STATIC_DRAW);
GLuint block_index = glGetProgramResourceIndex(
m_program, GL_SHADER_STORAGE_BLOCK, target_code->get_ro_data_segment_name(i));
glShaderStorageBlockBinding(m_program, block_index, m_next_storage_block_binding);
glBindBufferBase(
GL_SHADER_STORAGE_BUFFER,
m_next_storage_block_binding,
m_buffer_objects[cur_buffer_offs + i]);
++m_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 = target_code->get_ro_data_segment_size(i);
const char* segment_data = target_code->get_ro_data_segment_data(i);
std::cout << "Dump ro segment data " << i << " \""
<< target_code->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(target_code->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 = target_code->get_ro_data_segment_size(i);
const char* segment_data = target_code->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();
}
}
}
bool Material_opengl_context::prepare_texture(
GLuint texture_obj)
{
mi::Uint32 tex_width = canvas->get_resolution_x();
mi::Uint32 tex_height = canvas->get_resolution_y();
mi::Uint32 tex_layers = canvas->get_layers_size();
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 (tex_layers != 1) {
std::cerr << "The example doesn't support layered images!" << 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));
image_api->adjust_gamma(gamma_canvas.get(), 1.0f);
canvas = gamma_canvas;
} else if (strcmp(image_type, "Color") != 0 && strcmp(image_type, "Float32<4>") != 0) {
canvas = image_api->convert(canvas.get(), "Color");
}
code->get_texture_shape(texture_index);
glBindTexture(GL_TEXTURE_2D, texture_obj);
glTexImage2D(
GL_TEXTURE_2D, 0, GL_RGBA, tex_width, tex_height, 0, GL_RGBA, GL_FLOAT, data);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
}
check_gl_success();
return true;
}
bool Material_opengl_context::prepare_material_data(
{
set_mdl_readonly_data(target_code);
size_t cur_tex_offs = m_texture_objects.size();
m_material_texture_starts.push_back(GLuint(cur_tex_offs));
mi::Size num_textures = target_code->get_texture_count();
if (num_textures > 1) {
m_texture_objects.insert(m_texture_objects.end(), num_textures - 1, 0);
glGenTextures(GLsizei(num_textures - 1), &m_texture_objects[cur_tex_offs]);
for (
mi::Size i = 1; i < num_textures; ++i) {
if (!prepare_texture(
transaction, image_api, target_code,
i, m_texture_objects[cur_tex_offs + i - 1]))
return false;
}
}
return true;
}
bool Material_opengl_context::set_material_data(unsigned max_textures)
{
GLsizei total_textures = GLsizei(m_texture_objects.size());
if (total_textures > GLsizei(max_textures)) {
fprintf( stderr, "Number of required textures (%d) is not supported (max: %d)\n",
total_textures, max_textures);
return false;
}
if (m_use_ssbo) {
if (glfwExtensionSupported("GL_ARB_bindless_texture")) {
if (total_textures > 0) {
std::vector<GLuint64> texture_handles;
texture_handles.resize(total_textures);
for (GLsizei i = 0; i < total_textures; ++i) {
texture_handles[i] = glGetTextureHandleARB(m_texture_objects[i]);
glMakeTextureHandleResidentARB(texture_handles[i]);
}
glUniformHandleui64vARB(
glGetUniformLocation(m_program, "material_texture_samplers_2d"),
total_textures,
&texture_handles[0]);
glUniform1iv(
glGetUniformLocation(m_program, "material_texture_starts"),
GLsizei(m_material_texture_starts.size()),
&m_material_texture_starts[0]);
}
} else if (glfwExtensionSupported("GL_NV_bindless_texture")) {
if (total_textures > 0) {
std::vector<GLuint64> texture_handles;
texture_handles.resize(total_textures);
for (GLsizei i = 0; i < total_textures; ++i) {
texture_handles[i] = glGetTextureHandleNV(m_texture_objects[i]);
glMakeTextureHandleResidentNV(texture_handles[i]);
}
glUniformHandleui64vARB(
glGetUniformLocation(m_program, "material_texture_samplers_2d"),
total_textures,
&texture_handles[0]);
glUniform1iv(
glGetUniformLocation(m_program, "material_texture_starts"),
GLsizei(m_material_texture_starts.size()),
&m_material_texture_starts[0]);
}
} else {
fprintf(stderr, "Sample requires Bindless Textures, "
"that are not supported by the current system.\n");
return false;
}
}
return glGetError() == GL_NO_ERROR;
}
class Material_compiler {
public:
Material_compiler(
bool use_ssbo,
bool remap_noise_functions,
bool add_material_subexpr(
const std::string& qualified_module_name,
const std::string& material_db_name,
const char* path,
const char* fname);
private:
const std::string& qualified_module_name,
const std::string& material_db_name);
bool class_compilation);
private:
bool m_use_ssbo;
bool m_remap_noise_functions;
};
const std::string& qualified_module_name,
const std::string& material_db_name)
{
if (!material_definition)
exit_failure("Failed to access material definition '%s'.", material_db_name.c_str());
material_definition->create_function_call(0, &result));
check_success(result == 0);
if (result != 0)
exit_failure("Failed to instantiate material '%s'.", material_db_name.c_str());
material_instance->retain();
return material_instance.get();
}
bool class_compilation)
{
material_instance2->create_compiled_material(flags, m_context.get()));
check_success(print_messages(m_context.get()));
compiled_material->retain();
return compiled_material.get();
}
{
m_be_glsl->translate_link_unit(m_link_unit.get(), m_context.get()));
check_success(print_messages(m_context.get()));
check_success(code_glsl);
#ifdef DUMP_GLSL
std::cout << "Dumping GLSL code:\n\n" << code_glsl->get_code() << std::endl;
#endif
return code_glsl;
}
bool Material_compiler::add_material_subexpr(
const std::string& qualified_module_name,
const std::string& material_db_name,
const char* path,
const char* fname)
{
create_material_instance(qualified_module_name, material_db_name));
compile_material_instance(material_instance.get(), false));
m_link_unit->add_material_expression(compiled_material.get(), path, fname,
m_context.get());
return print_messages(m_context.get());
}
Material_compiler::Material_compiler(
bool use_ssbo,
bool remap_noise_functions,
, m_be_glsl(mdl_backend_api->get_backend(
mi::neuraylib::IMdl_backend_api:
:MB_GLSL))
, m_context(mdl_factory->create_execution_context())
, m_link_unit()
, m_use_ssbo(use_ssbo)
, m_remap_noise_functions(remap_noise_functions)
{
check_success(m_be_glsl->set_option("num_texture_spaces", "1") == 0);
if (m_use_ssbo) {
check_success(m_be_glsl->set_option("glsl_version", "430") == 0);
#if 0
check_success(m_be_glsl->set_option("glsl_max_const_data", "0") == 0);
#endif
check_success(m_be_glsl->set_option("glsl_place_uniforms_into_ssbo", "on") == 0);
} else {
check_success(m_be_glsl->set_option("glsl_version", "330") == 0);
#if 0
check_success(m_be_glsl->set_option("glsl_max_const_data", "1024") == 0);
#endif
check_success(m_be_glsl->set_option("glsl_place_uniforms_into_ssbo", "off") == 0);
}
if (m_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);
}
}
struct Window_context
{
int material_pattern;
};
void handle_key(GLFWwindow *window, int key, int , int action, int )
{
if (action == GLFW_PRESS) {
if (GLFW_KEY_KP_0 <= key && key <= GLFW_KEY_KP_9)
key += GLFW_KEY_0 - GLFW_KEY_KP_0;
switch (key) {
case GLFW_KEY_ESCAPE:
glfwSetWindowShouldClose(window, GLFW_TRUE);
break;
case GLFW_KEY_1:
case GLFW_KEY_2:
case GLFW_KEY_3:
case GLFW_KEY_4:
case GLFW_KEY_5:
case GLFW_KEY_6:
case GLFW_KEY_7:
{
Window_context *ctx = static_cast<Window_context*>(
glfwGetWindowUserPointer(window));
ctx->material_pattern = key - GLFW_KEY_0;
break;
}
default:
break;
}
}
}
void handle_framebuffer_size(GLFWwindow* , int width, int height)
{
glViewport(0, 0, width, height);
}
void show_and_animate_scene(
Options const &options)
{
Window_context window_context = { options.material_pattern };
GLFWwindow *window = init_opengl(options);
unsigned max_materials = options.use_ssbo ? 64 : 16;
unsigned max_textures = options.use_ssbo ? 32 : 16;
GLuint program = create_shader_program(
options.use_ssbo, options.remap_noise_functions, max_materials, max_textures, target_code);
GLuint quad_vertex_buffer;
GLuint quad_vao = create_quad(program, &quad_vertex_buffer);
{
Material_opengl_context material_opengl_context(program, options.use_ssbo);
check_success(material_opengl_context.prepare_material_data(
transaction, image_api, target_code));
check_success(material_opengl_context.set_material_data(max_textures));
GLint material_pattern_index = glGetUniformLocation(program, "material_pattern");
GLint animation_time_index = glGetUniformLocation(program, "animation_time");
if (!options.no_window) {
GLfloat animation_time = 0;
double last_frame_time = glfwGetTime();
glfwSetWindowUserPointer(window, &window_context);
glfwSetKeyCallback(window, handle_key);
glfwSetFramebufferSizeCallback(window, handle_framebuffer_size);
while (!glfwWindowShouldClose(window))
{
double cur_frame_time = glfwGetTime();
animation_time += GLfloat(cur_frame_time - last_frame_time);
last_frame_time = cur_frame_time;
glUniform1i(material_pattern_index, window_context.material_pattern);
glUniform1f(animation_time_index, animation_time);
glClear(GL_COLOR_BUFFER_BIT);
glBindVertexArray(quad_vao);
glDrawArrays(GL_TRIANGLES, 0, 6);
glfwSwapBuffers(window);
glfwPollEvents();
}
} else {
GLuint frame_buffer = 0, color_buffer = 0;
glGenFramebuffers(1, &frame_buffer);
glBindFramebuffer(GL_FRAMEBUFFER, frame_buffer);
glGenRenderbuffers(1, &color_buffer);
glBindRenderbuffer(GL_RENDERBUFFER, color_buffer);
glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, options.res_x, options.res_y);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
GL_RENDERBUFFER, color_buffer);
check_gl_success();
glUniform1i(material_pattern_index, window_context.material_pattern);
glUniform1f(animation_time_index, 0.f);
glClear(GL_COLOR_BUFFER_BIT);
glViewport(0, 0, options.res_x, options.res_y);
check_gl_success();
glBindVertexArray(quad_vao);
check_gl_success();
glDrawArrays(GL_TRIANGLES, 0, 6);
check_gl_success();
image_api->create_canvas("Rgba", options.res_x, options.res_y));
glReadPixels(0, 0, options.res_x, options.res_y,
GL_RGBA, GL_UNSIGNED_BYTE, tile->get_data());
mdl_impexp_api->export_canvas(options.outputfile.c_str(), canvas.get());
glDeleteRenderbuffers(1, &color_buffer);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glDeleteFramebuffers(1, &frame_buffer);
}
}
glDeleteVertexArrays(1, &quad_vao);
glDeleteBuffers(1, &quad_vertex_buffer);
glDeleteProgram(program);
check_gl_success();
glfwDestroyWindow(window);
glfwTerminate();
}
static void usage(char const *prog_name, bool default_ssbo)
{
std::cout
<< "Usage: " << prog_name << " [options] [<material_pattern>]\n"
<< "Options:\n"
<< " --nowin don't show interactive display\n"
<< " --res <x> <y> resolution (default: 1024x768)\n"
<< " --with-ssbo Enable SSBO" << (default_ssbo ? " (default)\n" : "\n")
<< " --no-ssbo Disable SSBO" << (!default_ssbo ? " (default)\n" : "\n")
<< " --no-noise-remap Do not remap MDL ::base noise functions\n"
<< " -o <outputfile> image file to write result in nowin mode (default: output.png)\n"
<< " <material_pattern> a number from 1 to 7 choosing which material combination to use"
<< std::endl;
exit_failure();
}
int MAIN_UTF8(int argc, char* argv[])
{
Options options;
bool default_ssbo = options.use_ssbo;
for (int i = 1; i < argc; ++i) {
char const *opt = argv[i];
if (opt[0] == '-') {
if (strcmp(opt, "--nowin") == 0) {
options.no_window = true;
} else if (strcmp(opt, "-o") == 0) {
if (i < argc - 1) {
options.outputfile = argv[++i];
} else {
usage(argv[0], default_ssbo);
}
} else if (strcmp(opt, "--with-ssbo") == 0) {
options.use_ssbo = true;
} else if (strcmp(opt, "--no-ssbo") == 0) {
options.use_ssbo = false;
} else if (strcmp(opt, "--no-noise-remap") == 0) {
options.remap_noise_functions = false;
} else if (strcmp(opt, "--res") == 0) {
if (i < argc - 2) {
options.res_x = std::max(atoi(argv[++i]), 1);
options.res_y = std::max(atoi(argv[++i]), 1);
} else {
usage(argv[0], default_ssbo);
}
} else {
usage(argv[0], default_ssbo);
}
} else {
options.material_pattern = atoi(opt);
if (options.material_pattern < 1 || options.material_pattern > 7) {
std::cerr << "Invalid material_pattern parameter." << std::endl;
usage(argv[0], default_ssbo);
}
}
}
printf("SSBO Extension is : %s\n",
options.use_ssbo ? "enabled" : "disabled");
printf("Noise function remap is : %s\n",
options.remap_noise_functions ? "enabled" : "disabled");
if (!neuray.is_valid_interface())
exit_failure("Failed to load the SDK.");
mi::examples::mdl::Configure_options configure_options;
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);
{
std::string module_name = "::nvidia::sdk_examples::tutorials";
mdl_impexp_api->load_module(transaction.
get(), module_name.c_str(), context.get());
if (!print_messages(context.get()))
exit_failure("Loading module '%s' failed.", module_name.c_str());
if (!module)
exit_failure("Failed to access the loaded module.");
std::vector<std::string> material_simple_names;
std::vector<std::string> fnames;
if (options.use_ssbo || options.remap_noise_functions) {
material_simple_names.push_back("example_execution1");
fnames.push_back("tint");
}
material_simple_names.push_back("example_execution2");
fnames.push_back("tint_2");
if (options.use_ssbo || options.remap_noise_functions) {
material_simple_names.push_back("example_execution3");
fnames.push_back("tint_3");
}
size_t n = material_simple_names.size();
std::vector<std::string> material_db_names(n);
for (size_t i = 0; i < n; ++i) {
material_db_names[i]
= std::string(module_db_name->get_c_str()) + "::" + material_simple_names[i];
material_db_names[i] = mi::examples::mdl::add_missing_material_signature(
module.get(), material_db_names[i]);
if (material_db_names[i].empty())
exit_failure("Failed to find the material %s in the module %s.",
material_simple_names[i].c_str(), module_name.c_str());
}
module.reset();
Material_compiler mc(
options.use_ssbo, options.remap_noise_functions,
mdl_impexp_api.
get(), mdl_backend_api.get(), mdl_factory.get(), transaction.
get());
for (size_t i = 0; i < n; ++i) {
mc.add_material_subexpr(
module_name, material_db_names[i], "surface.scattering.tint", fnames[i].c_str());
}
show_and_animate_scene(transaction, mdl_impexp_api, image_api, target_code, options);
}
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 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 call.
Definition: ifunction_call.h:52
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
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
This interface can be used to obtain the MDL backends.
Definition: imdl_backend_api.h:56
Factory for various MDL interfaces and functions.
Definition: imdl_factory.h:53
virtual IMdl_execution_context * create_execution_context()=0
Creates an execution context.
virtual const IString * get_db_module_name(const char *mdl_name)=0
Returns the DB name for the MDL name of a module (or file path for MDLE modules).
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
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 Sint32 commit()=0
Commits the transaction.
virtual const IInterface * get_interface(const Uuid &interface_id) const =0
Acquires a const interface from another.
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
float Float32
32-bit float.
Definition: types.h:51
signed int Sint32
32-bit signed integer.
Definition: types.h:46
Texture_shape
Definition: imdl_backend.h:811
@ Texture_shape_2d
Two-dimensional texture.
Definition: imdl_backend.h:813
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
/******************************************************************************
* Copyright 2024 NVIDIA Corporation. All rights reserved.
*****************************************************************************/
// examples/mdl_sdk/execution_glsl/example_execution_glsl.frag
//
// This file contains the implementations of the texture access functions
// and the fragment shader used to evaluate the material sub-expressions.
// Material pattern as chosen by the user.
uniform int material_pattern;
// Current time in seconds since the start of the render loop.
uniform float animation_time;
// Mapping from material index to start index in material_texture_samplers_2d.
uniform int material_texture_starts[MAX_MATERIALS];
// Array containing all 2D texture samplers of all used materials.
uniform sampler2D material_texture_samplers_2d[MAX_TEXTURES];
// Start offset of the current material inside material_texture_samplers_2d, set in main.
int tex_start = 0;
// The input variables coming from the vertex shader.
in vec3[1] texture_coordinate; // used for state::texture_coordinate(tex_space) in "arg" mode
in vec3 vPosition;
// The color output variable of this fragment shader.
out vec4 FragColor;
// The MDL material state structure as configured via the GLSL backend options.
// Note: Must be in sync with the state struct in generate_glsl_switch_func and the code generated
// by the MDL SDK (see dumped code when enabling DUMP_GLSL in example_execution_glsl.cpp).
struct State {
vec3 normal;
vec3 geom_normal;
vec3 position;
float animation_time;
vec3 text_coords[1];
vec3 tangent_u[1];
vec3 tangent_v[1];
int ro_data_segment_offset;
mat4 world_to_object;
mat4 object_to_world;
int object_id;
float meters_per_scene_unit;
int arg_block_offset;
};
//
// The prototypes of the functions generated in our generate_glsl_switch_func() function.
//
// Return the number of available MDL material subexpressions.
int get_mdl_num_mat_subexprs();
// Return the result of the MDL material subexpression given by the id.
vec3 mdl_mat_subexpr(int id, State state);
#if __VERSION__ < 400
int bitCount(uint x)
{
x = x - ((x >> 1u) & 0x55555555u);
x = (x & 0x33333333u) + ((x >> 2u) & 0x33333333u);
x = (x + (x >> 4u)) & 0x0F0F0F0Fu;
x = x + (x >> 8u);
x = x + (x >> 16u);
return int(x & 0x0000003Fu);
}
#endif
// Implementation of tex::lookup_*() for a texture_2d texture.
vec4 tex_lookup_float4_2d(
int tex, vec2 coord, int wrap_u, int wrap_v, vec2 crop_u, vec2 crop_v, float frame)
{
if (tex == 0) return vec4(0);
#if __VERSION__ >= 400
return texture(material_texture_samplers_2d[tex_start + tex - 1], coord);
#else
switch (tex_start + tex - 1) {
#if MAX_TEXTURES > 0
case 0: return texture(material_texture_samplers_2d[ 0], coord, 0);
#endif
#if MAX_TEXTURES > 1
case 1: return texture(material_texture_samplers_2d[ 1], coord, 0);
#endif
#if MAX_TEXTURES > 2
case 2: return texture(material_texture_samplers_2d[ 2], coord, 0);
#endif
#if MAX_TEXTURES > 3
case 3: return texture(material_texture_samplers_2d[ 3], coord, 0);
#endif
#if MAX_TEXTURES > 4
case 4: return texture(material_texture_samplers_2d[ 4], coord, 0);
#endif
#if MAX_TEXTURES > 5
case 5: return texture(material_texture_samplers_2d[ 5], coord, 0);
#endif
#if MAX_TEXTURES > 6
case 6: return texture(material_texture_samplers_2d[ 6], coord, 0);
#endif
#if MAX_TEXTURES > 7
case 7: return texture(material_texture_samplers_2d[ 7], coord, 0);
#endif
#if MAX_TEXTURES > 8
case 8: return texture(material_texture_samplers_2d[ 8], coord, 0);
#endif
#if MAX_TEXTURES > 9
case 9: return texture(material_texture_samplers_2d[ 9], coord, 0);
#endif
#if MAX_TEXTURES > 10
case 10: return texture(material_texture_samplers_2d[10], coord, 0);
#endif
#if MAX_TEXTURES > 11
case 11: return texture(material_texture_samplers_2d[11], coord, 0);
#endif
#if MAX_TEXTURES > 12
case 12: return texture(material_texture_samplers_2d[12], coord, 0);
#endif
#if MAX_TEXTURES > 13
case 13: return texture(material_texture_samplers_2d[13], coord, 0);
#endif
#if MAX_TEXTURES > 14
case 14: return texture(material_texture_samplers_2d[14], coord, 0);
#endif
#if MAX_TEXTURES > 15
case 15: return texture(material_texture_samplers_2d[15], coord, 0);
#endif
#if MAX_TEXTURES > 16
case 16: return texture(material_texture_samplers_2d[16], coord, 0);
#endif
#if MAX_TEXTURES > 17
case 17: return texture(material_texture_samplers_2d[17], coord, 0);
#endif
#if MAX_TEXTURES > 18
case 18: return texture(material_texture_samplers_2d[18], coord, 0);
#endif
#if MAX_TEXTURES > 19
case 19: return texture(material_texture_samplers_2d[19], coord, 0);
#endif
#if MAX_TEXTURES > 20
case 20: return texture(material_texture_samplers_2d[20], coord, 0);
#endif
#if MAX_TEXTURES > 21
case 21: return texture(material_texture_samplers_2d[21], coord, 0);
#endif
#if MAX_TEXTURES > 22
case 22: return texture(material_texture_samplers_2d[22], coord, 0);
#endif
#if MAX_TEXTURES > 23
case 23: return texture(material_texture_samplers_2d[23], coord, 0);
#endif
#if MAX_TEXTURES > 24
case 24: return texture(material_texture_samplers_2d[24], coord, 0);
#endif
#if MAX_TEXTURES > 25
case 25: return texture(material_texture_samplers_2d[25], coord, 0);
#endif
#if MAX_TEXTURES > 26
case 26: return texture(material_texture_samplers_2d[26], coord, 0);
#endif
#if MAX_TEXTURES > 27
case 27: return texture(material_texture_samplers_2d[27], coord, 0);
#endif
#if MAX_TEXTURES > 28
case 28: return texture(material_texture_samplers_2d[28], coord, 0);
#endif
#if MAX_TEXTURES > 29
case 29: return texture(material_texture_samplers_2d[29], coord, 0);
#endif
#if MAX_TEXTURES > 30
case 30: return texture(material_texture_samplers_2d[30], coord, 0);
#endif
#if MAX_TEXTURES > 31
case 31: return texture(material_texture_samplers_2d[31], coord, 0);
#endif
default: return vec4(0.0);
}
#endif
}
// Implementation of tex::texel_*() for a texture_2d texture.
vec4 tex_texel_2d(int tex, ivec2 coord, ivec2 uv_tile)
{
if (tex == 0) return vec4(0);
#if __VERSION__ >= 400
return texelFetch(material_texture_samplers_2d[tex_start + tex - 1], coord, 0);
#else
switch (tex_start + tex - 1) {
#if MAX_TEXTURES > 0
case 0: return texelFetch(material_texture_samplers_2d[ 0], coord, 0);
#endif
#if MAX_TEXTURES > 1
case 1: return texelFetch(material_texture_samplers_2d[ 1], coord, 0);
#endif
#if MAX_TEXTURES > 2
case 2: return texelFetch(material_texture_samplers_2d[ 2], coord, 0);
#endif
#if MAX_TEXTURES > 3
case 3: return texelFetch(material_texture_samplers_2d[ 3], coord, 0);
#endif
#if MAX_TEXTURES > 4
case 4: return texelFetch(material_texture_samplers_2d[ 4], coord, 0);
#endif
#if MAX_TEXTURES > 5
case 5: return texelFetch(material_texture_samplers_2d[ 5], coord, 0);
#endif
#if MAX_TEXTURES > 6
case 6: return texelFetch(material_texture_samplers_2d[ 6], coord, 0);
#endif
#if MAX_TEXTURES > 7
case 7: return texelFetch(material_texture_samplers_2d[ 7], coord, 0);
#endif
#if MAX_TEXTURES > 8
case 8: return texelFetch(material_texture_samplers_2d[ 8], coord, 0);
#endif
#if MAX_TEXTURES > 9
case 9: return texelFetch(material_texture_samplers_2d[ 9], coord, 0);
#endif
#if MAX_TEXTURES > 10
case 10: return texelFetch(material_texture_samplers_2d[10], coord, 0);
#endif
#if MAX_TEXTURES > 11
case 11: return texelFetch(material_texture_samplers_2d[11], coord, 0);
#endif
#if MAX_TEXTURES > 12
case 12: return texelFetch(material_texture_samplers_2d[12], coord, 0);
#endif
#if MAX_TEXTURES > 13
case 13: return texelFetch(material_texture_samplers_2d[13], coord, 0);
#endif
#if MAX_TEXTURES > 14
case 14: return texelFetch(material_texture_samplers_2d[14], coord, 0);
#endif
#if MAX_TEXTURES > 15
case 15: return texelFetch(material_texture_samplers_2d[15], coord, 0);
#endif
#if MAX_TEXTURES > 16
case 16: return texelFetch(material_texture_samplers_2d[16], coord, 0);
#endif
#if MAX_TEXTURES > 17
case 17: return texelFetch(material_texture_samplers_2d[17], coord, 0);
#endif
#if MAX_TEXTURES > 18
case 18: return texelFetch(material_texture_samplers_2d[18], coord, 0);
#endif
#if MAX_TEXTURES > 19
case 19: return texelFetch(material_texture_samplers_2d[19], coord, 0);
#endif
#if MAX_TEXTURES > 20
case 20: return texelFetch(material_texture_samplers_2d[20], coord, 0);
#endif
#if MAX_TEXTURES > 21
case 21: return texelFetch(material_texture_samplers_2d[21], coord, 0);
#endif
#if MAX_TEXTURES > 22
case 22: return texelFetch(material_texture_samplers_2d[22], coord, 0);
#endif
#if MAX_TEXTURES > 23
case 23: return texelFetch(material_texture_samplers_2d[23], coord, 0);
#endif
#if MAX_TEXTURES > 24
case 24: return texelFetch(material_texture_samplers_2d[24], coord, 0);
#endif
#if MAX_TEXTURES > 25
case 25: return texelFetch(material_texture_samplers_2d[25], coord, 0);
#endif
#if MAX_TEXTURES > 26
case 26: return texelFetch(material_texture_samplers_2d[26], coord, 0);
#endif
#if MAX_TEXTURES > 27
case 27: return texelFetch(material_texture_samplers_2d[27], coord, 0);
#endif
#if MAX_TEXTURES > 28
case 28: return texelFetch(material_texture_samplers_2d[28], coord, 0);
#endif
#if MAX_TEXTURES > 29
case 29: return texelFetch(material_texture_samplers_2d[29], coord, 0);
#endif
#if MAX_TEXTURES > 30
case 30: return texelFetch(material_texture_samplers_2d[30], coord, 0);
#endif
#if MAX_TEXTURES > 31
case 31: return texelFetch(material_texture_samplers_2d[31], coord, 0);
#endif
default: return vec4(0.0);
}
#endif
}
// The fragment shader main function evaluating the MDL sub-expression.
void main() {
// Set number of materials to use according to selected pattern
uint num_materials = uint(bitCount(uint(material_pattern)));
// Assign materials in a checkerboard pattern
int material_index =
int(
(
uint(texture_coordinate[0].x * 4) ^
uint(texture_coordinate[0].y * 4)
) % num_materials);
// Change material index according to selected pattern
switch (material_pattern)
{
case 2: material_index = 1; break;
case 4: material_index = 2; break;
case 5: if (material_index == 1) material_index = 2; break;
case 6: material_index += 1; break;
}
if (material_index > get_mdl_num_mat_subexprs())
material_index = get_mdl_num_mat_subexprs();
// Set up texture access for the chosen material
tex_start = material_texture_starts[material_index];
// Set MDL material state for state functions in "field" mode
State state = State(
/*normal=*/ vec3(0.0, 0.0, 1.0),
/*geometry_normal=*/ vec3(0.0, 0.0, 1.0),
/*position=*/ vPosition,
/*animation_time=*/ animation_time,
/*text_coords=*/ texture_coordinate,
/*texture_tangent_u=*/ vec3[1](vec3(1.0, 0.0, 0.0)),
/*texture_tangent_v=*/ vec3[1](vec3(0.0, 1.0, 0.0)),
/*ro_data_segment_offset=*/ 0,
/*world_to_object=*/ mat4(1.0),
/*object_to_world=*/ mat4(1.0),
/*object_id=*/ 0,
/*meters_per_scene_unit=*/ 1.0,
/*arg_block_offset=*/ 0
);
// Evaluate material sub-expression
vec3 res = mdl_mat_subexpr(material_index, state);
// Apply gamma correction and write to output variable
FragColor = vec4(pow(res, vec3(1.0 / 2.2)), 1.0);
}