I’ve uploaded a clip of the polarPixellate effect in action here.

## Archive for December, 2007

### polarPixellate Clip

I’ve just remembered- it’s possible to use curly-brace ( { } ) conditionals in GLSL, rather than the annoying ternary-style conditionals you have to use in CI Kernel Slang.

Having come to this exciting realisation this morning, I immediately set about modifying the polarPixellate code, taking the opportunity to add an extra function that would have been tricky to implement in a CIFilter.

This is the modified Fragment Shader I came up with:

// Control inputs

uniform float Angle;

uniform float AngleMin;

uniform float AngleWidth;

uniform float Radius;

uniform float RadiusMin;

uniform float RadiusWidth;

uniform vec2 Center;

uniform bool HideBg;

// Texture input

uniform sampler2D Texture;

void main()

{

bool bg = false;

// Normalised texture coords

vec2 texCoord = gl_TexCoord[0].xy;

// Shift origin to texture centre

vec2 normCoord;

normCoord.x = 2.0 * texCoord.x – Center.x;normCoord.y = 2.0 * texCoord.y – Center.y;

// Convert Cartesian to Polar coords

float r = length(normCoord);

float theta = atan(normCoord.y, normCoord.x);

// THE ACTUAL EFFECT

if (r > RadiusMin && r < (RadiusMin + RadiusWidth)) {

r = ceil(r / Radius) * Radius;

} else {

r = r;bg = true;

}

if (theta > AngleMin && theta < (AngleMin + AngleWidth)) {

theta = floor(theta / Angle) * Angle;

} else {

theta = theta;bg = true;

}

// Convert Polar back to Cartesian coords

normCoord.x = r * cos(theta);

normCoord.y = r * sin(theta);

// Shift origin back to bottom-left

texCoord.x = normCoord.x / 2.0 + (Center.x / 2.0);

texCoord.y = normCoord.y / 2.0 + (Center.y / 2.0);

// Output

if (bg == true && HideBg == true) {

gl_FragColor = vec4(0.0,0.0,0.0,0.0);

} else {

gl_FragColor = texture2D(Texture, texCoord);

}

}

The extra boolean variables ‘HideBg’ and ‘bg’ give the option of rendering transparency instead of the uneffected part of the image.

Having said how annoying I find using the ternary operator to do conditionals, it does make for neater-looking code. The traditional style of conditionals I’ve used above are so much easier to read, though, and have the big advantage that you can carry out more than one operation at a time (in this case, setting the values of r and bg and theta and bg within the conditionals).

### polarPixellate GLSL

I’ve converted the Core Image Filter code from polarPixellate 0.2 into a GLSL Fragment shader.

I’ve noticed you don’t seem to be able to change the order that input ports appear at the left side of a GLSL module, as you now can with macros that have custom input and output ports, which is a bit annoying.

If anyone’s interested, I will post the QTZ file for you to pick apart. Haven’t tested this in VDMX yet, but for the moment, it’s just a test really, and the CIFilter version works just as well.

GLSL code:

Vertex Shader

void main()

{

//Transform vertex by modelview and projection matrices

gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;

//Forward current color and texture coordinates after applying texture matrix

gl_FrontColor = gl_Color;

gl_TexCoord[0] = gl_TextureMatrix[0] * gl_MultiTexCoord0;

}

Fragment Shader:

// Control inputs

uniform float Angle; // range 2pi / 100000.0 to 1.0 (rounded down), exponential

uniform float AngleMin; // range -3.2 to 3.2

uniform float AngleWidth; // range 0.0 to 6.4

uniform float Radius; // range -10000.0 to 1.0

uniform float RadiusMin; // range 0.0 to 2.0

uniform float RadiusWidth; // range 0.0 to 2.0

uniform vec2 Center; // range: -1.0 to 3.0

// Texture input

uniform sampler2D Texture;

void main()

{

// Normalised texture coords

vec2 texCoord = gl_TexCoord[0].xy;

// Shift origin to texture centre (with offset)

vec2 normCoord;

normCoord.x = 2.0 * texCoord.x – Center.x;

normCoord.y = 2.0 * texCoord.y – Center.y;

// Convert Cartesian to Polar coords

float r = length(normCoord);

float theta = atan(normCoord.y, normCoord.x);

// The actual effect

r = (r < RadiusMin) ? r : (r > RadiusMin + RadiusWidth) ? r : ceil(r / Radius) * Radius;

theta = (theta < AngleMin) ? theta : (theta > AngleMin + AngleWidth) ? theta : floor(theta / Angle) * Angle;

// Convert Polar back to Cartesian coords

normCoord.x = r * cos(theta);

normCoord.y = r * sin(theta);

// Shift origin back to bottom-left (taking offset into account)

texCoord.x = normCoord.x / 2.0 + (Center.x / 2.0);

texCoord.y = normCoord.y / 2.0 + (Center.y / 2.0);

// Output

gl_FragColor = texture2D(Texture, texCoord);

}

A little festive-season project.

It’s basically a pixellation effect that works on polar, rather than cartesian coordinates.

