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

 

  • No labels