picture home | art | events | music | rack extensions | downloads | pixel blog

omino pixel blog

pixels, motion, and scripting
david van brink // Thu 2008.10.16 23:07 // {pixel bender}

Pixel Bender: Fun With Cubes 1

In which I present instances of a family of closed-cell geometric objects which are computationally trivial to render.

The Concept

Imagine moving a plane through a three dimensional mesh of cubes.

You start by rotating the plane to your liking. That is, for each pixel on the plane (since that is our rendering reference point) we rotate and translate it as we like, from (x, y, 0) to (x’, y’, z’). To find out which “cell” we’re in, we just look at the integer parts of the prime-coordinates. In the animation above, the cells have been “checkered” by whether their integer coordinates’ sum is even or odd. (Actually, I just used LightWave’s 3d checker texture function. But you get the idea.)

Drawing the checkerboard is easy. Drawing some depth in the cubes is only a little bit trickier. We need a perpendicular vector from the plane, to cast a ray, so we also rotate the perpendicular vector (0,0,1) by the same amount. Then we project that ray from the fractional position within the cell until it hits one of the 6 walls of the cube. Lastly, we give a non-optical depth cue based on how far the intersected wall is.

The Implementation

Here’s the math part of the rotation. There’s probably an easier way to get the 3-axis rotation matrix… But this works.

// concatenate three simple rotations to spinM
float3x3 spinXM = float3x3(1,0,0,0,cos(spin.x),spin.x,0,-sin(spin.x),cos(spin.x));
float3x3 spinYM = float3x3(cos(spin.y),0,sin(spin.y),0,1,0,-sin(spin.y),0,cos(spin.y));
float3x3 spinZM = float3x3(cos(spin.z), sin(spin.z), 0,-sin(spin.z), cos(spin.z), 0, 0, 0, 1 );
float3x3 spinM = spinXM * spinYM * spinZM;

float2 center = float2(400,500) / 2.0; // YUCK! hardcoded for my blog entry.
float2 oc = (outCoord() - center) / zoom; // adjust "input coordinates"

// we rotate the requested coordinate by 3 axes, giving the
// R3 point on the plane of the screen (after the mesh is turnt)
float3 p = float3(oc.x,oc.y,0) * spinM; //oc.x * axis1 + oc.y * axis2;

// perp is our viewing line, straight down from the screen.
float3 perp = float3(0,0,1) * spinM;

Here is an interactive demo of the algorithm, in Flash and Pixel Bender. (I added a full screen button, whee!)

And here is the Pixel Bender kernel:

And for reference, here is the template for the Flash displayer, compilable by mxmlc.

Flex trivia: stage.addEventListener(FullScreenEvent.FULL_SCREEN,screenChange);
works from mx:Application applicationComplete because the UI and display list is all instanced, but not from mx:Application creationComplete which happens a little bit earlier.

Next up: Suppose we didn’t intersect with a simple plane… !

oh, i dont know. what do you think?


david van brink // Tue 2008.10.14 22:27 // {pixel bender}

Pixel Bender: My First Flash Program

Since I love After Effects, I need to learn Pixel Bender. To talk about Pixel Bender in this blog, it sure is handy that the new Flash Player can show it, live. But first I had to learn Flash. Darn!

So, below is a program written for Flex, compiled with Adobe’s free mxmlc command line tool. Requires Flash 10 (currently in beta). It incorporates a simple Pixel Bender kernel to draw on a rectangular area. It doesn’t use an input image.

For reference, here’s the snippet of Flex code that simply fills in an area with the Bended Pixels:

        // "draw" is the name of a UIComponent instance,
        // and "shader" is a Pixel Bender kernel instance.
        var g:Graphics = draw.graphics;
        g.clear();
        g.beginShaderFill(shader);
        var m:Number = 20;
        g.drawRect(m,m,draw.width - 2*m,draw.height - 2*m);
        g.endFill();

And, below is the swf, live, in action. Click the “animate” checkbox for most fun.

As you can see, it displays each of the kernel’s numeric parameters as a slider. Since it knows the min- and max- of each parameter, we can randomize and animate them, which is much more fun that twiddling the slider by hand, yes? It also suggests some of the possibilities when animated in After Effects CS4.

Importantly, this Flex program discerns the parameter values and ranges at run time, so you can pop in another kernel, recompile, and play some more.

Here’s the kernel code. Just a bunch of Math fun… really it was just to test out the Flash displayer, but it came out kind of psychedelic.

And here’s the Flex program. To pop in our own kernel, just alter the Embed line.

Stay tuned for some Fun With Cubes.

oh, i dont know. what do you think?


david van brink // Mon 2008.10.6 06:46 // {pixel bender}

Pixel Bender: mod() bug

The mod() Function

