Custom Scripted Shader in 3D Studio Max

Hi to everyone again. Last time we talked about how to create a simple underwater scene using 3ds max and mental ray. Now is time to do the first exercise about scripting, we’ll try to understand some basic information about the 3ds max internal language, to be able in the future to create more complex custom tools, and in next weeks we’ll go back on some shading, lighting, rendering tutorials. I hope you’ll enjoy this tutorial, I’ll try to approach to it in a very artistic way jumping all the “programming” stuffs, but in future scripting tutorials we’ll need to talk about it so be prepared, and scared ! (I’m joking). Let’s see how we can create a custom shader plug-in that will work as a standard material, in our case we’ll try to do a simple X-Ray vision shader. Before we can start we have to prepare a test shader to understand which parameters and features we can need. So let’s create a very simple scene like in the figure and apply a standard material to the teapot.

Step 02

Set the Diffuse color = 193,222,254 and try to make a simple render test, we have the colored teapot,

Step 03

Go in the “Extended parameters” rollout, and set Falloff = In, Amt = 100, Type = Filter, make a render test, as you can see we have more transparency at the center of the meshes,

Step 04

Set the Shader type = Oren-Nayar Blinn, we’ll need it in future changes, for the moment we use it for a better light interaction, make a little render test, you’ll have something like in the figure,

Step 05

Now add a Falloff map in the Self illumination slot, set Falloff Type = Fresnel and Index of Refraction = 3.8, if you try to render it, you’ll obtain something similar to the image, as you can see the border are lighter than the center of the mesh,

Step 06

Add an Output map in the Diffuse channel, so we can controll the amount of diffuse usage, set the Map = a RGB Tint map to control the surface color,

Step 07

In the RGB Tint, set R as the shader diffuse color, G = 0,0,0, B=0,0,0, now we have again the base color. We created the base core to work on a X-Ray shader, it’s not so complex, but if we’ll create it using MAXScript, we can add features and the perfect interface to work on it, and to achieve better results in rendering.

Step 08

So, let’s start our 1st script. From the MAXScript menu, please open the MAXScript editor, in the image it is open and you can see in it our shader code. The 1st
part of the script, written in green is a comment, everytime you see green text it is a comment, so it does nothing. To create a comment, you have to start a line with “–”. At the beginning of a
software or a script, usually, it can be useful to write :

1. Script/software name,
2. revision number, date, etc.,
3. coder infos,
4. bug known,
5. bug to be fixed,
6. a “to do” list

as you can see in the figure and if you open the script, I wrote everything can be useful for this kind of

Then we’ll continue with the “PLUG-IN DECLARATION”, inside this part we have to declare which kind of scripted plug-in we are creating. So we have to write “plugin” clause, then the type of plug-in, (in our case “material”), and an ID to identificate the plug-in. Under this line we have to write the clause “name:”, then the name we’ll see in the material editor, (in our case “CGCookie | X-Ray”), going forward under this line we have to add the “classID” statement that identificate the plug-in internally in 3ds max to avoid duplicates, it’s a number you can choise freely in exadecimal format. Then the last line we can find start with the word “extends” to show that we are extending a real one plug-in or feature, in our case “Standard” as standard material, (in this case we are creating a standard material with a different interface and feature so we have to write which kind of plug-in we are changing), then we have to write if we are using a new interface or not, in our case the answer is YES, so we can write “replaceUI” as true. Perfect, the declaration is ready. I know that something will be difficult to understand, but this is normal at the beginning, we’ll try to understand everything with a series of tutorials.

Step 09

Now we have to define the interface of the shader, and first of all the parameters used and all the interaction that the user can have with it. The parameters stuffs will begin at the “INTERFACE DESIGN PARAMS” comment I wrote. Then to start writing the code we have to put the “parameters” statement, and we’ll write : “parameters main rollout:params”,which means literally : “here we declare the parameters for the main rollout of the interface that is named params”, it’ clear that later we’ll have to define the “params” rollout and all the interface stuffs to link the parameters to the interface. So now we are ready to prepare the stuffs behind the interface. Talking about it with simple words can be : we have some controls put inside the interface, when we change or use it something happens, to link the controls to the “happens” phase we need to declare a parameter for every single interface control, so if we have a spinner that controls the transparency level of the material, we need to declare is parameter controller here. To do it we need to give a unique name, the type of datas that can manipulate or contains, the default value and the link to the interface object. So, we’ll have :

Transparency type:#float default:100 ui:trans

