VEX Shaders for physically based rendering (PBR)

This is a new feature in Houdini 9. To learn about more new features, see what’s new in Houdini 9.

Overview

Writing a PBR surface shader involves designing a bidirectional scattering distribution function (BSDF) for the surface. This function describes how light scatters when it hits the surface. BSDFs are an opaque data type in VEX that you can store and operate on much like other primitive types.

You output the final BSDF for the the shader through a new global variable F. In VOPs, you connect a BSDF to the F connection of the output node (see VOPs below).

The BSDF is a self-contained description of how a surface reflects light. With PBR, you don’t need to compute illumination (for example, using illuminanceloops or trace() calls). You only need to provide a bsdf.

You don’t write the scattering function yourself. You create BSDFs by combining primitive BSDFs using addition, multiplication, and scalar multiplication. See “primitive BSDFs” and “combining BSDFs” below.

Depending on the settings selected for the render, mantra can use the BSDFs calculated by your shader in many different ways. For example, it may perform direct lighting, indirect lighting, or evaluate lighting in reverse for algorithms like photon mapping.

You can design shaders that support both traditional and physically based rendering. You just need to assign values to both types of output variables (Cf, Of, and Af for traditional rendering, F for PBR rendering) in your shader.

Primitive BSDFs

phong

  1. bsdf phong(float exponent)

  2. bsdf phong(vector nml, float exponent)

A phong reflection.

  • exponent - phong exponent.

phonglobe

  1. bsdf phonglobe(vector dir, float exponent)

A phong (blurred) reflection along a given direction vector. This will produce the same result as phong() when the direction vector is the reflection vector, but with this function you can also gather illumination from other directions (such as transmission).

  • dir – the direction of specularity.

  • exponent – phong exponent.

ashikhmin

  1. bsdf ashikhmin(float exponentx, float exponenty, vector framex, vector framey)

  2. bsdf ashikhmin(vector nml, float exponentx, float exponenty, vector framex, vector framey)

An anisotropic bsdf similar to phong() but with independent controls for the highlight size along 2 tangent vectors.

  • exponentx – phong exponent along the framex vector.

  • exponenty – phong exponent along the framey vector.

  • framex – highlight X direction.

  • framey – highlight Y direction.

blinn

  1. bsdf blinn(float exponent)

  2. bsdf blinn(vector nml, float exponent)

A blinn highlight.

  • exponent – blinn exponent.

matchvex_blinn

  1. bsdf matchvex_blinn(float exponent)

  2. bsdf matchvex_blinn(vector nml, float exponent)

The BSDF produced by blinn is not the same as the traditional VEX blinn() output. Use this function to produce a closer approximate match to the traditional VEX blinn().

specular

  1. bsdf specular(vector dir)

A mirror specular reflection along a given direction vector.

  • dir – the direction of specularity.

matchvex_specular

  1. bsdf matchvex_specular(float exponent)

  2. bsdf matchvex_specular(vector nml, float exponent)

The BSDF produced by specular is not the same as the traditional VEX specular() output. Use this function to produce a closer approximate match to the traditional VEX specular().

cone

A cone reflection along a given direction vector. This BSDF is constant within the given angle, producing a similar result to the gather or irradiance loops.

  • dir – the direction of specularity.

  • angle – cone angle in radians.

diffuse

  1. bsdf diffuse()

  2. bsdf diffuse(vector nml)

Diffuse reflections. This BSDF has an albedo of 0.5.

wireblinn

  1. bsdf wireblinn(vector tangent, float exponent)

Blinn function defined around a tangent vector. You can use this to produce the average specular illumination for thin wire-like primitives such as hair.

  • tangent – tangent vector along the hair.

  • exponent – blinn exponent.

wirediffuse

  1. bsdf wirediffuse(vector tangent)

Diffuse function defined around a tangent vector. This can be used to produce the average diffuse illumination for thin wire-like primitives such as hair.

  • tangent - tangent vector along the hair.

Combining BSDFs

  • bsdf +(bsdf, bsdf)

    Sum together two bsdfs. Summing bsdfs allows you to include more than one type of component, such as summing together a diffuse() and a phong().

  • bsdf *(float scale, bsdf)

  • bsdf *(vector scale, bsdf)

    Scale a bsdf by a color or a scale factor. Scaling a bsdf can allow you to incorporate surface colors (such as texturing) into the bsdf.

  • bsdf *(bsdf, bsdf)

    Create the produce of two bsdfs. This function is most useful for scaling a specular bsdf by a diffuse().

  • vector albedo(bsdf)

    Return the portion of reflected light for a bsdf. The return value should be between 0 and 1 for bsdfs that conserve energy. The albedo for a default diffuse bsdf is 0.5, but will change based on vector scale factors and other bsdf components.

VOPs

You can build a PBR shader using VOPs, by generating a BSDF (yellow) output and piping it to the F connection of the output node. Most traditional lighting VOPs now generate a bsdf output in addition to the traditional outputs.

This is an example of a VOP network that creates a specular bsdf and diffuse bsdf, adds them together (in add1), and sends the resulting bsdf to the output.