## Writing a VEX shader for Houdini

22 May 2007 96,940 views 18 Comments

### Coding VEX with the Inline Tool

Now we get to do the fun stuff. This is where we write the shading code to create the light wrap effect.

1. Select the "inline1" node and press Alt + e in the "Inline VEX Code" parameter to open the multiline text editor.
2. Paste the following code into the editor and press Accept.
```	\$out = {0,0,0};
float \$t = dot(normalize(\$L),normalize(\$N));
\$t = clamp(fit(\$t+\$wrap,0,1.0+\$wrap,0,1),0,1);
\$out.x=\$_illum.x + \$t*\$Cl.x;
\$out.y=\$_illum.y + \$t*\$Cl.y;
\$out.z=\$_illum.z + \$t*\$Cl.z;
```

Note that anything with a \$ in front of it is a variable, and matches the name of the input connection for the "inline1" node. The Inline Code node uses the \$ sign to make sure that each variable name that you use is unique (the entire VOP network that you’re creating is ultimately used to generate one big chunk of VEX code, so doing this avoids variable name conflicts later on).

I’ll take you through the code line by line. The first statement simply initialises the output to something sensible.

```	\$out = {0,0,0};
```

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 light ray vector) and N (the normal vector of the surface being shaded).

```	float \$t = dot(normalize(\$L),normalize(\$N));
```

We have to use the normalize() function on each of these two vectors to make sure that they both have a length of 1. By doing this, the dot product returns the cosine of the angle between the two vectors. This value can be used directly to shade the surface and gives the default Lambert style shading.

However, the next line will modify this value to give us the light wrapping effect.

```	\$t = clamp(fit(\$t+\$wrap,0,1.0+\$wrap,0,1),0,1);
```

This isn’t as complex as it looks. From the previous line \$t holds a single value somewhere between -1 for a surface facing directly away from the light and 1 for a surface facing directly towards the light. We want part of the surface facing away from the light to be treated as if it’s facing the light, and so we want to change \$t to this:

```	\$t+\$wrap
```

Unfortunately, our value could now be above 1, so we use the fit() function to squeeze it back into the correct range:

```	fit(value, old_min_value, old_max_value, new_min_value, new_max_value)
```

where

```	value = \$t+\$wrap

old_min_value = 0
old_max_value = 1.0+\$wrap

new_min_value = 0
new_max_value = 1
```

Note that any value that falls outside the range (i.e. the possible negative values) will also get scaled by this function and won’t get clamped to the new minimum and maximum values. This is why we finally use clamp() to make sure no values fall outside the 0 to 1 range. Any negative values will get set to 0 which is exactly what we want, as there should be no illumination in those regions (and certainly not negative illumination which makes no sense!).

Finally, we multiply the surface illumination by the light color and add it to the input \$_illum variable. It is important to add our result to \$_illum as it contains the result for any lights that we’ve previously encountered in the Illuminance Loop. We then set \$out to the result. Note that since we’re using a vector to represent color, we’re setting it to the x, y, and z components as opposed to r, g, and b (although in VEX, there is no difference between using x,y,z or r,g,b – I just prefer to stick to the type declaration, which in this case is a vector).

```	\$out.x=\$_illum.x + \$t*\$Cl.x;
\$out.y=\$_illum.y + \$t*\$Cl.y;
\$out.z=\$_illum.z + \$t*\$Cl.z;
```

That’s it. That’s all there is to the code.

We just have a few other things to add to the VEX network to complete the shader. We first need to add a control for the artist to be able to set the color of the surface.

1. Press "u" to step up out of the illumination subnet.
2. Press Tab. Select "Parameter". Rename this node to "diffuse".
3. In parameters, change "Parameter Type" to "Color", "Parameter Name" to "diffuse", and "Parameter Label" to "Diffuse". Change "Color Default" on the "Color" tab to white.

The final illuminance network

Now, we need to multiply the color of the surface by the result of the illumination loop.

1. Press Tab. Select "Multiply".
2. Connect the "diffuse" output of the "diffuse" node to the white "input1" of the new "multiply1" node.
3. Connect "_illum" of the "illuminance1" node to the new white "input2" of the new "multiply1" node.
4. Connect the "product" output of the "multiply1" node to the "Cf" input of the "output1" node.

Okay, that’s the VEX work finished. On the next page we’ll create a shader directly from this network.

(2 votes, average: 5.00 out of 5)

• SG said:

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

• 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.

Cheers

Andy

• 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:

Typo?

“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,
thanks!
alex

• 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?

thanks
alex

• 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!

Cheers,
Andy

• 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,
peace
alex