this is the 1st parameter and literally it means : “Transparency” is the name we give to the parameter, “type:#float” means that the number can expressed as floating point value, “default:100” = as soon as we lanch the plug-in the value will be 100, “ui:trans”, it means that this value is connected to the object named “trans” that we’ll find later in the interface description. So we can continue with the parameters declaration :

Color type:#color default:[203,232,255] ui:col

Le type:#float default:1.8 ui:output

two type:#boolean default:false

inpurity type:#float default:0 ui:distort

imp_scale type:#float default:1.0 ui:purity_scale

when you find “type:#boolean”, means that the value can be “TRUE” or “FALSE”

Let’s move on the “INTERFACE ACTION MANAGEMENT” section. Here we put all the code that we need to tell 3ds max what it has to do when the user interact with the interface objects, so if we change a spinner value, here we must insert the right code to menage this event. The material we are creating will be managed with the literal name “DELEGATE”, so when we have to indicate it or work on it, we have to use this words. MAXScript is object-oriented and part of this kind of programming methodology is the properties and the father/daughter possibilities. Everything can be an object and every object has properties, to move in this structure, we’ll have :

DELEGATE = our material

TWOSIDED = the two sided switch properties we can find in the standard material

so if we have to work on this properties we’ll write : DELEGATE.TWOSIDED and then the value, so if we put “delegate.twosided = on”, we are saying to activate the “twosided” feature in our scripted shader.

So for the moment we’ll write all the events’ code, and then we’ll understand better it when we’ll put the interface inside also.

To manage an event we have the programming structure “ON parameter SET VAL DO action code”, it means literally : “WHEN the parameter named x WILL CHANGE ITS VALUE DO these actions”. So for example :

ON Transparency SET VAL DO delegate.opacityFallOff = VAL

it will change the amount of the opacity falloff of the shader assigning it the value of the “Transparency” controller that is linked to the “trans” spinner in the interface. So let’s continue with all the events :

on Le set val do delegate.diffuseMap.map1.output.Output_Amount = val

(when the “Le” parameter change its value, this value will be assigned to the “Output” parameter of the map put in the diffuse slot)

on Color set val do delegate.diffuseMap.red = val

(when the “Color” parameter change its value, this value will be assigned to the “Red” channel of the diffuse map, and we’ll notice that this map will be a RGB Tint, so we’ll change literally the “R” color of this map)

on two set val do delegate.twoSided = val

(when the “two” parameter change its value, this value will be assigned to the “twoSided” property of the shader so we’ll change its state to ON or OFF)

on imp_scale set val do delegate.selfillumMap.map2.size = val

(when the “imp_scale” parameter change its value, this value will be assigned to the “size” value of the map put in the 2nd slot of the map put in the self illumination slot of the shader, in our case it will be a Noise map, so we’ll change the size of the turbulence)

on inpurity set val do
delegate.selfillumMap.map2Amount = val

(when the “inpurity” parameter change its value, this value will be assigned to the “amount” value of the map #2 put in the self illumination
map, so literally we’ll change the amount of usate of the noise we talked about before)

We have all the events we need, we can proceed with the interface.

Step 10

In this figure we see the interface put in the material editor, and the code we need to write to create it, so let’s describe it step by step. To create the interface we have to
create the rollout that will contains all the parameter, a rollout can contains sub-rollouts but for the moment it is not interesting for us. To create a rollout we have to use this code structure :

rollout rollout_name “label given by the user”

(interface controls put here)

So in our case it will became :

rollout params “X-Ray |
base features version |”

(–all the controls)

and as you can see in the figure, we can find the rollout with the informations we wrote here. Now we have to create the logo bitmap, and the parameters to controls color, transparency, two-sided option, inpurity etc. and you can see it in the interface, so let’s do it step by step :

bitmap logo_sea “Bitmap” pos:[6,5] width:313 height:61 filename:”c:logo.bmp

(we are creating a bitmap object that can show images, the name given is “logo_sea”, the position in the interface is set by “pos:[6,5]”, which means 6 pixels on X axis and 5 on the Y axis, the scale of the object is in pixel and is set to 313 for the X axis and 61 for the Y axis, then we have the iamge file that must be loaded set by the
“filename:” value. If the file doesn’t exists we’ll have no errors but no image will be showed)

After this line you can see that in the interface we have a little box that includes all the shader parameters and it’s named “X-Ray base Parameters”, to create it we have to use this structure :

group “name of the group”

(list all the controls here)

Out of this box you can see the informations about the shader version, coder, website etc.

So let’s see how we can finish the interface, after the bitmap line please put this code :

group “X-Ray base Parameters”


colorpicker col “X-density color (dc): “ align:#left offset:[0,0]

