The mighty Pixel Spread

First of all, let me say how much we admire the mighty Pixel Spread — it’s been our faithful companion in many challenging shots, smoothing out edges and solving all kinds of tricky situations like a true hero.

We were wondering: is there any way to access the logic or source code behind the Pixel Spread node? We’re exploring ways to implement a similar solution in another software environment, and having a deeper understanding of how it works would be incredibly helpful.

Of course, we completely understand if this isn’t publicly available, but thought it was worth asking the wise minds of the Logik

Hi Mert. There is a Matchbox Pixel Spread (user MX, not ADSK), but it doesn’t use the exact same tech as the ADSK Batch node. Maybe the MX code could shed some light.

As far as I know, machboxes use compiled code, so there is no code to look in.

There are several tutorials with a deep look inside this kind of effect for nuke and the maths inside. The basic approach is a blur+divide op. There are more techniques. In nuke pixel spread is “edge extend”, “edge push”, or things like that.

1 Like

I do love the pixel spread so obviously I wanted to have it in Nuke as well.

I found a few gizmos (I have forgotten their names atm) but they were all pretty much leveraging the same tricks.
A combination of blur, divide and dilate/erode operations.

Similar to how the Renee Tymn’s “Object Obliterator works.

1 Like

The default pixel spread is similar to Sapphire distort set to “fine” mode, where it just warps edges of a matte into the black.

Interpolate is basically the object obliterator as far as I can tell.

Stretch is the only one I haven’t seen mimicked in other areas, but it calculates an edge normal and then stretches along it.

4 Likes

I still remember the moment a senior flame op showed me the sapphire distort method and it blew my mind. Then when they made it a standard tool in Flame, I felt a little cheated of that hard won knowledge that had been handed down to me.

“Pixel Spread and forget”
It’s the best thing since deodorant.

1 Like

I’m interested in how other people use Pixel Spread in conjunction with an additive key when you get edges that aren’t quite working with the additive.

I’ll usually do a separate pixel spread & matte edge combo garbage masked to the area I want to limit it to but have also played with it prior to the additive key itself with some interesting results. You need to create some kind of holdout matte for the additive key regardless so you can get some interesting results then applying that to the main and despill sources with a pixel spread prior to the additive keyer. Not sure if that makes sense or not.

2 Likes

@AdamArcher For keying (say off green screen) you can pull a semi-good key to get yourself a basic matte, then use that matte in pixel spread to contract the subject in (pulling the background inwards - and essentially creating a clean plate around the edges). Then you can use an IBK, or dif matte to get a great edge key, or divide the result with the original to get just the edges on white and then use that to either multiply onto the BG or Luma key it.

2 Likes

It’s a mix.

Under the hood a matchbox shader is glsl code (to be executed directly on the GPU). Most community matchboxes have been left in readable/open source form and you can see what they do and modify them.

There is a choice to ‘compile’ them into a single .mx file, which is more like a .zip file. It can optionally be encrypted which would obscure the contents.

Lastly there is the special case of nodes that are referred to as Matchbox, but that are in fact custom code. All those are ADSK made, and an example is Image.

Regarding crok_pixelspread, that is in fact a readable shader. If you install the logik matchbox shaders via the portal, you should find the file in /opt/Autodesk/presets/“flame version”/shaders/matchbox/LOGIK/…

Here’s the main code file:

vec3 adsk_getDiffuseMapCoord();
vec4 adsk_getDiffuseMapValue(in vec2 texCoord);
uniform float adskUID_amount;

float adskUID_alpha(vec2 uv)
{
   return adsk_getDiffuseMapValue(uv.xy).a;
}


vec4 adskUID_lightbox(vec4 source)
{
	vec3 uv = adsk_getDiffuseMapCoord();
	vec2 size_inv=vec2(1.0 / 2000.0, 1.0 / 2000.0);
	
	float p_amount = -1.0 * adskUID_amount;
    float cr=cos(0.0);
    float sr=sin(0.0);
	
	mat3 g_trans = mat3( cr*p_amount*size_inv.x, sr*p_amount*size_inv.y, 0.0, -sr*p_amount*size_inv.x, cr*p_amount*size_inv.y, 0.0, 0.0, 0.0, 1.0 );

	vec4 disp_h = vec4(adskUID_alpha(uv.xy+vec2(0.0,size_inv.y)),adskUID_alpha(uv.xy+vec2(0.0,-size_inv.y)),adskUID_alpha(uv.xy+vec2(-size_inv.x,0.0)),adskUID_alpha(uv.xy+vec2(size_inv.x,0.0)));
	vec4 disp_d = vec4(adskUID_alpha(uv.xy+size_inv),adskUID_alpha(uv.xy-size_inv),adskUID_alpha(uv.xy+vec2(-size_inv.x,size_inv.y)),adskUID_alpha(uv.xy+vec2(size_inv.x,-size_inv.y)));
		
	disp_h = max( disp_h, 0.0);
	disp_d = max( disp_d, 0.0);
		
	vec4 levels = vec4(1.0);
	disp_h = pow( disp_h, levels );
	disp_d = pow(disp_d, levels );
	
	vec2 gradient = vec2(0.0);
	gradient.x = dot( disp_h, vec4(0.0, 0.0, 2.0, -2.0)) + dot( disp_d, vec4(-1.0, 1.0, 1.0, -1.0));
	gradient.y = dot( disp_h, vec4(-2.0, 2.0, 0.0, 0.0)) + dot( disp_d, vec4(-1.0, 1.0, -1.0, 1.0));
		
	gradient = ( g_trans * vec3( gradient, 1.0)).xy;
		
	vec4 col = vec4(adsk_getDiffuseMapValue(uv.xy+gradient ).rgb, 1.0);
	
	return vec4(col.rgb, source.a);
}

I’ve tried a bunch of things like this with varying results when using additive keys. I always use the additive key output as a fill or premultiplied, so never as the main comp itself. The real eye opener for me recently was realizing that the really important thing for edges on the additive key has nothing to with the edges or the color correct of the bg to be multiplied over the spill map, it’s the quality of the spill map itself. And something I’ve found as of late gives me much better results is fairly simple: instead of just suppressing the green screen or blue screen to grey, I use the master keyer luma and I pull it down so that everything goes black until just up to the edges of what I’m extracting. Give it a try and see if it helps!

2 Likes

Interesting- so is stretch in the Autodesk pixelspread a displacement based on the luminance of the matte input? So contract and extend are just either positive or negative matte? And things like softness are just blurring that gradient?

Have you seen this from the LogikFest?

1 Like

Funny. The math behind the gradient direction is the same you use for in Houdini for creating flow vectors and edge normals with nearpoints in vex.

Basically a sobel filter…

1 Like

I assume it was obvious, but just to be clear: the code you’ve been looking at is from crok_pixelspread. I was just illustrating @kily 's question that code isn’t compiled, and can be read/inspected. I didn’t write that code, this is Ivar’s code.

Also noteworthy about this code is that it makes use of lesser known API functions. The Flame manual has a full list of API functions available to shaders, which is quite extensive: Shade API Documentation

1 Like

my guess would be that it would be the best for you to check nuke based clones