mardi 26 janvier 2016

Relighting Tool, position passe based



Without any doubt the more complicated tool I did so far.

This is the second part of the solution for the smalls particules in the lighthouse of the movie "The End". We wanted to illuminate the particules with the wall lighting. To avoid the huge 3D render time, I find a 2D solution.

I discovered last year the blink scripts, and I have to say that it gives nuke a whole new power !

This gizmo relight an object using, instead of a point, a luminance passe and a position pass. For that, it uses two blink script, one for optimisation, one for the actual computation.

Basically, the gizmo compares each pixel of the target position passe to every pixels of the lighting position passe. If there are close enough, it relights the target object according to the distance and the amount of light in the area.

Let's get into it step by step :


The tolerance knob controls the black point of the Grade node, that allows the user to choose from which amount of light the relight is active.

Here is the first expression node. Simple expression meaning :
if alpha < 0 : alpha = 0; else : alpha = 0

The first blink script 


As I said, the gizmo compares each pixel of the target position passe to every pixels of the lighting position passe.
For an HD image as we had for the movie. It means :
1280 * 720 = 921600
921600 pixels compared each time to 921600 pixels
So.... 921600*921600 = 849346560000 comparation. This is to much.

So I had to optimized the thing. First, the reformat downscale a bit the lighting passe: That helps.

Then, this blink script check one time every pixel. It keeps and orders only the one that are not black in the alpha (which is the luminance).

Instead of having that :


This blink script gives us that :


Because the blink scripts are mutli core process, I used the alpha of the pixel (0,-1) as a mutex. The actual position in the image is saved in this pixel.

Here is the code :
kernel glow3D_Kernel: ImageComputationKernel<ePixelWise>
{
Image<eRead, eAccessPoint, eEdgeClamped> src; //target position
Image<eWrite, eAccessRandom> dst;  //the output image

  //In define(), parameters can be given labels and default values.

param:
 int width;

void define() {
 defineParam(width, "Width", 1280);
 }

void process() { // this fonction is executed for every pixel
 if (src(3) != 0){ //if the alpha is not black
  int position = dst(0,-1,3); // get the pixel index where it should write from the mutex pixel
  dst(0,-1,3) = (float)(position+1); // set the mutex pixel to the next pixel index
  int x = position % width; // get x and y from the pixel index
  int y = position / width;
  dst(x,y) = src(); // write the actual pixel value in the actual position
  }
  }
};
The color is still the position pass, and the alpha is still the luminance. But the pixels are ordered, we don't have to check the whole image for each pixel now.

The second blink script.


This is where everything happens. This blink script takes two entry the light position pass and the target object position pass.

The script runs through the target object image, for each pixel, if the alpha is not 0, it goes trough the light position passe.

For each pixel in the light position passe, it checks if the pixel is in the zone defined by the user. If it is. It weight the luminance value by the distance and the decay set by the user.

The result is added to the light amount of the target object pixel and the denominator is incremented.
The denominator is actually the number of pixel affecting the target pixel

After running through the light position passe, the luminance amount is divided by the denominator. And the value is wrote in the target object pixel.

Here is the code :
kernel glow3D_Kernel: ImageComputationKernel<ePixelWise>
{
Image<eRead, eAccessPoint, eEdgeClamped> posDst; //target position
Image<eRead, eAccessRandom, eEdgeClamped> posRef;  //reference position pass
Image<eWrite> dst;  //the output image

param:
 float size; //Size of the glow
 float decay;
 int width;


  //In define(), parameters can be given labels and default values.
void define() {
 defineParam(size, "Size", 1.0f);
 defineParam(decay, "Decay", 1.0f);
 defineParam(width, "Width", 1280);
 }

local:
 int x1;
 int x2;
 int y1;
 int y2;

  //The init() function is run before any calls to kernel().
void init() {
  }

float distance(float ref0,float targ0, float ref1, float targ1, float ref2, float targ2) {// this fonction calculate the distance, thx pythagore :)
 float dist = sqrt((targ0-ref0)*(targ0-ref0) +(targ1-ref1)*(targ1-ref1) +(targ2-ref2)*(targ2-ref2) );
 return dist;
 }

void process(int2 pos) {
 if (posDst(3) != 0) { //if alpha is not 0
  float tempDist;
  int denominator = 0;
  float lightAmount = 0;
  float weight;
  int i = 0;
  int x = i % width;
  int y = i / width;
  while (posRef(x,y,3) != 0) { //this loop run through the light passe, but stops when alpha = 0
   tempDist = distance(posRef(x,y,0), posDst(0), posRef(x,y,1), posDst(1), posRef(x,y,2), posDst(2)); // get the distqnce between the two pixel
   if (tempDist<size) { // if the pixel is in the area
    weight = 1-(tempDist/size); // calcul the weight with the distance
    weight = pow(ponderation,decay); // add the decay
    lightAmount += (posRef(x,y,3)*ponderation); // add the actual light impact to the light amount
    denominator++; // add one pixel impacting the target pixel
    }
   i += 1;
   x = i % width;
   y = i / width;
   }
  if (denominateur != 0) {
   dst() = lightAmount/denominator; // divide the light amount by the amount of pixel impacting
   }
  else {
   dst() = 0;
   }
 }
 else {dst() = 0;}
 
   }
};
This way, the light is calculated in relation with the distance, the light amount, and the decay and gives a real smooth results.

Here is a result of the particle system used with this gizmo :

Since this method doesn't compute the pixel that are outside the field of view, I overscaned the 3D renders to have a better results.

I hope you understood, if you have any question, please ask ! :)

1 commentaire:

  1. How do I change the name of the casino? - KTNV
    Casino 양산 출장마사지 Name change. Casino Name Change. 문경 출장마사지 When will the name change? The name change 춘천 출장샵 is not stated in 안산 출장안마 the name, but you can change the 영주 출장마사지

    RépondreSupprimer