In the 2D Cartesian coordinate system, X and Y coordinates define the left/right and up/down position of a given point. In a Polar coordinate space, a point is defined by its Angle (represented by the Greek letter Phi in my code, but more often Theta is used, apparently) and distance from a centre point (r). The effect converts Cartesian to Polar coordinates, then ‘quantises’ the values of Phi and r to tweakable step-sizes. The effect is to divide the image up into ‘slices’ and rings. Easier to demonstrate with a few screenshots than to explain in words, as is usually the case.

Still needs some work, and I’m not quite sure how to deal with the ugly round hole in the centre, but I’m pretty pleased with it so far.

‘Stepping’ the Angle (Phi) parameter

…and finally, applying quantisation to both dimensions.

The Cartesian-Polar coordinate code I stole shamelessly from Libero Spagnolini’s Photobooth Demystified page, and the polarPixellate effect is a development of my earlier circle-wipe effect.

I think I’ll do a few more experiments in the future, to see what I can do with rectangular-to-polar coordinates and various distortions.

With thanks to Ed. for code advice and general encouragement and enthusiasm.

I spent some time struggling with the problem of how to consistantly translate and rotate a set of objects created by an Iterator Patch.

The problem, it turned out, was the result of my assumption (incorrect, as I eventually discovered), that if an object, or group of objects, is translated using a 3D Transform patch, the Rotation Origin on the Z, Y and Z axes will remain in the same place, and the objects will be moved in relation to this fixed point.

This misapprehension caused me all kinds of confusion, as I tried to create logic to compensate for the translation, and the Z-axis depth of the iterated objects, by moving the Origin. The maths should of course have been very simple, but I wasn’t getting anything like the result I expected.

Eventually, through trial, error, hair-tearing and gnashing of teeth, it dawned on me that if I translate the objects on the Z-Axis (for a ‘zoom’ effect), the Rotation Origin moves with them! So, to have the objects rotate around their centre on the Z-plane, all I needed to do is move the Rotation Origin on the Z-axis forward by half the combined depth of the array of iterated objects.

I love simple solutions!

### 3DZoomBlur 1.5 (Tiger)

Updated Tiger version of the effect. Should also works on Leopard systems (unlike the original).

There is no Queue patch in Tiger, so this version of the effect just makes static copies of the input image. It should therefore be a bit less processor-hungry than the Leopard version.

I’ve also implemented inertia on the controls.

Thanks to Karan Lyons from the Apple Quartzcomposer-dev list for the Prototype Smooth macro, which does the trick beautifully!

### 3DZoomBlur 2.0 (Leopard)

A couple of posts back, I enthused about the new features of Quartz Composer 3.0s Core Image Filter patch.

It turns out they’re a little more tricky to use than I’d realised, so the wonderful space-saving composition I posted a screenshot of didn’t actually work as I’d intended, sadly.

As Kevin Quennesson from the Apple Quartzcomposer-dev list very patiently explained to me, the reason it didn’t work is to do with ROIs and DODs.

The ROI, (Region Of Interest) defines the region of the input image that a filter will act upon, when it’s Kernel code is called from the JavaScript in the bottom pane of the patch’s editing window.

The DOD (Domain Of Definition) defines the dimensions of the image returned by a given Kernel function.

All this isn’t an issue, if you’re creating a simple, single-pass filter, or if the dimensions of the output of the filter are the same as those of the input image. However, I wanted to combine 2 seperate filter functions, and a crop (which resulted in the dimensions of the output image changing, obviously). These are the stages of the multi-pass filter I created:

Filter 2: Pixel-Strip

Creates strip at bottom-left, with each vertical stripe representing one ’tile’ of the input image.

Pixels outside this ‘active area’ receive the colour values of the pixel at 0.0, 0.0, but are cropped-out at the next stage, anyway.

Final Stage: Crop

(Enlarged for clarity)

The JavaScript portion of the Core Image Filter code is as follows:

I’ve highlighted the ROI/DOD-related code in purple.

function __image main(__image Image, __number Rows, __number Cols)

{

// CREATE CONTROL VALUES FROM INPUT SPLITTERS

// Input image dimensions

var Width = Image.extent.width;

var Height = Image.extent.height;

// Dimensions of Pixellation cells

var CellWidth = Width / (Math.ceil(Rows));

var CellHeight = Height / (Math.ceil(Cols));

// Total number of cells

var CellNum = Rows * Cols;

// ROI + DOD FUNCTIONS

// Thanks to Kevin Quennesson for his explanation and sample code

// Region Of Interest (ROI) of input image to be processed

function myROIFunction(samplerIndex, dstRect, info)

{

return info;

}

// Output image dimensions (DOD rect)

var dodRect = new Vec(0.0, 0.0, Width, Height);

// IMAGE OPERATIONS

// Pixellate image

xy_pixellate.ROIHandler = myROIFunction;

imagePixellated = xy_pixellate.apply(dodRect, dodRect, Image, CellWidth, CellHeight);

// Create strip

makeStrip.ROIHandler = myROIFunction;

imageStrip = makeStrip.apply(dodRect, dodRect, imagePixellated, CellWidth, CellHeight, CellNum, Cols);

// Crop

imageCropped = Filter.Crop(imageStrip, new Vec(0.0, 0.0, CellNum, 1.0));

// Output

return imageStrip;

}