In GL Shading Language, and Pixel Bender, the mod function is defined like so: mod(x,y) => x - y * floor(x / y). The result is always positive. We can see quickly that y’s sign goes away. And the floor function pushes towards negative infinity so y * floor(x / y) is always “lower” (not “smaller”) than x.

The mod() Function, GPU vs CPU

Here is a short Pixel Bender kernel.

<languageVersion : 1.0;>
kernel modTest2 <namespace : "x";vendor : "omino.com";version : 1;description : "mod() bug";>
{
    // on PBT build 35, produces different display on GPU/CPU
    parameter float span;
    output pixel4 dst;

    void evaluatePixel()
    {
        float x = outCoord().x / span - 1.0;
        float y = outCoord().y / span - 1.0;
        float m = mod(x,y);
        float t = x - y * floor(x / y); // computed by definition
        dst = float4(m / y,abs(m) / y,t / y,1);
    }
    
    region generated(){return region(float4(0,0,2.*span,2.*span));}
}

On the GPU, it produces:

And on the CPU:

The blue component is correct on both CPU and GPU because it’s computed according to the formula. The other two color components reveal the nature of the bug. (Probably that x/y is being rounded towards zero.)

As an aside: Notice that some of the CPU rendering’s lines are quite a bit smoother than the GPU rendering’s. Subtly different arithmetics.

The Workaround

A workaround might not be needed, in practice. The mod() function seems to work correctly in Flash, even though it’s on the CPU. (The PBT and Flash implementations are, clearly, if surprisingly, different, since you can turn off the Flash errors & warnings in PBT. Also confirmed in this note by Adobe engineer Tinic Uro.)

On the other hand, After Effects CS4’s CPU-renderer might still have the bug. On the gripping hand, perhaps Adobe will fix it… I’ve had trouble signing up onto the JIRA system to report it, though.

But to get correct render results on the CPU within PBT build 35 on Macintosh, simply use the expanded formula.

float m = mod(x,y); // may have a bug
float m = x - y * floor(x / y); // always works.
4 comments
Kerry // Sat 2008.10.18 22:1010:10 pm

> Notice that some of the CPU rendering’s lines are quite a bit smoother than the GPU rendering’s. Subtly different arithmetics.

Yes. The GPU’s computations are using a floating-point scheme that is inferior to the CPU’s for improved performance. (It uses fewer bits and a faster but less well behaved rounding method.)

As stated by William Kahan, who architected IEEE Std 768,
“Gresham’s Law for Computing:
The Fast drives out the Slow even if the Fast is Wrong.”

david van brink // Sun 2008.10.19 13:321:32 pm

Yes!

Even more distressing is that the default arithmetic results using doubles (doubles!) under GNU-C can produce different results on Power PC and Intel. (Presumably there’s a hi-fi compiler option someplace.)

Kerry Veenstra // Sun 2008.10.19 15:563:56 pm

Hee hee! A history of floating-point computer arithmetic would fill a book. PowerPC’s floating point is pure IEEE Std 754, but none of Intel’s floating point is. (And that’s pure irony! Kahan’s work on the 8087 is what makes IEEE Std 754 so good.) The original 8087 used 10-byte numbers with 64-bit mantissas, but IEEE doubles use 8-byte numbers with 53 bit mantissas. To get results on an 8087 that are close to those of 8-byte IEEE doubles, one rounds the Intel 64-bit mantissas to 53 bits. Unfortunately, rounding first to 64 bits (as the 8087 does) and then rounding to 53 bits is not the same as rounding straight to 53 bits (as PowerPC does). So the results on PowerPC and on 8087 are slightly different. (“8087” means all x86 CPUs through the Pentium III.)

Then, with Pentium 4, Intel changed its floating-point strategy and implementation. First, Intel’s own compilers avoided the 8087-compatibility registers and instead used the processor’s SIMD media instructions for scalar computations. Since the SIMD instructions support 8-byte floating point numbers but not 10-byte 8087-like numbers, it sounds like one will get results that are the same as on the PowerPC. Unfortunately, Intel also went away from IEEE Std 754’s “gradual underflow.” Gradual underflow lets the CPU represent numbers between 2^(-1022) and 2^(-1074) without underflowing straight to zero.

As unlikely as their appearance may seem, these values will show up occasionally. And unfortunately the Pentium 4 emulates gradual underflow in software. By default such emulation is turned off–and for a good reason. Once a computation starts underflowing gradually, subsequent computations that use the gradually underflowed result also underflow. One sees a noticable hit in performance as a thread of execution drags repeatedly through the emulation library routines. In media processing, such a hit is unacceptable. So on the Pentium 4 gradual underflow emulation is disabled by default, and you get a different floating-point result than you would get on a PowerPC.

Kerry // Sun 2008.10.19 18:126:12 pm

