Arbitrary output variables (AOVs) make it possible to output image besides the beauty render, for using in compositing software.

Light Path Expression AOVs

For Arnold light path expressions are used to output light into specific AOVs, with the beauty RGBA AOV being the most commonly used one. Rather than writing to specific AOVs from shaders, LPEs can be used to extract specific light paths using regular expressions. Builtin LPEs are available for the common cases.

See Light Path Expression AOVs for more details.

Builtin AOVs

In addition to builtin LPEs, these builtin AOVs are available.

NameType 
A
FLOAT
Alpha channel
Z
FLOAT
Z depth
opacity
RGB
Per RGB channel alpha
volume_opacity
RGB
Per RGB channel alpha, using volumes only
ID
UINT
id parameter from object
P
VECTOR
World space position
PrefVECTORReference space position, from Pref user data
NVECTORNormal
motionvectorVECTOR2Screen space motion vector
shadow_matteRGBAShadow
cputimeFLOATTime taken to render pixel
raycountFLOATNumber of rays traced to render pixel
shader
NODE
Pointer to shader node, for filters and drivers
object
NODE
Pointer to object node, for filters and drivers

Custom AOVs

Shaders can output custom AOVs for cases that are not covered by LPEs and builtin AOVs. For example to output mattes or masks, ambient occlusion, textures, etc. AOVs are only written for camera rays.

Here's an example shader, writing a noise texture to a float AOV:

#include <ai.h>

AI_SHADER_NODE_EXPORT_METHODS(WriteAovMtd);

struct WriteAovData
{
   AtString aov_name;
};

enum WriteAovParams
{
   p_aov_name,
};

node_parameters
{
   AiParameterStr("aov_name", "");
   AiMetaDataSetBool(nentry, "aov_name", "linkable", false);
}

node_initialize
{
   AiNodeSetLocalData(node, new WriteAovData());
}

node_update
{
   // register AOV
   WriteAovData *data = (WriteAovData*)AiNodeGetLocalData(node);
   data->aov_name = AiNodeGetStr(node, "aov_name");
   AiAOVRegister(data->aov_name, AI_TYPE_FLOAT, AI_AOV_BLEND_OPACITY);
}

node_finish
{
   WriteAovData *data = (WriteAovData*)AiNodeGetLocalData(node);
   delete data;
}

shader_evaluate
{
   const WriteAovData *data = (WriteAovData*)AiNodeGetLocalData(node);
   const float noise = AiPerlin3(sg->P);

   // write AOV only if in use
   if ((sg->Rt & AI_RAY_CAMERA) && AiAOVEnabled(data->aov_name, AI_TYPE_FLOAT))
      AiAOVSetFlt(sg, data->aov_name, noise);

   sg->out.FLT() = noise;
}

node_loader
{
   if (i != 0)
      return false;
   node->methods     = WriteAovMtd;
   node->output_type = AI_TYPE_FLOAT;
   node->name        = "write_aov";
   node->node_type   = AI_NODE_SHADER;
   strcpy(node->version, AI_VERSION);
   return true;
}