Home » 3D, Houdini

Writing a VEX shader for Houdini

22 May 2007 111,913 views 18 Comments

This tutorial is designed to show how easy it is to use VEX to create a simple shader. It’ll also illustrate how to use inline code in your VEX shader, as personally I believe it’s a lot easier to create, maintain, and debug a few lines of code rather than a giant spaghetti network of nodes and connections.

The goal is to create a light wrap shader. This is a common shader used in CG production that can help to suggest a more ambient lighting environment than the default Lambert shading achieves. It basically "pulls" light further round the surface and means that even normals facing away from the light can receive some illumination.

Starting with VEX

The first step is to create a VEX network; this will act as a container of your shader code. There are many different types of VEX network and they each have their own particular context. Each context makes certain information available appropriate to that context, so for example, the VEX Surface Shader context gives you access to the position and normal (in camera space) of the point being shaded.

  1. Open Houdini(!)
  2. Choose VEX Builder as current context (i.e. /vex/->)
  3. Press Tab. Select "VEX Surface Shader"
  4. Select new node and rename it to "lightWrap"
  5. In parameters, change the "SHOP Type Name" parameter to "Light Wrap"

A VEX Surface Shader network can be used to define a single instance of a shader or it can be used to create a digital asset. If you create a shader, the "SHOP Type Name" is the name that will be given to the shader that we create. If you create a digital asset, the "SHOP Type Name" is the name that will appear in the Tab menu when you create a new shader using that method.

The new VEX node

So let’s crack on with creating the basic structure of our shading network. The shader is simple, we first need to gather the illumination of the surface from each light while taking into account our light wrapping effect, and we then use that to illuminate the base color of the surface. To do the first stage, we need to use an "Illuminance Loop" node. This is what interrogates each light to find out how much light is hitting the surface.

  1. Press "i" or <Enter> to navigate into the VEX surface shader node.
  2. Press Tab. Select "Illuminance Loop".

We now need to give the illuminance node information about the point on the surface that it is shading. To do this we create a Global Variables node and this is where we get access to the context’s variables. We then just need to hook up the position and normal.

  1. Press Tab. Select "Global Variables".
  2. Connect "P" from "global1" node to "P" on the "illuminance1" node.
  3. Connect "N" from "global1" node to "N" on the "illuminance1" node.

The illuminance node has an optimisation feature that allows it to ignore any parts of the surface that face more than a certain angle away from the light. For our purposes however, we want to be able to shade all parts of the surface, even the ones facing away from the light (as that’s the whole point of the light wrap shader). So this is what the next step achieves by setting the angle threshold to 180.

  1. Middle click on the "angle" input on the "illuminance1" node and select "Create Constant".
  2. Select the new "angle" node and change the "1 Float Default" parameter to 180.

To get a value out of the illuminance loop, we need to give it something to modify. To do this, we feed a constant value in. This is initialised to black so that we don’t pass in an initial luminance in. If you wanted, you could expose this as a parameter instead of a constant and use it as a default flat ambience control.

  1. Press Tab. Select "Constant". Rename this node to "illum_value".
  2. In parameters for "illum", set "Constant Type" to "Color". Also, change "Constant Name" to "illum", and "Constant Label" to "Illumination". Verify that the "Color Default" on the "Color" tab is black.
  3. Connect "illum" of the "illum_value" node to the white "next" input of the "illuminance1" node.

The start of the VEX lightwrap network

We don’t yet have an output of the illuminance node to connect to. This is because we need to go inside the illuminance node to create a network to define the illumination. Until we do that, no output connection is available.

  1. Select the "illuminance1" node and press "i" or <Enter> to navigate into the sub network.
  2. Press Tab. Select "Inline Code".
  3. Connect "_illum" from "subinput1" to the white "next" input of the "inline1" node.

The "inline1" node is what will contain the code to calculate the light wrapping effect. But first, we need to give it a bit more information. Remember what I was saying about VEX networks each having their own context? Well the Illuminance Loop subnetwork also has it’s own context. Here, the global variables also give you access to the light color and the direction of the incident light from the current light being evaluated. We’ll also obtain the surface normal, although we could equivalently use the N output from the "subinput1" node.

  1. Press Tab. Select "Global Variables".
  2. Add "N", "Cl", and "L" from the "global1" node to the "inline1" node in a similar way.

Since we want to give ourselves control over the amount of light wrap, we’ll add a "wrap" parameter to this shader. Parameters are exposed in the user interface of the shader and allow the artist to modify the shader

  1. Press Tab. Select "Parameter". Rename this node to "lightwrap_param".
  2. In parameters for "lightwrap_param", change the "Parameter Name" to "wrap" and the "Parameter Label" to "Light Wrap". Set the "Float Range" to be from -0.99 to 10.
  3. Connect the "wrap" output of "lightwrap_param" to the white "next" input of the "inline1" node.

Typically, the useful range for this light wrap shader will be from 0 (the default Lambertian response) to 1, where the full range of lighting is still available. However, it can be useful to expose a greater range to the artist for "special effects". It just depends how much you trust the users of the shader not to go nuts with putting in daft values!

