Image or image path as MDL parameter?

Is it somehow possible to pass a resource location as a parameter of an MDL material? In my experiments, i am only able to use hardcoded file paths, as the texture_2d constructor does not seem to accept string variables, only string literals.

Is there a workaround/another way to do this? I’d like to create a flexible base material that can load user-defined textures without having to create an MDL material for each one.

Hi,

sure that’s possible, but you need to use a uniform parameter of type texture_2d, for example:

mdl 1.8;

import ::df::*;
import ::tex::*;
import ::state::*;

export material test_texture(uniform texture_2d texture = texture_2d("textures/lines.png"))
= let {
    float3 uvw = state::texture_coordinate(0);
    color col = tex::lookup_color(texture, float2(uvw.x, uvw.y));
} in material(
    surface: material_surface(
        scattering: df::diffuse_reflection_bsdf(tint: col)
    )
);

Best regards
Moritz

1 Like

Maybe some rough notes on possible ways how to use this example material:

  1. Instantiate the material with different texture_2d expressions. See example_calls.cpp, create_textured_material(), where first a mi::neuraylib::ITexture object is created in the database and then a mi::neuraylib::IExpression is created from a mi::neuraylib::IValue_texture referencing the database object. This expression could then be used in a mi::neuraylib::IExpression_list passed as arguments to the create_function_call function on the material definition. From there, everything runs as usual.

  2. Compile the material once with class-compilation and use custom resource IDs in the argument blocks for the additional textures you want to provide. If you use the mi::neuraylib::ITarget_code::create_argument_block() function, you can provide these custom resource IDs in the mi::neuraylib::ITarget_resource_callback callback. You will also have to tell your texture runtime about these custom resources, of course.

  3. Do create one material per texture and call your base material from these (probably not what you want)

If you need more details for one of these ways, just ask!

1 Like

I managed to get it working. Before i accept your answer, could you you explain what is wrong with my version that uses valueFactory::create_texture instead of valueFactory::create? I just need an IValue_texture from the successfully created and loaded ITexture, but the valueFactory->create_texture call crashes with read at 0x0. Not sure what the second parameter needs to be.

See below

EDIT: Nevermind. The second parameter of create_texture wants the NAME of the database entry of the texture (“nvidia_texture” in this example), not the texture data.

      // Create a DB element for the image and the texture referencing it.
      mi::base::Handle<mi::neuraylib::IImage> image(
         transaction->create<mi::neuraylib::IImage>("Image"));

      int load_result = image->reset_file(getFilePath().toStdString().c_str()); // returns 0 => OK (absolute path works here)

      transaction->store(image.get(), "nvidia_image");
      mi::base::Handle<mi::neuraylib::ITexture> texture(
         transaction->create<mi::neuraylib::ITexture>("Texture"));
      texture->set_image("nvidia_image");
       transaction->store(texture.get(), "nvidia_texture");

      const mi::neuraylib::IType_texture* tex_type = valueFactory.get_type_factory()->create_texture(mi::neuraylib::IType_texture::Shape::TS_2D);
      //mi::neuraylib::IValue_factory& valueFactory 
      
      //this crashes with read at 0x0
      //auto tex_value = valueFactory.create_texture(tex_type, texture->get_image()); //<<<<< fails here!
      
      //this version works
      auto tex_value = valueFactory.create<mi::neuraylib::IValue_texture>(tex_type);
      tex_value->set_value("nvidia_texture");

      //EDIT / solved: this also works
      //auto tex_value = valueFactory.create_texture(tex_type, "nvidia_texture");
      return mi::base::Handle<mi::neuraylib::IValue>(tex_value);

texture->get_image() would have returned the name “nvidia_image” you provided with the texture->set_image("nvidia_image") if the texture object would still be valid at the point.
But objects provided to transaction->store() may not be used afterwards anymore: mi::neuraylib::ITransaction::store().
If you want to use them again after storing them in the database, you need to call transaction->access() (or if you want to modify the object transaction->edit()) to get a new handle to the object.
But as you know the name already here, you can also just directly use the name, as you did now.

Please also note, that you should use the mi::base::Handle class in most cases for pointer return values of MDL SDK functions. Otherwise you may get hard-to-find problems with handle leaks. In the code above, when the application terminates, the tex_type object will still have a reference count of 1.
Please refer to Library Design for an explanation of how handles work in MDL SDK.

1 Like

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.