art with code

2010-07-27

Berlin!



Hanging out in Berlin this week, with a shiny new sketchbook to boot.

Went to the Pergamonmuseum on Sunday to be all Ozymandias. The things they have there are huge (indoor display of 15m high pillars, etc.) and pretty old (2-3 thousand years) and beautiful. And messed up in that ancient style of messed-upness. The objects in the Islamic museum side were nicee, superior metalcraft and calligraphy (also a millennium or two younger than the rest of the stuff but who's counting). The Assyrian reliefs had writing going across them, like a textured strip of cuneiform. They had a special display on the coloring of the Greek statues, with brightly colored reproductions of much RGB might.

2010-07-20

Silly C hacks

Macros! To do safe mallocs and fopens! Brittle like the glass you tread upon!

/* A small cat that prints out the files given to it */

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <malloc.h>
#include <sys/stat.h>

#define BLOCK(pre, post, ok, error, block) \
{ \
pre; \
if (ok) { \
block; \
post; \
} else { \
error; \
} \
}

#define with_malloc(t, var, sz, block) \
BLOCK(t var = (t)malloc(sz), free(var), var, \
fprintf(stderr, "Malloc failed, out of memory\n"); exit(1), block)

#define with_file(var, fn, mode, error, block) \
BLOCK(FILE* var = fopen(fn, mode), fclose(var), var, error, block)

#define with_file_contents(var, len, fn, error, block) \
with_file(var##__f, fn, "r", error, \
off_t len = file_size(fn); \
with_malloc(char*, var, len+1, \
len = fread(var, 1, len, var##__f); \
var[len] = 0; \
block))

off_t file_size(char *path)
{
struct stat st;
stat(path, &st);
return st.st_size;
}

int main(int argc, char *argv[]) {
int i;
for (i=1; i<argc; i++) {
with_file_contents(s, slen, argv[i], fprintf(stderr, "Couldn't open %s.\n", argv[i]),
fwrite(s, 1, slen, stdout);
);
}
}



It would be kinda fun to port the whole functional array-munging part of prelude.ml to C. Fun in a very painful fashion.

Small C++ SSE3 vector lib

If you want to do some simple and somewhat fast vector math in C++, sse.h might be fun.

It has float4, double2 and double4 structs, constructors of different kinds, operator overloading and methods for horizontal add, dot product and swizzling.

Code examples:

4x4 matrix multiply with floats and doubles:

// dst = a x b, ~44 cycles with -O3 -funroll-loops
void mmul4x4 (const float *a, const float *b, float *dst)
{
for (int i=0; i<16; i+=4) {
float4 row = float4(a) * float4(b[i]); // float4(a) is {a[0], a[1], a[2], a[3]}
// float4(b[i]) is {b[i], b[i], b[i], b[i]}
for (int j=1; j<4; j++)
row += float4(a+j*4) * float4(b[i+j]);
*(float4*)(&dst[i]) = row;
}
}

void mmul4x4d (const double *a, const double *b, double *dst)
{
for (int i=0; i<16; i+=4) {
double4 row = double4(a) * double4(b[i]);
for (int j=1; j<4; j++)
row += double4(a+j*4) * double4(b[i+j]);
*(double4*)(&dst[i]) = row;
}
}


Dot product of two arrays of vectors:

double dotArrays (const double2 *a, const double2 *b, int len)
{
double2 sum;
for (int i=0; i<len; i++) {
sum += a[i] * b[i];
}
return sum.sum();
}

2010-07-12

AC the planet

+7C equals ow. (Check out the map in that: yellow areas would be deadly.)

Guess humans wouldn't have survived too well in the Cretaceous period (when the avg. temp was 15-20C higher than today. Tropical sea surface temperatures at around +37C. Here a fun lecture on that.) If you'd time-travel back to ride on brontosaurs or whatever, you'd probably end up with a heat stroke.

2010-07-09

Future display tech

Graphics, where is the limit? The display resolution of the day is 1920x1080, or Full HD, as it is called, with high-end displays operating at 2560x1600. Modern graphics cards are quite capable of running games at those resolutions. Adding stereo-3D on top of that doubles the amount of pixels, but that isn't much of a problem either. Pushing up the resolution from 100 ppi to 300 ppi would need an extra 9x the resolution, which would probably start pushing today's limits.

To do no-glasses viewing-angle independent 3D requires a pixel for every direction (or something like a pixel, e.g. plop a lens in front of a flat pixel array). That would easily multiply the resolution by 16 or 256 or such. Not to mention requiring new display tech and the cameras to produce content for them.

You probably want to push the dynamic range of the display as well, in order to reproduce bright noonday sun and a pitch-black night. According to this page the contrast from the sun to the limits of human night vision is around 300 trillion to one. To do that range with fixed step would take 48 bits. The display would have to be able to emit from a couple photons at a time all the way up to a billion candela per square meter, which is a good million times brighter than today's bright LCD displays.

Using 16 bits per RGB channel for encoding the hue and saturation, and then 48 bits divided among them to do the luminosity would result in 96 bits per pixel. With 32-bit alpha channel, 128 bits per pixel, or four times today's display bit depth.

The display would be nice if it covered your field of vision from a comfortable viewing distance. A wall-sized display might be the ticket to that. Let's say 5 meters by 3 meters.

Summing up all the above, a future wall-sized 256x integral hologram display with 300 ppi resolution and natural dynamic range would have 550 gigapixels at 128-bit color, and would need a 8.8 TB frame buffer from the graphics processor. A more modest 30" display at those specs would have only 9.4 gigapixels and a 150 GB frame buffer. If we assume that 1 GFLOPS is enough to run a 32-bit two megapixel display, a 128-bit 550 gigapixel display would require around 1,100,000 GFLOPS of processing power. Today's graphics cards are at around 2 GFLOPS, so if they keep doubling in performance every year it'd take 20 years for them to get to the required 1.1 PFLOPS.

What would such a display look like in person? I suppose the experience would be like looking out of a window. A window to a place that doesn't exist. Where the searing hot twin suns rise above the desert, windswept dialog boxes of long-forgotten programs casting long shadows into your living room. Or the view out into a starlit forest: a ghastly green shape here, another there, the darkness registering as a noisy shimmer as the rods in your eyes struggle to resolve the scant errant photons reflected from your surroundings.

I don't really know if 256x holography would be enough, though. Maybe you want to boost that to 4096x or more. And make the display wrap around you for that VR cave experience. And add one extra color channel for tetrachromats. Or heck, just use photon energy histograms as your colors instead of the limited RGB. The people with those new-fangled omni-EM eyes will thank you for that. And a monitor just isn't a monitor unless you can do astronomical spectrography off it, take your X-ray with it, microwave an egg, make your radio play tunes and do a realistic documentary of the Chernobyl disaster, complete with the viewer receiving a half dozen gray of radiation.

2010-06-24

Funny multiplication tricks

This kinda struck me yesterday: multiplication is additive pattern splatting (I just came up with that term.) I've been seeing binary tricks that have used multiplication as copy operation but never really understood it before now.

With sparse binary patterns it's quite apparent. For example, let's multiply 00100010001001001 by 111. Sounds hard? It's actually easy, you replace each 001 in the long pattern with the short pattern and end up with 11101110111111111. In the same way you can copy a byte to different places in a word. This may sometimes be faster and/or less bother than bit-shifting:

int64 fillInt64WithByte_shift(int8 b) {
return b << 56 | b << 48 | b << 40 | b << 32 | b << 24 | b << 16 | b << 8 | b;
}

vs.

int64 fillInt64WithByte_mul(int8 b) {
return 0x0101010101010101 * b;
}


It also works with decimal numbers: 10101 x 99 is 999999. If you have a number that's not a 1 in the target pattern, you need to multiply your source pattern with that: 001 002 003 0005 x 203 = 203 406 609 1015.

However, the simple copy splatting only works when you don't have overlaps. With overlaps you need to add the new splat to the target pattern. Let's consider non-carry addition first using 1001301001 x 211 as the example. First you fill in the non-overlapping parts (the 1001001001 pattern) and end up with 211 211 211 211. To integrate the 3 into the result you multiply it by 211 and add the result to the target pattern.

1001301001 x 211 =
211211211211
+ 633
------------
211274511211


When you have carries, it gets a good deal more tedious. Consider binary 1111 x 1111. First we splat 1111 on the first 1 in the target pattern: 01111000, then on the second 1: 00111100, etc. and finally get 01111000 + 00111100 + 00011110 + 00001111 = 11100001. Drudgery.

We could move it to base-15 and get 10 x 10 = 100, then convert that back to base-2... for no benefit whatsoever. 1111^2 x 1 = 1111 x 1111. Or do a trick with 10000 x 1111 - 1111 = 11110000 - 1111 = 11100001.

Or convert to base-16 for F x F = E1, then convert that to base-2 for 1110 0001. Note the similarity between F x F = E1, 9 x 9 = 81, o7 x o7 = o61, b1 x b1 = b01. This can be explained by

For base N, N = 10 - 1:
N x N = N x (10 - 1)
= N x 10 - N
= N x 10 - (10 - 1)
= (N x 10 - 10) + 1
= (N-1) x 10 + 1


Back to splat-additive multiplication, it seems to work for positive integers. Does it work for negative ones? Well, yes, 1001001 * -101 = -101000000 + -101000 + -101 = -101101101. And there's that sign-trick to turn multiplication of negative integers to multiplication of positive integers: -A x B = -(A x B), -A x -B = -(-(AxB)) = AxB.

Does it work for matrices... not that I can tell. Matrix multiplication is not splatting anyhow. What would a splat-additive matrix multiplication operation look like? I don't know, let's start exploring by trying to extend the scalar splat-additive multiplication to vectors. If we treat vectors as bucket-based integers (that is, no carries, base-∞), integer-like splat-additive vector multiplication would be quite easy. For example, [1 0 0 2] [x] [3 6] = [3 6 0 6 12], but what can that be used for? Is there some sensible conversion to scalar from it? Can it be extended to matrices?

As an aside, scalar vector multiplication can be used to make integer multiplication a bit simpler. Using 32 x 1935 as an example: 32 x [1 9 3 5] = [32 288 96 160]. We can convert that to an integer value by doing a base conversion for each element and adding them together: 32*1000 + 288*100 + 96*10 + 160*1 = 61920 = 32 x 1935.

Another way to do splat multiplication with vectors would be to generate a matrix from vector multiplication, an approach not entirely without appeal. [A B] x [C D] would result in [ [AC AD] [BC BD] ]. It can express number multiplication as a special case: AxB would be |A| x |B| = AxB (I'm using |A| to say that |A| is a single-element vector of zero dimension). If you have dimensional numbers, it preserves the dimensionality of the result, [Length] x [Width] = [[Area]].

Can the vector-splatting multiplication be extended to vectors of vectors? Seems so:

[ [1 2] [3] ] x [ [5 5] [6 2] ] would be, uh, ...
[ [ [1 2]x[5 5] [1 2]x[6 2] ] [ [3]x[5 5] [3]x[6 2] ] ] =
[ [ [[5 5] [10 10]] [[6 2] [12 4]] ] [ [[15] [15]] [[18] [6]] ] ]

And to continue with the dimensional numbers,

[Height] x [[Area]] = [[[ Height x Area ]]]
[[Area]] x [[Area]] = [[[[ Hypercube! ]]]]


What properties would this vector-splat multiplication have?

[ A B ] x [ C D ] = [ [AC AD] [BC BD] ]
[ C D ] x [ A B ] = [ [CA CB] [DA DB] ]
Not commutative (unless the equality is sum-equality: A = B if sum(A) = sum(B).)

([ A B ] x [ C D ]) x [ E F ] =
[ [AC AD] [BC BD] ] x [ E F ] =
[ [[ACE ACF] [ADE ADF]] [[BCE BCF] [BDE BDF]] ]

[ A B ] x ([ C D ] x [ E F ]) =
[ A B ] x [ [CE CF] [DE DF] ] =
[ [[ACE ACF] [ADE ADF]] [[BCE BCF] [BDE BDF]] ]
Seems to be associative.

The identity element would be a dimensionless 1:
1 x [ A B ] = [ 1xA 1xB ] = [ A B ]

Note that a dimensional one won't do:
[1] x [ A B ] = [ [A] [B] ]

To do the inverse element, we need vectors with negative dimensions:
I x [ A B ] = 1 requires I to reduce the dimension of [ A B ]

Additionally, the inverse element requires sum-equality (i.e. [A B] = [B A])
[A B] = [C] iff A + B = C
|0.5 0.5| = 0.5 + 0.5 = 1, hello confusing notation for dimensionless vectors
[[A [B] C]] = |[[[B]]] [[A C]]|, hello polynomial notation

I x [ A B ] = 1
[1/2A 1/2B]-1 x [ A B ] = |A/2A B/2B| = |1/2 1/2| = 1

So the inverse function would be
-- get inverse vector for which V x inv(V) = 1
inv(V) = (map (\i -> 1/(V_len*i)) V)-V_dim


...ghhh. So, uh, I guess with sum-equality it could maybe form a field with some sort of vector addition. The real question is whether there is anything where this multidimensional splat-multiplication works better than existing methods. It looks a lot like polynomials. Maybe the vector addition could be..

|A B| = (A + B)0
[A B] = (A + B)1
|[[A B] C] D| = |[[A B]] [C] D| = (A+B)2 + C1 + D0
= [[A B]] + [C] + |D|

That's kinda interesting, the (A+B)2 isn't (A+B)2 but rather means that (A+B) is a two-dimensional number.

Oh well, back to the grind.

2010-06-06

WebGL stereo rendering demo


Cross your eyes and prepare for eyestrain, here comes the WebGL stereogram. Drag with LMB to rotate, drag with MMB to pan, wheel zooms, shift-wheel scales model.

The demo uses glScissor and glViewport to restrict drawing to one side of the viewport, then draws the scene to the left and right sides with the camera offset slightly. The camera offset is along the right-vector of the camera and you can get it by finding the normal of the camera up and look vectors. Basically right = cross((lookAt - position), up), then leftEye = position - (sep/2 * right); rightEye = position + (sep/2 * right). Then you render to left viewport from rightEye and to the right viewport from leftEye.

Blog Archive