The next step is to create an appropriate output for the "inline1" node. Note that there is no "color" type for the output, but a vector works fine as it also is made up of 3 floating point components.

  1. Select the "inline1" node. In parameters, change the "Output 1 Type" to "Vector". Set the "Output 1 Name" to "out" and the second field (the label) to "out" as well.
  2. Connect "out" output of the "inline1" node to the "_illum" input of the "suboutput1" node.

The start of the illuminance network

The next page shows how to write the code to create the functionality of the light wrap shader.

1 Star2 Stars3 Stars4 Stars5 Stars (3 votes, average: 5.00 out of 5)


  • SG said:

    I tried this in h10 but no luck. Any idea what might have to change>?

  • AndyN (author) said:

    Hmmm. No, not off the top of my head. I should probably update this tutorial for version 10 anyway.

    I’ll take a look and get back to you, or you can send me the hip file if you like. I’ll ping you my email address in a sec.

  • SG said:

    Hey – thnks.
    Im pondering over it now. The VOP errors at the moment, may be the inline code, but i have no way of
    error checking it. Im trying nodes instead of code to see if this fixes it.

  • SG said:

    my gaff, its working just fine in H10 – i think i gave the wrap a constant instead of a parameter.
    Useful little vop – thanks.

  • AndyN (author) said:

    Cool, glad you got it working πŸ™‚

  • Danil said:

    Hey guys,

    I could not find this option:
    43. Go to the “Shading” tab of the “sphere” node and use the “+” button of the “SHOP surface” parameter to select the “lightWrap1” shader inside the “shop” branch.

    I have a sphere node selected and i am in Shading where Sampling and Dicing is but i cant find “”+” button of the “SHOP surface” parameter to select the “lightWrap1” shader inside the “shop” branch.” πŸ™

    Any ideas?

  • AndyN (author) said:

    Hi Danil,

    Yep, this tutorial is quite confusing if you’re trying to follow it in the latest version of Houdini.

    The problem is that the interface and method of shader assignment in version 10 has changed. Basically, you need to set up a material with the shader in and assign it to the object on the Material tab of the object. If you’re not sure how to do that yet, then you should probably investigate some other more fundamental tutorials before trying to decipher this one.

    I’ll try and get round to updating this tutorial soon.



  • Danil said:

    Hi AndyN,

    Ohhh now i got it πŸ™‚ Thanks for the explanation. Shader is working like a charm, i am actually using Houdini 9 here πŸ™‚ and trying to figure out last few steps from 50, and on.

    Also just wanted to say, very, very and i mean very good tutorial. I have been looking for something like that for god knows how long. This does not get any better that that. I hope to see any tutorials soon πŸ™‚ Maybe something about custom made SSS πŸ™‚

    Thank you.

  • Danil said:

    btw, by any chance do you know what would be the best way to output this as one of AOV in mantra render ?

  • AndyN (author) said:

    I’ve not needed to do that much, but I believe you can just export the parameter from inside the VOP network using a Parameter VOP with it set to Export Always. You can then pick it up from inside the ROP using the image planes.

  • Danil said:

    Hey AndyN.
    One more question, say i want to have light ramp happening only on the edge of the sphere. Like, something that would represent the light coming from the back side of the geo and then use Light Wrap to control how much light gets spieled over. I know it maya it would involved using facing ratio node that would use normal of the geo to figure out what is what.

  • AndyN (author) said:

    I’m not quite sure what you’re after, but you can generate a “facing ratio” value by doing the dot product of the Surface Normal (N) and the Eye Ray Vector (I) both of which you can get from the Global Parameters node inside the VOP network. You can use that to drive a ramp parameter.

  • kerem said:


    “We then create a temporary floating point variable called $t (it could be called anything, I just picked “t”) and take the dot product of L (the incoming eye ray) and N (the normal vector of the surface being shaded).”

    Is not “L” light vector?

  • AndyN (author) said:

    Hi Kerem. Yes, you’re exactly right. I’ve fixed it.

    Thanks for pointing it out.

  • Alex said:

    Hi Andy,

    Great tutorial you’ve written, I like the breakdowns and explanations especially. I know this was written for H9, but I’m using H11.1 and I’m running into a minor problem involving the viewport display of the shader.

    I’ve managed to get the material assigned to the sphere and can see the effect of the light wrap and diffuse color changes if I do a render or render region preview, but I can’t seem to get it to display in the viewport. I added in the OGL render parameters and can get the diffuse color to update, but can’t seem to figure out how to get the light wrap parameter to affect anything in the viewport. I don’t have this VEX shaded mode.

    Any help would be appreciated,

  • Alex said:

    hi Andy,

    OK, never mind about the previous question after reading the final page about requiring a render to see the effect…

    I was also wondering if this light wrap setting is easy to implement in a PBR shader like the default mantra surface shader in H11. I’m not sure where to insert it, must be somewhere in the surfaceModel?


  • AndyN (author) said:

    Hi Alex,
    Glad you figured it out. Yep, I could do with updating this tutorial, it’s looking a little dated!

    As for the PBR, that’s not something I’ve spent a lot of time looking in to, so I’m probably not the best person to ask. Sorry!


  • Alex said:

    Hi Andy,

    No worries, I posted a question to the sidefx forums and we’ll see if it turns anything up. Some of the PBR stuff isn’t well documented in my opinion.

    thanks again,