/* * ffmpeg -f rawvideo -s 640x360 -r 25 -pix_fmt rgba -i /tmp/rubik.raw -vf palettegen /tmp/rubik.png * ffmpeg -f rawvideo -s 640x360 -r 25 -pix_fmt rgba -i /tmp/rubik.raw -i /tmp/rubik.png -filter_complex paletteuse /tmp/movie.gif */ #include #include typedef struct { int r; int g; int b; } color_t; typedef struct { int a; color_t color; } point_t; unsigned char data[160*90*4]; point_t point_color(float t0, float x0, float y0, int frame) { /* color definition source: https://www.deviantart.com/the-rubiks-cube/journal/Using-Official-Rubik-s-Cube-Colors-268760351 */ color_t c1 = { r : 196, g : 30, b : 58 }; color_t c2 = { r : 0, g : 81, b : 186 }; color_t c3 = { r : 255, g : 255, b : 255 }; color_t c4 = { r : 255, g : 88, b : 0 }; color_t c5 = { r : 0, g : 158, b : 96 }; color_t c6 = { r : 255, g : 213, b : 0 }; float x = t0 * x0; float y = t0 * y0; float z = 3 - t0; /* aaaaaaaaaaaand rotate! */ float angle = 2 * M_PI * frame / 100; float x2 = x * cos(angle) - z * sin(angle); float z2 = z * cos(angle) + x * sin(angle); x = x2; z = z2; angle = M_PI / 3 * cos(2 * M_PI * frame / 50); x2 = x * cos(angle) - y * sin(angle); float y2 = y * cos(angle) + x * sin(angle); x = x2; y = y2; angle = M_PI / 3 * cos(2 * M_PI * frame / 100); y2 = y * cos(angle) - z * sin(angle); z2 = z * cos(angle) + y * sin(angle); y = y2; z = z2; if (x > .9) return (point_t){ a : 1, color : c1 }; if (y > .9) return (point_t){ a : 1, color : c2 }; if (z > .9) return (point_t){ a : 1, color : c3 }; if (x < -.9) return (point_t){ a : 1, color : c4 }; if (y < -.9) return (point_t){ a : 1, color : c5 }; if (z < -.9) return (point_t){ a : 1, color : c6 }; return (point_t){ a : 0 }; } color_t raytrace(int _x, int _y, int frame) { float x = _x, y = _y; /* translate to center of canvas */ x -= 80; y = 45 - y; /* 100 pixels mean 1 */ float scale = 1. / 100.; float x0 = x * scale; float y0 = y * scale; /* camera center is at (0,0,3) screen plane at z=2 */ /* sphere is at (0,0,0) r=1 */ float r = 1; /* a sphere is x^2 + y^2 + z^2 = r^2 * current ray is from (0,0,3) to (x0,y0,2) * a point (x,y,z) is on the ray if there is t such that: * (x,y,z) = t * ray * for t=0 we decide to be at (0,0,3) * for t=1 we decide to be at (x0,y0,2) * so for generic point (x,y,z), we have: * 0 0 0 3 * 1 x0 y0 2 * t x y z * so: * t = x / x0 * t = y / y0 * t = (z-3) / -1 * and: * x = t * x0 * y = t * y0 * z = 3 - t * a point both on sphere and ray has to be: * (t * x0)^2 + (t * y0)^2 + (3 - t)^2 = r^2 (3-t)^2 = 9 - 6*t + t^2 * t^2 * (x0^2 + y0^2 + 1) + t * (-6) + 9 - r^2 = 0 * if b^2-4ac >= 0 there is a solution, so the ray intersects the sphere */ float a = x0 * x0 + y0 * y0 + 1; float b = -6; float c = 9 - r * r; color_t blue = { r : 158, g : 178, b : 235 }; float det = b * b - 4 * a * c; if (det < 0) return blue; float t0 = (-b - sqrt(det)) / (2 * a); float t1 = (-b + sqrt(det)) / (2 * a); /* process min t then max */ if (t1 < t0) { float x = t1; t1 = t0; t0 = x; } point_t res = point_color(t0, x0, y0, frame); if (res.a == 0) res = point_color(t1, x0, y0, frame); if (res.a == 0) return blue; return res.color; } void point(int x, int y, color_t color) { data[(y * 160 + x) * 4] = color.r; data[(y * 160 + x) * 4 + 1] = color.g; data[(y * 160 + x) * 4 + 2] = color.b; } void draw(int frame) { int x; int y; for (y = 0; y < 90; y++) for (x = 0; x < 160; x++) { point(x, y, raytrace(x, y, frame)); } } void dump(void) { int i, j; for (i = 0; i < 90*4; i++) { for (j = 0; j < 160*4; j++) { int x = j / 4; int y = i / 4; putchar(data[(y * 160 + x) * 4]); putchar(data[(y * 160 + x) * 4 + 1]); putchar(data[(y * 160 + x) * 4 + 2]); putchar(255); } } } int main(void) { int i; for (i = 0; i < 100; i++) { draw(i); dump(); } return 0; }