An Iray Shader Mixer Cookbook

2»

Comments

  • mephoriamephoria Posts: 120

    Building a Better Tiler

    The venerable Tiler brick is a mainstay for pretty much all shaders, and it does a reasonable job for many purposes. However, there are three ways in which it doesn’t suffice for more advanced shaders.

    • It can’t take a Texture Coordinate System as an input. This means that you can’t chain multiple tilers, or use non-UV coordinates, or perform other coordinate transformations.

    • It only handles X/Y tiling (scaling) and translation which means that it can’t generalize to actual 3D coordinate systems. (Properly speaking, it should refer to U/V coordinates, since this is it’s primary strength.)

    • It doesn’t allow rotation of textures. Rotation, scaling (which, for UV maps is the same as “tiling”), and translation are almost always handled together, so it seems a shame to eliminate one third of the triad.

    All of these issues can be addressed by combining two existing bricks. Alas, the result isn’t quite as pretty as the existing Tiler brick, but the increased versatility makes up for it.

    image

    The most important thing to remember is that tiling is accomplished simply by scaling an input, and taking advantage of the fact that UV coordinates use modulo arithmetic, and thus wrap around to 0.0 whenever their values exceed 1.0. Thus, the standard Tiler brick is scaling and translating the X and Y (or U and V) coordinates. In the more general case, we would apply these coordinate transformations to the X, Y, and Z parts of the coordinate. This transformation can be accomplished by the existing …>MDL>Default Modules>base>Transform Coordinates brick, but only if we supply a Transform parameters of type F4x4 (a 4x4 floating point matrix). Creating this matrix by hand would be quite a nuisance, but there is no need. The …>MDL>Default Modules>base>Rotation Translation Scale brick exists for precisely this purpose. Its three inputs are all F3 values (though labeled as Point) which can easily be set through either Direct Value bricks or by connecting to the User Parameters brick. Any unneeded portions can be omitted, since the default values are exactly what you typically want -- scaling of 1.0 in all dimensions and rotation/translation of 0.0 in all dimensions. You will, however, need to explicitly specify a Coordinate Source, since no default is provided.

    For ease of use, it’s worth adding in a brick to convert the rotation from degrees (which most of us find more convenient) to radians (which is the standard unit of rotation). The …>Mathematical>Standard Functions>Radians brick provides just what we need. (Just remember to set it to operate on the entire Float3 input and to compute a constant Direct Value.)

    Put it all together, and you get the set of bricks illustrated below. (The 3 bricks in the center column constitute the actual tiler.) Note that we don’t bother providing values for translation (because it’s not needed for this example) or Coordinate (because we actually want the default UV coordinate system for this example). Because we are operating in the UV coordinate system, we only need to specify scaling for X and Y and rotation for Z. We could just as easily use a Coordinate Source to operate in world coordinate system and provide separate values for scaling in X, Y, and Z directions or rotation around any of the axes.

    image

     

  • Studio94Studio94 Posts: 23
    edited June 2018

    thanks for this!

    * feel free to remove this digression or put it somewhere else if it doesn't fit!!! 

    The hint about Ambient Color was very helpful in getting more sanity between the OpenGL and iRay previews (again, for simple cases), but there's still quite a discrepancy in brightness [having tried to be careful with headlamps and scene lighting].  Do you expect reasonable correspondence? (if so, I'll keep looking for my mis-match).

     

    Using MDL Directly

     

    The Material Definition Language (MDL) is what lies beneath the (active) bricks used in the examples so far.

    There are some useful places to read about MDL

    It's a programming language, so not everyone is going to be comfortable with it.

    However, if you know that what you want is a grey colour that varies with position like this:

     

    grey = 0.5 * (sin (position * cycles * 2 * PI) + offset)

     

    Then actually, it can be a whole lot clearer to use that knowledge to build a custom brick to do it.

     

    Using "Many Bricks"

     

    Here's what that looks like when built up from pre-existing bricks ManyBricks.duf

    .image  image

    As expected, it gets really complicated, really quickly .. 

    Using a Custom Brick

     

    Studio needs to know where to find the MDL code we're going to write.

    For my part, I tend to keep experiments away from my general content installations (I have a "Manual Installs" folder, and under that I've got Shaders/MDL (for the MDL) and Shaders/Studio94 (for saving shaders from the Shader Mixer and as DUFs so that they can be seen from the Studio content interfaces).  

    • (mandatory) Map a folder to hold your MDL source (ShaderMixer>Edit>MDL Director Manager...) 
    • (optional) Map a folder to use to hold the Shader Mixer saves etc.

    The MDL for our example is in example_sin.mdl and looks like this:

     

    /****************************************************************************** * Silly example, just to illustrate how to glue things together. * Studio94, June 2018*/mdl 1.1;import math::*;export float sinusoidal_grey (   float position,   float cycles = 2.0,   float offset = 1.0 ){   return 0.5 * (math::sin (position * cycles * 2.0 * math::PI) + offset);}

     

    As you can see, the main part of our intended shader is quite clear.

    The other stuff is a wrapper part gives it a name (sinusoidal_grey) and tells us what the parameters are that we can vary (and their default values).

    So put that MDL text file into the folder you mapped (in my case Shaders/MDL)

    Now the neat bit -  you can then drag that MDL source text file to the Shader Mixer window - and when you do this, one of two things will then happen:

    1. It will show up as a Custom MDL brick (YAY!)
    2. It will pop up an error dialogue saying that the MDL could not be compiled (Oh well, typos happen)

    In the case of (2), life's a bit tedious - since the only place I've found to look for the problems is in the usual Studio error log file

    (aside) My wishlist would def. include the ability to edit MDL from the Shader Mixer and re-compile 

    Once you have the custom brick you can wire it up as before, and name it to reflect your intent.  Here's the DUF : SinusoidalGrey.duf (to use this don't forget to put the MDL file into your mapped MDL folder, or nothing will happen!).  As you can see, all the computation has been collapsed into the single brick leaving only the wrappers around it that we need to interface to Studio.  You can see the "MDL callable" has the name sinusoidal_grey as per our text above.

    image image

     

    Other Notes

     

    You can save and load custom bricks - but sadly they seem to get placed outside the content folders (in the place used by Studio for saving its internal state).

    It's not terribly "streamlined" yet in terms of needing to delve a bit into the internals - perhaps an editor / compile interface in the shader mixer would sove that, and provide for better / quicker feedback on changes).

     

    mdl
    mdl
    example_sine.mdl
    376B
    ManyBricks-shader.png
    1870 x 1044 - 345K
    ManyBricks.png
    512 x 512 - 40K
    CustomBrick-shader.png
    1862 x 1099 - 182K
    CustomBrick.png
    512 x 512 - 40K
    duf
    duf
    ManyBricks.duf
    68K
    duf
    duf
    SinusoidalGrey.duf
    39K
    Post edited by Studio94 on
  • Wow! Extremely nice write-up. Thank you so much!

  • mephoriamephoria Posts: 120

    @studio94

    Studio94 said:

    * feel free to remove this digression or put it somewhere else if it doesn't fit!!! 

    I've been experimenting with dragging MDL code into the shader mixer window - which works pretty well.

    My guess is, if you know you need sin(:pos: * PI / 180) / 2 + 0.5)”, then you'll most likely be able to represent it in an MDL fragment (which does simplify things to one custom brick).

    Your "digression" is quite appreciated, and the fleshed-out example even more-so. I've been avoiding getting into MDL custom bricks simply because it's an issue for packaging and distributing shaders to others, but it's certainly a very useful capability. I may yet do a more pedantic version of your example to show off all the subtle advantages and challenges, but your post already covers most of the necessary concepts. (It certainly taught me at least one thing that I didn't know. I had been under the impression that you needed more explicit "annotations" in order to make the MDL function parameters work properly.)

    I actually suspect that, for my own personal needs, I'm likely to start implementing the majority of my shaders in pure MDL, with the shader mixer merely providing a way to wrap the code for use in DAZ. I tend to be more comfortable with textual coding than most, so I actually find the graph to be an obstacle rather than an enabler. Alternatively, I might continue with graph-based coding, but using Substance Designer -- which appears to be more streamlined and bug-free than the Shader Mixer, and -- quite importantly -- provides near-instant feedback after each edit. I may comment on the results in this thread, but it would more properly rate a separate essay rather than a footnote in this thread.

     

  • Studio94Studio94 Posts: 23
    mephoria said:

    @studio94

    Studio94 said:

    * feel free to remove this digression or put it somewhere else if it doesn't fit!!! 

    I've been experimenting with dragging MDL code into the shader mixer window - which works pretty well.

    My guess is, if you know you need sin(:pos: * PI / 180) / 2 + 0.5)”, then you'll most likely be able to represent it in an MDL fragment (which does simplify things to one custom brick).

    Your "digression" is quite appreciated, and the fleshed-out example even more-so. I've been avoiding getting into MDL custom bricks simply because it's an issue for packaging and distributing shaders to others,

    * The need to map an MDL dir separately is somewhat irritating, especially since the default is actually inside the application package (at least on OSX) which is a no-no for changes.   

    * Likewise, the place for custom bricks is in the application folder ..  which ought not to be user-writable!

    I guess someone will have to try and package up an MDL based brick for distribution (the fact that the MDL is also in plain text might not suit every PA).

    Probably if PAs want to make much us of this, there would need to be some agreed place to put MDL in the normal Runtime/Data search paths etc.

    mephoria said:

    but it's certainly a very useful capability. I may yet do a more pedantic version of your example to show off all the subtle advantages and challenges, but your post already covers most of the necessary concepts.

    .. I could/should improve the formatting of the post.. will look at that later/tomorrow

    mephoria said:

    (It certainly taught me at least one thing that I didn't know. I had been under the impression that you needed more explicit "annotations" in order to make the MDL function parameters work properly.)

    I removed all the :annot: stuff from my example to make it easier to follow - a follow-up might be to do something a bit more 'production' in style.

    mephoria said:

    I actually suspect that, for my own personal needs, I'm likely to start implementing the majority of my shaders in pure MDL, with the shader mixer merely providing a way to wrap the code for use in DAZ. I tend to be more comfortable with textual coding than most, so I actually find the graph to be an obstacle rather than an enabler.

    +1 on those observations.. 

    mephoria said:

    Alternatively, I might continue with graph-based coding, but using Substance Designer -- which appears to be more streamlined and bug-free than the Shader Mixer, and -- quite importantly -- provides near-instant feedback after each edit. I may comment on the results in this thread, but it would more properly rate a separate essay rather than a footnote in this thread

    will be interested to read your experiences there - I looked at the two applications, but couldn't quite justify them for what I'm doing so far (the frustration of dealing with compile errors in the shader mixer might change my mind on that tho).

     

  • RoadwareRoadware Posts: 8
    edited June 2018
    mephoria said:

    Beyond Tiling -- Fun with Texture Coordinate Info

    ...

    image

    ...

    I'm Following this stuff pretty well, thank you so much!

    One question though. I'm following the above example, but can't figure out how to use the "Struct Get Members" brick. I enter the Stuct Type as "Bricks (Default)::Functions::MDL::Default Modules::base::Types::Texture Coordinate Info" I am not getting an error in the log so I guess that's right.. But I do not see the Outputs as shown above. I am using DS 4.10

    Thanks for your thoughts.

     

    Post edited by Roadware on
  • mephoriamephoria Posts: 120
    mephoria said:
    One question though. I'm following the above example, but can't figure out how to use the "Struct Get Members" brick. I enter the Stuct Type as "Bricks (Default)::Functions::MDL::Default Modules::base::Types::Texture Coordinate Info" I am not getting an error in the log so I guess that's right.. But I do not see the Outputs as shown above. I am using DS 4.10

    I'm sorry that I didn't cover this in the original post, as it originally confused me as well. There is no way to specify the struct type directly. Instead, you have to connect a structure output to the "ST" input, and the type will be filled in automatically. For the example you quote, I actually started with a node that provided a Texture Coordinate Info, connected it to the Struct Get Members brick, and then deleted it again to save space in the image. In retrospect, this wasn't a good way to illustrate what was going on.

    Note that you have to do similar things when using an Enum Value brick in order to provide the enum type. 

  • RoadwareRoadware Posts: 8
    mephoria said:
    mephoria said:
    One question though. I'm following the above example, but can't figure out how to use the "Struct Get Members" brick. I enter the Stuct Type as "Bricks (Default)::Functions::MDL::Default Modules::base::Types::Texture Coordinate Info" I am not getting an error in the log so I guess that's right.. But I do not see the Outputs as shown above. I am using DS 4.10

    I'm sorry that I didn't cover this in the original post, as it originally confused me as well. There is no way to specify the struct type directly. Instead, you have to connect a structure output to the "ST" input, and the type will be filled in automatically. For the example you quote, I actually started with a node that provided a Texture Coordinate Info, connected it to the Struct Get Members brick, and then deleted it again to save space in the image. In retrospect, this wasn't a good way to illustrate what was going on.

    Note that you have to do similar things when using an Enum Value brick in order to provide the enum type. 

    Thanks Sooooo Much!!! It works!

  • StratDragonStratDragon Posts: 3,167

    Bookmarking this, 

    This needs to be a PDF if it isn't already.

  • This is almost as rough as my first semester of Organic Chemistry. Our lab book was mostly a wall of text and we had to figure out most of the stuff ourselves. Never even knew "bricks" existed in Daz.

  • Thanks for this magnificient tutorial. I've been months stumbling around on the internet looking for an in-depth explanation of the Daz shader mixer and thrilled to find this. It's also a great bonus that you cover iRay as well. This is very much appreciated. 

  • I wanted to say "thank you", too. This thread is a great resource.

    Just in case, NVIDIA changed something between MDL 1.3 and 1.4 (didn't try whether 1.5 is supported, yet) which resulted in an error message:

    IOR in a fresnel_layer is a float in 1.4 and a color in 1.3

    Using 1.3 in the header still seems to allow a color, using one in 1.4 results in "cannot convert color to float". Which isn't a problem in itself, but hit me while using a code-block that used to work flawlessy before.

    I hope this helps to avoid the confusion I went through. ;-)

  • Thank you very much for this remarkable educational work. It has become one of my favorite tools. So much so that to facilitate access to the information it contains, I have gathered the posts in a PDF with a table of contents. I am attaching it to this post hoping that it will also be useful to many others.

    pdf
    pdf
    AnIrayShaderMixerCookbook_by_mephoria.pdf
    1M
  • AnimAnim Posts: 241

    @mephoria

    Many thanks for posting the information about shader building.

    @chachah

    Thanks for making the PDF

     

  • Andrey PestryakovAndrey Pestryakov Posts: 172
    edited January 2020

    Hello mephoria,

    Is it possible to use DS Shader Mixer to realtime generate a mask (Projection Type: Solid) for texture map so that all surfaces from the top are painted white and bottom are black?
    For example something like this:

    Post edited by Andrey Pestryakov on
  • JuNeJuNe Posts: 85

    Hello,

    Happy new year!

    Is there any way to have the same channel several times (completely new to shader builder)? I want to have different colours transmitted in different depths of the material (e.g. red should be filtered out after 5m, yellow after 10, green after 20, and so on). I figured I have to put in several Transmission Measurement Distance and Transmission colour Channels, but I might be wrong.

    Thank you very much!

  • RikGGRikGG Posts: 20

    Thank you for putting in all this work. A great starting place for someone at the "Hello World" stage of understanding Shader Mixers and MDL

  • RiverMissyRiverMissy Posts: 292

     

    The Material Definition Language (MDL) is what lies beneath the (active) bricks used in the examples so far.

    There are some useful places to read about MDL

    It's a programming language, so not everyone is going to be comfortable with it.

    The nvidia link isn't working, anyone have an update?  Thank you.

Sign In or Register to comment.