(we are creating a color picker control with all the same possibilities given by 3ds max, so you can click on it to change the color, or you can drop a color from an other one, etc etc. the color picker is named “col” and you can find it in the parameters description wrote before to understand how we linked it. With “X-density color (dc)” we wrote the label that will be found in the interface with the color picker, and we set the alignment to the left side of the panel using the “align” statement and we have no offset setting “offset” = 0,0.)

checkbox twoside “2-sided vision” align:#center offset:[210,-23]

(we are creating a check box object which can be used to activate or not a feature because it can be on or off as you can see, as the line before, we can find the name “twoside”, the text label and the alignment and offset parameters)

output “Light emission (le): “ align:#left range:[1.0,50.0,1.0] fieldwidth:45 offset [0,0]

(we are creating a spinner object to manipulate the output value of the diffuse map, as you can see we have everytime the same parameters to controls name, text label, alignment and offset, but here we have also “range:” to control which kind of value the user can gives to the spinner, in this case we have minimum set to 1.0, maximum set to 50.0 and default value set to 1.0, then we have the
“fieldwidth:” that controls how long can be the spinner text space)

spinner trans “Transp.: “ align:#right fieldwidth:45 offset:[-90,0]

(here we are creating the spinner that will controls the transparency level, using always the same kind of scheme for the alignment and offset)

spinner distort “inpurity amt.” fieldwidth:45 offset:[-90,0] range:[0.0,100.0,0.0]

(the spinner that controls the amount of usage of the Noise map put in the self illumination channel with all the range and alignment and text informations)

spinner purity_scale “inpurity scale” fieldwidth:45 offset:[-90,0] range:[0.01,500.0,0.1]

(and finally the spinner that controls the noise scale)


label abt0 “all the text you like to put on the interface with your name, shader release etc”

(with the “label” command we can put a text inside the interface)

Every single part we are describing must be inside a “(“ at the begining and “)” at the end, to be sure you are writing right and to not receive error messages, please check the source code we are giving with the scene file.

Step 11

So, we are near at the end of our 1st scripted material, it’s not so simple because we are talking about programming, but I hope that step by step it will became more simple to you. Now we need to create the material to work on, so literally we have to create the DELEGATE in the material editor to add on it the interface we just created.

To create the material we need to use the structure :


(and here you have to put all the action needed to create the material )

So to create the x-ray shader we have to control the diffuse channel, the opacity and the self illumination channel.

Let’s start to prepare it.

To start the shader description we have to say the type of shading system used, and the base parameters :

delegate.shaderType = 4 (we are setting the shader to Oren-Nayar Blinn)

delegate.specularLevel = 0 (this line set the specular level to 0)

delegate.glossiness = 0 (and this one the glossiness to 0, so we have not specularity)

delegate.opacityFallOffType = 0 (with this line, we enter in the “Extended Parameters” and we set the Falloff to “In”)

delegate.opacityFallOff = 100 (and the amount of falloff opacity to 100, so we have complete transparency at the center of the object)

So now we can setup the diffuse channel :

delegate.diffuseMap = RGB_Tint() (we are putting a RGB Tint map in the diffuse channel slot)

delegate.diffuseMap.red = [112,193,255] (we are changing the R value of the RGB Tint to set the X-Ray color)

delegate.diffuseMap.green = [0,0,0] (we set to black the G color of the RGB Tint)

delegate.diffuseMap.blue = [0,0,0] (we set to black the B color of the RGB Tint)

delegate.diffuseMap.map1 = output() (we put a Output map in the map slot inside the RGB Tint map, to control the output value as it is a luminosity map)

And now we can work on the self illumination channel :

delegate.selfillumMapEnable = on (we are enabling the self illumination map)

delegate.selfillumMap = falloff() (we are putting a Falloff map in the self illumination slot)

delegate.selfillumMap.type = 2 (now we set the falloff type of the Falloff map to Fresnel)

delegate.selfillumMap.ior = 3.8 (and now we are setting the IOR of the fresnel falloff to 3.8)

delegate.selfillumMap.Amount = 100 (we are setting the amount of usage of the map just created)

delegate.selfillumMap.map2 = Noise() (and we are putting a Noise map in the 2nd map slot that you can find in a falloff map)

delegate.selfillumMap.map2.size = 1 (with this line we can set the Noise scale to 1)

delegate.selfillumMap.map2Amount = 0 (and with this one the amount of usage of the noise map we just inserted)

delegate.selfillumMap.map2on = on (we are saying to use the noise map)

