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

omino pixel blog

pixels, motion, and scripting
/\/\/\/\
david van brink // Thu 2013.10.3 19:46 // {webgl}

webgl matrix interlude

This just in: The entire world of WebGL matrixes is backwards.

Instead of saying

translatedPoint = [x y z 1] [1 0 0 Tx
                             0 1 0 Ty
                             0 0 1 Tz
                             0 0 0 1]

or

var translateM = [1,0,0,tx,
                  0,1,0,ty,
                  0,0,1,tz,
                  0,0,0,1];

or

vec4 p2 = pos * rotateFirstM4 * translateM4;
// alternatively
vec4 p2 = pos;
p2 *= rotateFirstM4;  // first transformation
p2 *= translateM4;    // second transformation

they want everything backwards, like

mat4 translateM4 = mat4(vec4(1,0,0,0),
                        vec4(0,1,0,0),
                        vec4(0,0,1,0),
                        vec4(tx,ty,tz,0));
vec4 p2 = translateM * rotateFirstM4 * pos;

It does have one small advantege. It takes a little less paper-space to write out.

translatedPoint = [1  0  0  0  *  [x
                   0  1  0  0      y
                   0  0  1  0      z
                   Tx Ty Tz 1]     1]

And you could just do it all the other way, transposed, and left to right. Except… I’ve chosen to use a matrix library glMatrix.js. It’s handy, it’s good, it has all the basics like mat4.rotate() and everything. But it enforces the OpenGL backwards-ness.

I grump.


People blame this on the fact that OpenGL lays out its matrixes in-memory in “Column Major Order”, meaning the first column takes the first four memory locations. Whereas we read them and code them in “Row Major”-ey style, left to right and top to bottom.

But that’s no excuse… by putting your horizontal vector on the left of the matrix, instead of vertical on the right, you can get pretty code.

On my backlog is “translate glMatrix.js to Row Major stylings”, so my code can look more like textbook math.

oh, i dont know. what do you think?


david van brink // Sun 2010.12.26 17:47 // {general}

Beziers

We most of us use Bezier curves all the time, for masks, shapes, animation curves. They’re pretty intuitive. Points, control handles… yeah, you can usually get them to do what you want.

To do some math recently, I googled a bit more about them. They’re named from Pierre Bezier (1910-1999) who popularized them for CAD while designing cars for Renault. The standard “two-handle” style used by Adobe apps (and most others) is a “cubic Bezier”, because the equation uses some cubes.

To walk a cubic Bezier mathematically, given the four points (two end points, and two intermediate control handles), you can use this Python code, varying t from 0.0 to 1.0:

# given points p0, p0Out, and p1In, and p1, and t varying from 0.0 to 1.0...
a0 = (1 - t) ** 3
a1 = 3 * (1 - t) ** 2 * t
a2 = 3 * (1 - t) * t ** 2
a3 = t ** 3
# calculate x and y
x = a0 * p0.x + a1 * p0.xOut + a2 * p1.xIn + a3 * p1.x
y = a0 * p0.y + a1 * p0.yOut + a2 * p1.yIn + a3 * p1.y

But that’s all tricky. Here’s an intuitive way to visualize just what the heck a Bezier curve really (for really) is:

I did this animation in Python… stay tuned for much more on that front.

Super kudos to bimixual.org for the clear explanation.

oh, i dont know. what do you think?


david van brink // Mon 2008.10.20 23:28 // {after effects pixel bender}

Pixel Bender: A Gradient… and Some Math

Featuring mix(), clamp(), and dot().

Very often I need a simple linear-gradient in After Effects, and end up sing the four-color gradient because that’s all I can find. And so, here, for your perusal, is a Pixel Bender implementation of a Linear Gradient Generator.

After that, we’ll see the code, and after that, a quick review of the math.

Here is the .swf. (With some improvements on the Pixel Bender viewer — you can drag any float2 parameters as points, now.)

Isn’t that fun? Well, I’m easily amused.

The Code

Here’s the Pixel Bender code.



kernel Gradient 
{
    output pixel4 dst;
    parameter float2 p1<minValue:float2(0,0);maxValue:float2(400,400);defaultValue:float2(10,10);>;
    parameter float2 p2<minValue:float2(0,0);maxValue:float2(400,400);defaultValue:float2(130,130);>;

    parameter float3 color1<defaultValue:float3(0,0,0);>;
    parameter float3 color2<defaultValue:float3(1,1,1);>;

    void evaluatePixel() 
    {
        float2 co = outCoord();
        
        // shift everything relative to p1.
        float2 uv = co - p1;
        float2 xy = p2 - p1;

        // the math.
        float g = dot(uv,xy) / dot(xy,xy);
        g = clamp(g,0.0,1.0);

        // the color.
        dst.a = 1.0;
        dst.rgb = mix(color1,color2,g);
    }
}