And BTW, if the gradual-underflow issue can be dealt with, it’s likely that *floats* will give the same result on Intel and PowerPC. This is because rounding to 64 bits first and then to 23 bits (as Intel does) gives the same result as rounding to 23 bits directly (as PowerPC does). I never found the proof, but I remember that rounding to 2x (or more) bits before rounding to x bits does not change the result of the final rounding.

oh, i dont know. what do you think?


david van brink // Sun 2008.10.5 21:26 // {pixel bender}

A Simplest Pixel Bender Filter

Pixel Bender is Fun!

Ok. This post will show the source code for the tiniest, simplest Pixel Bender generator you can imagine. A generator is like a filter, with no inputs; it just creates an output. Afterwards, some thoughts about integrating into Flash.

To run it, just download the Pixel Bender Toolkit from Adobe, and paste this code into it.

<languageVersion : 1.0;>

kernel ShowAColor <namespace : "x";vendor : "x";version : 1;>
{
    parameter float3 the_color < defaultValue : float3(0.8,0.1,.5) ; >;
    output pixel3 dst;

    void evaluatePixel()
    {
        dst = the_color;
    }

    region generated() // (but not supported in Flash)
    {
        return region(float4(0,0,100,100));
    }
}

When you run it, you’ll get to input a color with sliders…

And as you choose the color, a 100×100 square of GPU-accelerated pixels will display it.

Nothing too high-concept here, but it does show the two main methods. evaluatePixel() does the actual rendering; it gets called over and over for each pixel of output. generated() just tells Pixel Bender how big an image to create.

Adding Another Parameter

Lets do one further little tweak, and then get on to a discussion about Flash and such. We’ll add a control to invert the color.

<languageVersion : 1.0;>

kernel ShowAColor <namespace : "x";vendor : "x";version : 1;>
{
    parameter float3 the_color < defaultValue : float3(0.8,0.1,.5) ; >;
    parameter int invert_color < minValue : 0 ; maxValue : 1 ; >;
    output pixel3 dst;

    void evaluatePixel()
    {
        if(invert_color > 0)
            dst = float3(1,1,1) - the_color;
        else
            dst = the_color;
    }

    region generated() // (but not supported in Flash)
    {
        return region(float4(0,0,100,100));
    }
}

Now we get one more control, which inverts the color.

My Comments, Observations, and (Constructive) Critique

I’ll assume here that the reader is using other references as well to learn about Pixel Bender… that you know about the float and float3 and float4 types, and that you can have an input image to process, and so forth.

This particular kernel won’t work in Flash 10, for two reasons:

  • The output in Flash must be of type pixel4, not pixel3. That is, it must have an alpha channel.
  • Flash doesn’t support the generated() function. In the Pixel Bender Toolkit, omission of the generated() function (for an inputless kernel) causes a blank display when you run it. In Flash, however, a kernel is alway run as a “Filter” on something, and so assumes its size.

In the second example, there are two declared parameters. the_color is first, and invert_color is second. But they’re displayed in opposite order. The Pixel Bender Toolkit always displays them alphabetically. That’s not bad, but it would be nice if you, the author, could control the display order.

Also, it would be nice if the_color was shown as a color picker instead of three sliders. For that matter, it would be nice if invert_color showed up as checkbox. I think it would be nice if you could type:

    parameter float3 the_color
        < defaultValue : float3(0.8,0.1,.5) ; type : "colorpicker" >;
    parameter int invert_color
        < minValue : 0 ; maxValue : 1 ; type : "checkbox" >;

It turns out that you could do this in a generic Flash framework. All of the metadata in the <this:that> is visible to ActionScript as named properties of kernel’s instance (an instance of class ShaderFilter). You’d have to control the ordering with another assignment, but, in principle the following code would give such a hypothetical Flash-based parameter-editor enough clues:

    parameter float3 the_color
        < defaultValue : float3(0.8,0.1,.5) ; _index : 1 ; _type : "colorpicker" >;
    parameter int invert_color
        < minValue : 0 ; maxValue : 1 ; _index : 2 ; _type : "checkbox" >;

I’m hoping that integration with After Effects CS4 uses something like that… Hi Adobe… listening? 🙂

One Last Gotcha / Complaint

Annoyingly, Pixel Bender doesn’t do type-promotion. That is, you get errors if you ever mix integers with floats. Each line of the following code produces errors!

	parameter float xParam <defaultValue:2;>; // Ha! must be 2.0
	float x = 0; // Ha! must be 0.0
	if(x < 0) x = -x; // Ha! still must be 0.0
	float3 xyz = float3(1,2,3); // well, this is allowed! hooray

Next up, adventures with finally, finally learning a bit of Flash.

oh, i dont know. what do you think?



Deprecated: Function the_block_template_skip_link is deprecated since version 6.4.0! Use wp_enqueue_block_template_skip_link() instead. in /home/polyomino/omino.com/pixelblog/wp-includes/functions.php on line 6121

0.0341s
(c) 2003-2023 omino.com / contact poly@omino.com