Houdini 12 Rendering

Background information about how mantra works.

Generating IFD

  • Rendering from Houdini begins with Houdini using SOHO to generate an IFD file describing the scene. You can save the IFD file to disk or pipe it directly to mantra, the native Houdini renderer.

See how to set up a render node and how to render for information on how to start rendering.

Sampling

  • Mantra’s micropolygon rendering is based on the REYES algorithm. It is a divide and conquer algorithm, a strategy whereby a difficult problem is divided and sub-divided into smaller and smaller problems until it is decomposed into a large number of simple problems. For micropolygon rendering, this takes the form of refinement.

    • The REYES model is almost always working on quad meshes. One quad has 4 vertices and 1 poly. However, a 10×10 quad mesh has 121 vertices and 100 polys. So, as soon as the meshes get to a non-trivial size, the polycount and vertex count become similar.

  • In micropolygon sampling, mantra refines the geometry until it is small enough to dice into micropolygons.

    • Refinement is the process of splitting large polygons, NURBS surfaces, etc. into smaller primitives until a primitive is small enough to “dice” into a grid of micropolygons: polygons about the size of a single pixel (the default, although you can target larger or smaller micropolygons using the shading quality setting). Polygons that are already small enough will not be split before being diced, and polygons smaller than the size of a single micropolygon cannot be annealed into larger micropolygons.

      (This clever strategy means the renderer doesn’t need to know how to rasterize large polygons into pixels. It only needs to worry about one pixel at a time.)

    • Mantra dices polygons into micropolygons in batches called buckets (by default, mantra uses 16 by 16 pixel buckets). Because each bucket is rendered independently of its surroundings, the renderer can throw away all the computed micropolygon geometry for the bucket once it’s been shaded, saving memory.

  • In raytracing, mantra does not refine geometry if it knows how to raytrace it natively.

    • The raytracing engine has algorithms to do efficient raytracing of points, circles, spheres, tubes, polygons, and mesh geometry.

    • Other surface types, e.g. subdivision, NURBS, and any surface with a displacement shader are refined into micropolygons, and the renderer intersects the rays with the micropolygons. This is important to remember because refinement requires more memory. If you can reduce the amount of geometry in the scene that requires refinement, the raytracer will use less memory.

  • Refinement requires the creation of duplicate, independent refined geometry.

    • Refining geometry takes more memory. When raytracing geometry that requires refinement (such as subdivision surfaces or polygons with a displacement shader), try using simple base primitives, such as lower resolution meshes, to reduce memory use.

    • Refining instanced geometry may create independent refined geometry for each instance, defeating the memory savings of instancing. (Subdivision surfaces and NURBS may still share some data if the instances require the same detail level. Only displacement requires wholesale duplication of each instance.)

    • Refined geometry is cached during raytracing, and there is a limit to the amount of refined geometry mantra stores for raytracing. The cache size is controlled by the geometry cache size parameter.

  • Mantra samples each pixel to find out what its color should be.

    • In raytracing, mantra runs the shaders at each sample. It then averages the samples. The number of samples is controlled by the Pixel samples parameter.

    • In micropolygon sampling, mantra runs the shaders once at each vertex of the micropolygon grid. It then takes samples across the pixel. The values of the samples are interpolated from the values at the corners (where the shaders ran). The number of samples is controlled by the Pixel samples parameter.

      (If the vm_smoothcolor property is off, the sample simply gets the value from the bottom left vertex instead of interpolating. This might be useful if you're sampling plate geometry and don’t want interpolation across its face.)

    • In micropolygon PBR shading, mantra runs the shaders at the vertices of the micropolygon grid. However, because PBR depends on taking many shading samples, it runs the shader many times (controlled by the Max ray samples parameter), sending rays in random directions controlled by the BSDF. As with regular micropolygon rendering, it then takes samples across the micropolygon. The values of the samples are interpolated from the values at the corners (where the shaders ran). The number of samples is controlled by the Pixel samples parameter.

    Because the Pixel samples parameter controls how many sub-pixel samples are averaged together to get the pixel color (whether the shaders run at the samples or the values of the samples are interpolated from the vertices), increasing the pixel samples gives better anti-aliasing for geometry edges, motion blur, and depth of field.

  • Micropolygon rendering runs the shaders roughly once per micropolygon and interpolates the results during sampling. Raytracing runs the shaders on every sample.

    • So raytracing will often (but not always) run the shaders more often and so will be slower (especially since each shader run might send out a large number of secondary rays to do reflection, occlusion, etc.), but raytracing gives more accurate results, because the shaders are run independently on each sample, instead of the sample values being interpolated from the micropolygon vertices.

    • Raytracing will not always run the shaders more often than micropolygon sampling. Consider a model with 10 million polygons that is distant from the camera (so relatively small in the image).

      In micropolygon rendering, the model will have 10 million micropolygons (the renderer can’t merge polygons, so the number of micropolygons is never smaller than the original number of polygons), so the shaders will run about 10 million times.

      In raytracing, the renderer runs the shaders based the number of pixel samples. So if the number of pixel samples is 9 (3×3, the default) and the model takes up 10 pixels in the image, the shaders will run 90 times.

  • Samples are distributed randomly in space (across the face of the pixel) and in time (across the shutter open time of the frame, during which time the geometry may move).

    • In raytracing, the shaders run on every sample, so the shaders are run at different sub-frame times, possibly with the geometry in different positions, giving accurate anti-aliased motion blur.

    • In the micropolygon engine, mantra always runs the shaders at the beginning of the frame.

      The color is always determined at the beginning of the frame, but because samples are taken across the shutter open time, micropolygons in motion will be smeared, giving fast but less accurate motion blur.

      This only works for micropolygons in motion. For example, shadows of a moving object that fall on a stationary object will not be motion blurred, because the stationary object gets its shading at the beginning of the frame and its micropolygon are not smeared because it’s not moving. To get motion-blurred shadows, use raytracing instead of micropolygon sampling. (You can however add the Mantra x.x/Sampling/Raytrace Motion Blur (vm_traceblur) property, which when enabled will distribute secondary rays sent by the shader across time, allowing you to achieve the same look with micropolygon rendering.)

Traditional shading

  • Shaders are programs the renderer runs to allow specification of various aspects of rendering, such as the color of surfaces. Mantra’s shaders can be hand-written in the VEX language, or created by wiring together VOP node networks, which are automatically converted to VEX by Houdini.

  • Mantra runs five types of shaders: Displacement, Surface, Light, Shadow, and Fog.

    • Displacement shaders run first. A displacement shader can move the corner of the micropolygon. Displacement shaders work by modifying the global variables P (the vertex position) and N (the vertex normal, since changing the position usually changes the normal).

      An important factor for displacement shading is the displacement bounds setting. Since micropolygon rendering only looks at a small part of the scene at a time, if a displacement would move a corner outside the area mantra is looking at, the micropolygon will be clipped, resulting in holes. The displacement bounds setting tells mantra the maximum displacement distance (in Houdini units) you expect in the scene. Mantra uses this number to increase the amount of geometry it keeps in memory, so you should increase it to the maximum displacement in the scene but no higher.

      Displacement shaders can store values on the vertex using export parameters, for example it could store the predisplaced position and/or normal, the displacement distance, etc. for use by the surface shader. A surface shader can retrieve the stored values using the dimport VEX function or Import Displacement Variable VOP.

    • Surface shaders run after displacement shaders. Surface shaders are responsible for calculating the color of the surface at the sampled point.

      Surface shaders work by setting the following global variables:

      Cf

      The surface color.

      Of

      The surface opacity.

      Af

      The pixel transparency (alpha).

      Although they sound similar, surface opacity and pixel transparency are different and can be used for different effects. Of (surface opacity) affects the visibility of surfaces behind the one being shaded. Af (pixel transparency) affects the transparency of the resulting pixel in the rendered image. The output values of the Cf, Of, and Af variables are stored in the sample.

    • Surface shaders can invoke raytracing using various VEX functions or VOP nodes to send out secondary rays. Each of those rays get sampled and shaded and return their values to the invoking shader.

      For example, A shader might send a reflection ray if the material is reflective (or multiple reflection rays for a blurred reflection). It might send refraction rays if the material is refractive. It might send occlusion or irradiance rays for more realistic lighting.

      So, in “traditional” (non-PBR) shading, the surface shader (or light shader) determines how many secondary rays to send and the distribution of the rays. This may lead to inefficiencies where a light might compute hundreds of ambient occlusion rays, even for surfaces (such as highly reflective materials) where the light contributes very little to the final color. This situation is more gracefully handled by PBR shading.

    • Surface shaders can cause light and shadow shaders to run if they call an illuminance loop. An illuminance loop iterates over each light, calling the light’s light shader, which sets the Cl (light color) variable inside the loop. The code in the loop may then call shadow(Cl), which uses the light’s shadow shader to modify the variable. The variable L (direction from the surface to the light) is also available inside the loop.

      Of course, the code in the loop may ignore these values or use them in different ways. For example, here’s some pseudo-VEX code that uses the shadow shader to tell whether the surface is in shadow, but colors all unshadowed areas blue and all shadowed areas red.

      illuminance(P) {
          Cx = Cl
          shadow(Cl)
          if (Cl == Cx) {
              Cf = blue
          } else {
              Cf = red
          }
      }
      
    • Fog shaders run after surface shading if they are present. Fog shaders can modify the final Cf (surface color) and Of (surface opacity) of every surface. You can add a fog shader to the scene by creating an Atmosphere object at the object level and specifying the shader in the Atmosphere object’s parameters. In general, however, rendered volumes are a much more accurate and flexible way to simulate atmospheric effects than using a fog shader.

    • Variable arguments to the shader will be overridden by any attributes on the shaded geometry with the same name, allowing the shader access to arbitrary attributes. There are also various useful global variables. For example the P variable contains the coordinates of the point being shaded, and the dPds, dPdt global variables contain the delta between the current position (P) and the position of the neighboring micropolygon vertex along S and T. You can use these vectors to get the approximate length of micropolygon edges. See the list of global variables in the shading contexts.

See how to use materials and shaders for information on how to set up shading.

PBR shading

  • PBR (physically based rendering) is a shading process that supports accurate physical simulations of lighting, shadows, and shading.

    • PBR automatically enables: raytraced reflections and refractions, glossy reflections and refractions, and reflected light sources.

    • PBR is useful for rendering many types of scenes, for example: scenes that requires soft shadows from an environment map or area lights, lots of indirect lighting (such as closed rooms with reflective walls), glossy reflections and refractions, and caustics.

    • For information on how to set up the mantra node to use PBR, see setting up PBR.

  • PBR is a shading process. It can work with either of Houdini’s sampling methods (micropolygon sampling or raytracing). That’s why the mantra node lets you choose Micropolygon PBR (“Micropolygon Physically Based Rendering”) or Raytracing PBR (“Physically Based Rendering”) using the Rendering engine parameter on the Sampling sub-tab of the Properties tab.

  • Whereas in traditional shading the shader programs control the number and distribution of secondary rays, in PBR secondary rays are controlled by the surface’s BSDF (Bidirectional Scattering Distribution Function).

    • In PBR, the surface shader is responsible for computing the BSDF of the surface. Whereas traditional surface shaders set Cf (surface color) and Of (surface opacity) color values, PBR shaders set F to a bsdf value (bsdf is a new VEX datatype introduced in Houdini 9).

      • A BSDF is a function that takes the incoming light angle and a reflected light angle, and returns the magnitude of the reflected light given that “bounce”.

      • Houdini provides several VOPs/functions for creating bsdf values, including ashikhmin, blinn, cone, isotropic, phonglobe, diffuse, specular, and phong (use vcc -X surface | grep bsdf for a full list).

      • You can add bsdf values together and scale them by vectors or floats. Multiplying a BSDF by a color vector makes the surface reflect that color.

        // Multiply the diffuse (hemispherical) BSDF by the texture color,
        // multiply a phong BSDF by 0.5, and then add the two BSDFs together
        F = texture(map) * diffuse() + 0.5 * phong(20);
        
        // Red diffuse surface
        F = diffuse() * {1.0, 0, 0}
        
    • Mantra currently does not support measured BSDFs.

  • For each shading sample...

    • Mantra computes the direct lighting contribution for all lights in the scene. This involves sending up to one shadow ray toward every light in the scene, as well as one additional ray to account for reflections of light sources.

    • Mantra takes the eye angle, and a random direction based on the shape of the BSDF (for example, in diffuse BSDF, every secondary ray direction is equally likely, while in a reflective BSDF, the reflection direction is much more likely) and sends an indirect lighting ray. If the indirect lighting ray hits a surface, mantra runs its shader and samples its contribution to the current surface’s color.

      Running the indirect lighting surface’s shader will send out secondary rays for that shader, and those rays may hit surfaces, causing their shaders to run, sending out more rays. To prevent this process from continuing forever, mantra uses the reflect limit settings (on the PBR subtab of the Properties tab) to limit the number of bounces mantra will follow.

    • So, for each shading sample in BSDF, mantra will only generate a few additional rays: direct lighting rays in the direction of light sources, a direct lighting ray for reflections of light sources, and an indirect lighting ray in a direction chosen via the BSDF.

      • The output of the BSDF for the incoming eye direction and the direct and indirect lighting rays is the final surface color.

      • Because each sample only traces a few rays, different samples might compute completely different colors. For this reason, in PBR you will typically use 16×16, 32×32, or more samples per-pixel (pixel samples setting). Accumulating that many samples will average out the differences from sampling only a few rays.

      • NOTE: mantra version 10 uses a special fog shader (pathtracer.vfl) to implement the preceding in VEX. This replaceable shader uses the rendering settings on the PBR tab of the mantra output node and the surface shader’s BSDF to compute final lighting values. The pathtracer.vfl shader supports standard features in the Fog context, such as deep raster exports.

  • You can create a single surface shader that works both in the traditional shading pipeline and in PBR. All you have to do is set/connect Cf/Of as well as F. For example, in VEX

    001 surface simple()
    002 {
    003    vector nn = nomalize(frontface(N, I));
    004    Cf = diffuse(nn);
    005    F = diffuse();
    006 }
    

    The rendering engine will only execute the code needed to compute the appropriate variable(s), so in the traditional shading pipeline, the renderer will run lines 3 and 4 of the above code, while in the PBR pipeline it will only run line 5. This also means that if your shaders aren’t set up to compute F and you switch to PBR rendering, the shaders will output black, because the traditional shading code isn’t run.

See the how to use physically based rendering for information on how to set up a PBR render node.

Sampling and shading volumes

  • Mantra samples and shades volumes very similarly to micropolygons. Whereas micropolygon rendering refines and dices surfaces into micropolygons (each with a size measured by dPds and dPdt), micro-volume rendering dices primitive volumes into micro-volumes.

    • Mantra dices Microvolumes symmetrically in all dimensions, with the aim of microvolumes with a volume proportional to micropolygon area x depth.

    • The height and width of the micro-volumes are controlled by the shading quality, just like micropolygons. The Z depth is controlled by the volume step size setting.

    • The dPdz global contains the Z-depth delta for the current microvolume.

  • Shading micro-volumes uses the same shading pipeline as shading surfaces. All the standard types of shaders you use on surfaces are used on volumes. Displacement shaders can move the micro-volumes, which can add detail to the volume cheaply.

    • The way you write a surface shader or displacement shader to work with volumes instead of surfaces is different, however.

    • For surface shaders, you’ll generally want to scale the Of output to take into account the volume step being shaded (dPdz).

    • For displacement shaders, displacing along the normal displaces along the volume gradient, but what you’ll usually want is to perform a 3D displacement that is independent of the normal.

  • Geometry attributes are available to the shaders just like with standard surface shaders. In addition, the density variable represents the volume density (passed to shaders as a float), and the global variable N represents the gradient vector (the direction of greatest change in the density) at the micro-volume being rendered.

    If you use the Name surface node to name volume primitives, which stores the name of the volume (e.g. density, or vel.x, or temperature, etc.) in the special name attribute. You can then create parameters on your shader called density, vel, temperature, etc., and they will automatically bind to the value of the named volume. You can use this, for example, to base the color of a surface shader on the volume’s temperature.

  • Because volumes are rendered using the same pipeline as surfaces, they get the same features as surfaces. For example, opacity culling, instancing, shadow maps, depth of field, and motion blur. The most important benefit is that semi-transparent micro-volumes are composited just like surfaces, allowing accurate compositing of volumes and surfaces. This is opposed to fog shaders which run as a post-process to surface shading and composites over the surfaces, and so semi-transparent surfaces don’t composite the fog properly.

  • Volumes can be generated in various ways

    • Houdini’s volume primitives, using dynamics or the Isooffset surface node.

      You can map multiple Houdini primitives to a single rendered volume primitive. Each Houdini primitive is represented by a separate VEX variable, which allows different resolutions for different “channels” of the volume. For example, you could have a volume with a high-resolution “density” channel and a low-resolution “color” channel.

    • An i3d 3D texture file. Using an i3d file gives you paged memory usage. Each channel in the i3d file is represented by a separate VEX variable.

    • You can choose to render metaballs as volumes (since metaballs define density fields, they map naturally to volumes). Point attributes on the metaball primitive map to VEX variables in the shader.

See how to use volumetric rendering for information on how to set up volumes.

Filtering

  • After mantra runs the shaders and gets values for each sample, it sums the contributions of each pixel’s samples to get the pixel color. The pixel filter controls how much each sample contributes to the final value. The default gives more weight to samples near the center of the micropolygon. The filter width setting controls the radius within which pixel samples are considered. Increasing the filter width will increase anti-aliasing/blurring. This is controlled by the controlled by the Pixel filter parameter on the Output sub-tab of the Properties tab. The default is a 2×2 Gaussian filter.

  • Once the renderer has the summed pixel color, it quantizes the floating point color vector into the specified color format (e.g. 8-bit or 16-bit integer) to get the final value of the pixel in the C (color) plane of the final image.

  • The renderer sends the pixel values to the output device driver, for example a file format driver like TIFF or JPEG, or the ip device, which displays the image in Mplay.