Here is the source code for a simple perspective camera node with vignetting and lens distortion:
#include <ai.h> #include <string.h> AI_CAMERA_NODE_EXPORT_METHODS(MyCameraMethods) #define _fov (params[0].FLT) node_parameters { AiParameterFlt("fov", 60.f); } node_initialize { AiCameraInitialize(node, NULL); } node_update { AiCameraUpdate(node, false); } node_finish { AiCameraDestroy(node); } camera_create_ray { const AtParamValue* params = AiNodeGetParams(node); float tan_fov = tanf(_fov * AI_DTOR / 2); AtPoint p = { input->sx * tan_fov, input->sy * 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 float dist2 = input->sx * input->sx + input->sy * input->sy; output->weight = 1 - dist2; // now looking down -Z output->dir.z *= -1; } 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 also need to compute the direction and position derivatives. This is important for correct filtering. For a perspective camera the position 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 = AiArrayInterpolateFlt(_fov, input->relative_time, 0); 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