The Math

I’m ok at math, but not always fluent in it. I can remember what a matrix is, but not necessarily exactly what a cross-product is. Maybe you’re like me. In which case this quick run through the math and logic behind the gradient fill will be quite useful in your own Pixel Bender bending!

And be assured, a few pages of high-school-algebra-style scribblings, and the occasional google (How do I invert a rotation again? What’s the formula for perspective?) really can lead to workable results. Good clean fun!

Let’s go.

Consider a trivial gradient, where our two control points are at (0,0) and (1,0). That would be easy! Just take the x-value of any pixel, and that’s your color. (That is, the position on the ramp between your two gradient colors.)

trivialGradient.png

By the way, this picture is from Ron Avitzur’s “Graphing Calculator”. The story of its development is legendary; less well known is that it’s actually a very powerful and useful tool.

I found the easiest way to think about a general two-point gradient was to ask, “How do we rotate and scale our control points back to this trivial gradient?” And let’s assume that the first control point is always at (0,0).

Here is matrix for a basic rotation, which moves any input point by a rotation around (0,0):

gradientEq1.jpg[1]

A rotate-and-scale matrix looks like that, times a constant on each element.

We want to think of our general gradient as a transformation on the trivial gradient. Let’s say our second control point is (X,Y), and our first control point is (0,0) as mentioned above. The rotate-and-scale matrix being applied, conceptually, is:

gradientEq2.jpg[2]

To figure out the 0-to-1 trivial position of any of our input points, we need to invert it. A matrix inversion looks like:

gradientEq3.jpg[3]

So, we invert the second equation [2] by plugging it in to [3] and get:

gradientEq4.jpg[4]

Now, let’s call each pixel position of our output (U,V). For our gradient, we actually don’t care about the resulting Y position; just X gives us our gradient value. So we get:

gradientEq5.jpg[5]

Now, the definition of the dot product of (A1,A2,A3,…) and (B1,B2,B3,…) is (A1B1 + A2B2 + A3B3 + …). So, from the above, UV+XY is, conveniently, (U,V) dot (X,Y). Similarly, X2 + Y2 is (X,Y) dot (X,Y).

And now, when you read this part of the code a second time, it should be much clearer what’s going on:

    void evaluatePixel() {
        float2 co = outCoord();
        
        // shift everything relative to p1.
        float2 uv = co - p1;
        float2 xy = p2 - p1;

        // the math.
        float g = dot(uv,xy) / dot(xy,xy);
        g = clamp(g,0.0,1.0);

        // the color.
        dst.a = 1.0;
        dst.rgb = mix(color1,color2,g);
    }

Simple and useful!

Next up, soon, more cubes.

5 comments
Ron Avitzur // Wed 2008.10.22 10:5710:57 am

You can express max(min(x,1),0) more concisely in GC as clamp(x,0,1).

less well known is that it’s actually a very powerful and useful tool.

Thank you! (I find it deeply ironic that after so many years working to improve math education, the story is way better known than the software.)

david van brink // Wed 2008.10.22 11:1111:11 am

Hi Ron — I remember meeting you briefly in the lobby of I think DA2, because Qarin knew you. I don’t remember which color badge you were using that day. Anyhow, yep, I use GC pretty often for thinking about stuff. Also, for fast cool graphics, like on the side of the “space house” in http://omino.com/gata/.

Thanks for a great product!

aron // Mon 2008.10.27 00:4012:40 am

“Very often I need a simple linear-gradient in After Effects, and end up sing the four-color gradient because that’s all I can find.”
hmm, haven’t you ever noticed Generate—> ramp? that’s what linear gradient is called in ae.
A bit simpler than this code 🙂 but anyway, good job!

david van brink // Mon 2008.10.27 17:525:52 pm

Hahaha Thanks aron. I knew it had to be there somewhere. Oh well, I figured out some math along the way. And seriously — two lines for the math, really, taht’s pretty simple, isn’t it? 🙂

caseyc // Thu 2010.01.21 11:1311:13 am

Would this work as a “Gradient Overlay” style filter in Flash, so you could add gradient fills to text? I’m sick of hacking the effect with bevel filters etc.

oh, i dont know. what do you think?



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