Camera Nodes - Arnold Developer Guide
![](https://help.autodesk.com/cloudhelp/ENU/AR-DevGuide/images/av-camera_node.jpg)
Here is the source code for a simple perspective camera node with custom distortion and vignetting :
mycamera.cpp
#include <ai.h>
#include <string.h>
AI_CAMERA_NODE_EXPORT_METHODS(MyCameraMethods)
enum
{
p_fov
};
struct MyCameraData
{
float tan_fov;
};
node_parameters
{
AiParameterFlt("fov", 60.0f);
}
node_initialize
{
AiCameraInitialize(node);
AiNodeSetLocalData(node, new MyCameraData());
}
node_update
{
MyCameraData* data = (MyCameraData*)AiNodeGetLocalData(node);
data->tan_fov = tanf(AiNodeGetFlt(node, AtString("fov")) * AI_DTOR / 2);
AiCameraUpdate(node, false);
}
node_finish
{
MyCameraData* data = (MyCameraData*)AiNodeGetLocalData(node);
delete data;
}
camera_create_ray
{
const MyCameraData* data = (MyCameraData*)AiNodeGetLocalData(node);
const AtVector p(input.sx * data->tan_fov, input.sy * data->tan_fov, 1);
// warp ray origin with a noise vector
AtVector noise_point(input.sx, input.sy, 0.5f);
noise_point *= 5;
AtVector noise_vector = AiVNoise3(noise_point, 1, 0.f, 1.92f);
output.origin = noise_vector * 0.04f;
output.dir = AiV3Normalize(p - output.origin);
// vignetting
const float dist2 = input.sx * input.sx + input.sy * input.sy;
output.weight = 1 - dist2;
// now looking down -Z
output.dir.z *= -1;
}
camera_reverse_ray
{
const MyCameraData* data = (MyCameraData*)AiNodeGetLocalData(node);
// Note: we ignore distortion to compute the screen projection
// compute projection factor: avoid divide by zero and flips when crossing the camera plane
float coeff = 1 / AiMax(fabsf(Po.z * data->tan_fov), 1e-3f);
Ps.x = Po.x * coeff;
Ps.y = Po.y * coeff;
return true;
}
node_loader
{
if (i != 0) return false;
node->methods = MyCameraMethods;
node->output_type = AI_TYPE_UNDEFINED;
node->name = "mycamera";
node->node_type = AI_NODE_CAMERA;
strcpy(node->version, AI_VERSION);
return true;
}
In camera_create_ray
you can also compute the direction and position derivatives. This is important for correct filtering. However, you can let Arnold compute the differentials automatically if you leave these fields set to the default 0.0 value.
The following example shows how to compute them in the camera node for a perspective camera. In this case the ray's origin is always the same, but the direction changes from pixel to pixel. Follows some sample code to compute the derivatives for a perspective camera (we do not take into account uv noise):
float fov = data->fov;
fov *= (float) (AI_DTOR * 0.5);
float tan_fov = tanf(fov);
...
// scale derivatives
float dsx = input.dsx * tan_fov;
float dsy = input.dsy * tan_fov;
...
AtVector d = p; // direction vector == point on the image plane
double d_dot_d = AiV3Dot(d, d);
double temp = 1.0 / sqrt(d_dot_d * d_dot_d * d_dot_d);
// already initialized to 0's, only compute the non zero coordinates
output.dDdx.x = (d_dot_d * dsx - (d.x * dsx) * d.x) * temp;
output.dDdx.y = ( - (d.x * dsx) * d.y) * temp;
output.dDdx.z = ( - (d.x * dsx) * d.z) * temp;
output.dDdy.x = ( - (d.y * dsy) * d.x) * temp;
output.dDdy.y = (d_dot_d * dsy - (d.y * dsy) * d.y) * temp;
output.dDdy.z = ( - (d.y * dsy) * d.z) * temp;
// output.dOd* is also initialized to 0s, the correct value
The following .ass file generates an image similar to the one at the top of this page:
mycamera.ass
options
{
xres 1024
yres 1024
AA_samples 6
camera "mycamera"
GI_diffuse_depth 4
GI_specular_depth 4
}
mycamera
{
name mycamera
position 0 1 4
look_at 0 -0.2 0
up 0 1 0
}
skydome_light
{
name myskydome
intensity 1
color 1 1 1
camera 0.0
}
standard_surface
{
name mystd
base_color 0.4 0.8 0.4
}
sphere
{
shader mystd
matrix
1 0 0 0
0 1 0 0
0 0 1 0
-1.5 0 0 0
}
sphere
{
shader mystd
matrix
1 0 0 0
0 1 0 0
0 0 1 0
0 0 0 0
}
sphere
{
shader mystd
matrix
1 0 0 0
0 1 0 0
0 0 1 0
1.5 0 0 0
}
standard_surface
{
name myfloor
base_color 0.2 0.2 0.2
specular 0
}
plane
{
name myplane
normal 0 1 0
point 0 -0.5 0
shader myfloor
}