Read Time:4 Minute, 27 Second

Hello you all :), so this weekend I was in a cabin with some friends, and one talked to me about Blink using Nuke. It was like 7 am in the morning, after having somewhat a party night… Anyways, I googled and saw that it was a very powerfull tool, so I made my hands dirty and wrote a classic ray-marcher using Nuke.

Note: Before having this discussion with my friends, I never used Nuke, and this is not a Nuke Tutorial, but rather some words on using Blink from a graphics programmer mindset.

What we will do:

To be honest, I suck at compositing and VFX… But I love to explore those tools! Nuke is a powerfull tool and I am far from understanding how it actually works… I am not into VFX. That being said, I was curious about the Blink Node, in short: It is very powerfull. In this article, we will write a volumetric ray-marcher node that will output this kind of image.

Nuke is not meant to be used that way once would ask, but not really giving a shit I would answer! It’s all about exploring Nuke’s very own Blink rather than making a specific usecase.

### Let’s dive into Blink

To create a new Blink node in wich we can write code, can be apply like this:

I just send a checkerboard image to the Blink node…

The Kernel Source is where we will write our code for this node! So let’s write a basic source code to output Red Image. As you can see, it is quite simple to make the dst() to a float4.

kernel Custom_Color : ImageComputationKernel<ePixelWise>
{
Image<eWrite> dst;                                                      //output image

param:
float4 color;                                                         //parameter

void define() {

}

void process() {
dst() = float4(1.0f, 0.0f, 0.0f, 1.0f);                                                        //output
}
};

If you are new to code, it’s ok to not really care about all the code-poutine! 🙂 The only line you want to get is this one:

dst() = float4(1.0f, 0.0f, 0.0f, 1.0f);

As you can see, the Blink node outputs a texture, in our case we simply made this to red.

You can changer the RGBA value of this BlinkNode such as…

For dst() -> you pass the color of the pixel. In our case it is red! But the power of Blink comes with per-pixel operations: More on that later!

So, in shorts, this node runs for every pixel… You can feed this node image or simply starts from scratch as I did in this tutorial. A bit like a fragment shader, there are very clever techniques and algorithms to generate nice stuff.

So let’s assume that we want to build some 3D images on a basic Blink node… We do not have some sexy vertices to raster, but simply a programs that will run for every pixels using the Blink SIMD(Single instruction, multiple data) architecture-ish.

So yeah, let’s write 3D using ray-marching! This way, we can have sphere and shit to shade onto a single texture, from a single node!

Note: Normally you would have all of these from your footages, but I am not a footage guy… So I will simply use Blink to write a ray-marcher in Nuke, because: Why the fuck not?

### Ray marching in Blink!

I know it is not the proper way to use this node… But let’s use this node to generate a 3D scene, create normals, make a basic sun lighting and repeating objects.

kernel UVmap : ImageComputationKernel<ePixelWise>
{
Image<eRead,eAccessRandom, eEdgeClamped> src;    //I
Image<eWrite> dst;                               //O
local:
float width;
float height;

void init() {
width = src.bounds.x2;                         //function to find right edge
height = src.bounds.y2;                        //function to find top edge
}
float sphere(float3 pos, float r)
{
return length(pos) - r;
}
float map(float3 pos)
{

float3 q = pos;
q.x = fmod(q.x + 4.0f, 8.0f) - 4.0f;
q.z = fmod(q.z + 4.0f, 8.0f) - 4.0f;
q.y = fmod(q.y + 4.0f, 8.0f) - 4.0f;

float d = sphere(q, 1.5f);

return d;
}

float3 computeNormal(float3 pos)
{
return normalize(float3(
map(pos + float3(0.1f, 0.0f, 0.0f)) - map(pos - float3(0.1f, 0.0f, 0.0f)),
map(pos + float3(0.0f, 0.1f, 0.0f)) - map(pos - float3(0.0f, 0.1f, 0.0f)),
map(pos + float3(0.0f, 0.0f, 0.1f)) - map(pos - float3(0.0f, 0.0f, 0.1f))
));
}
void process(int2 pos) {

float2 fg = float2(pos.x, pos.y);
float2 ires = float2(width, height);
// (fragCoord -.5 * iResolution.xy) / iResolution.y;
float2 uv = (fg - .5f * ires)/ires.y;

float3 rd = normalize(float3(uv,1.0));
float3 pos2 = float3(25.0f, 5.0f, -15.0f);
float3 color = float3(1.0f,0.0f, 0.0f);

color = float3(uv.x, uv.y, uv.x * uv.y);

for(int i=0; i < 512; i++)
{
float d = map(pos2);
if(d < 0.01)
{

color = float3(1.0f);
float3 nws = computeNormal(pos2);
float3 sunPos = float3(-2.f, 2.f, -4.f);
float sunFactor = dot(normalize(nws), normalize(sunPos));

float3 basicColor = float3(.2f, .3f, .88f);
color = basicColor * sunFactor * normalize(pos2);

}
pos2 += d * rd;
}
//rendering with a fog calculation (further is darker)

dst(0) = color.x;
dst(1) = color.y;
dst(2) = color.z;
dst(3) = 1.0f;
}
};

Happy
0 %
0 %
Excited
0 %
Sleepy
0 %
Angry
0 %
Surprise
0 %

5 Star
0%
4 Star
0%
3 Star
0%
2 Star
0%
1 Star
0%