delegate.selfillumMap.map2.thresholdHigh = 0.615 (we are setting the High value of the noise threshold)

delegate.selfillumMap.map2.thresholdLow = 0.275 (and now the Low value of the noise threshold)

delegate.selfillumMap.map2.color1 = [155,155,155] (we are changing the color n.1 from black to a grey 155)

We have the entire shader ready.

Step 12

Now we can execute our scripted shader, so you can go in the “Tools” menu and click on Execute All, remember that if you wrote something bad or you lost some “()” you’ll receive errors, so if it will happen, please check your shader with our shader to understand if you missed some characters or not, if you have problems, you can contact me using the “comments” to the tutorial. Now our script has become a plug-in so you can find it in the material editor,

Step 13

If you go in the material editor and you try to create a new material in the slot, you can find in the shader list “CGCookie | XRay”,

Step 14

You can create it in the slot, assign it to the teapot and set Light Emission = 8.7, Transp. = 91.9, inpurity amt. = 62.6 and inpurity scale = 0.2, try to make a render and you’ll see something similar to the image,

Step 15

Here you can see a render test done using “Blend” anti-alias and our shader with inpurity amt. = 100,

Step 16

Now we make a test on a more complex

Step 17

So if you set Light emission = 7.7, Transp. = 95.8, inpurity amt. = 100 and inpurity scale = 0.2, you can achieve something like this, you must remember to activate 2-sided vision checker also,

Step 18

Now we can make a test to see more details, move the camera near the mesh, set our shader using Light emission = 5.7, Transp. = 100, inpurity amt. = 60, inpurity scale = 0.4 and 2-sided vision = on, make a render test.

So we saw a base shader done with the scripting, in our tutorial we prepared a more interesting interface to control a standard shader and it can be useful to prepare shaders for artists so they can use only the features you prepared for them. A more interesting but complex possibility is to create hard coded part of the shader for maps, procedurals and internal features, maybe soon we’ll talk about it in future tutorials, after we’ll understand some other MAXScript features and programming stuffs.

By: Visit my website at: http://www.alessandrocangelosi.com/


Leave Comment


12 Responses to “Custom Scripted Shader in 3D Studio Max”
  1. Posts: 18
    Infaas says:

    Hi Alessandro , I really like your tutorials , they are very helpful indeed.

    By the way, Did you upload any images for the above tutorial ? I am new to this scripting thing and yes a little scared 😉 , If I see images of this tutorial it will be easier to follow , you know a friendly tut :)
    If you are busy with other stuff its okay, you don’t have to take the trouble.
    Maybe for the future tutorials on scripting you could add images :)

    Thank you

    • Posts: 111
      a.cangelosi says:

      thanks for your interest in our tutorials and thanks for the suggestion, next time I’ll add more images on scripting tutorials.

      Stay tuned for more news from us.

  2. Posts: 1
    404 says:

    images gone 404

  3. Posts: 1
    manideep says:

    show me vedio tutroils

  4. Posts: 3
    Jone says:

    Can you please explain the “UI” part again. If am creating a shader in FinalRender what is the name i need to type in UI for Color?

    • Posts: 1026
      a.cangelosi says:

      Hi Jone,

      every 3rd party renderer/plug-in has its features exposed to MaxScript, maybe not the entire feature set, but a lot of it. So to find the right syntax we can use the MacroRecorder, it can be really helpful. Soon we’ll release a tip video to see how we can use it. So for the moment I can plan a new tutorial to see how we can develop scripts interacting with 3rd party plug-in.

      I’ll try to release it soon.


  5. Posts: 3
    Jone says:

    also after creating the shader, how can i share it?

    • Posts: 1026
      a.cangelosi says:

      To share your scripted tools, it is really simple, you can release your code in “.ms” file format, and everyone who want to use it, must run it, or can copy it in the start up folder in 3ds max. There’s the possibility to encrypt the code, so it will be not exposed but the scripted tool will remain usable from everyone, you have to use the syntax : “EncryptScript <>” to obtain in output the encrypted version. If you need more help please contact me.


  6. Posts: 1

    Excelente como todos tus tutoriales.
    Gracias por compartirlos.

    • Posts: 1026
      a.cangelosi says:

      Hi Lucas, thanks for posting. We hope you’ll continue following us !

  7. Posts: 1
    l0lo86 says:

    Thank you very much for this tutorial. I just did it on max 2013. It seems to be working, the listener tells me “Xray Shader OK”
    but when I try to assign it I cannot find the material in the material list.
    Would you know if there is something more I need to do on new versions of Max ?

Leave a Comment

You must be logged in to post a comment.