Pues bien, la forma en la que funciona este motor voxel esta perfectamente documentada en este repositorio de GitHub: https://github.com/s-macke/voxelspace
Básicamente, y dicho de una forma que se entienda sin formulas ni código, debemos:
Básicamente, y dicho de una forma que se entienda sin formulas ni código, debemos:
- Aplicar perspectiva en el eje X de forma que cuanto más cerca este un elemento del mapa de la cámara, más "zoom" haremos
- De la misma forma en el eje Y, cuanto más cerca este un elemento del mapa de la cámara, más se exagerará la elevación
- Trazar lineas verticales que alcancen esa altura calculada, empezando o dando prioridad a las más cercanas a la cámara
Muy bien, ahora si queremos animarlo para que por ejemplo de vueltas, tendremos que añadir rotación antes de hacer el crop, y de paso añadiremos un poco de desplazamiento hacia los lados con: rotate=n/50:bilinear=0,crop=340:256:64+32*cos(n/25)
Aquí "n" es el número de frame, y usamos la opción bilinear=0 para que busque siempre el pixel más cercano en vez de calcular promedios, que afectarían al mapa de alturas.Muy bien, ahora ¡empecemos a implementar nuestro motor! para aplicar perspectiva al eje X usaremos el filtro qeg que permite aplicar una formula a cada valor de pixel, en este caso para los colores la formula será p(floor(170+(X-170)*Y/256),Y)
Sobra decir que aquí 170 es la mitad del ancho y 256 es el alto. ¡De momento pinta bien! Para el canal alpha donde también hay que "exagerar" las alturas la formula será clip((height-alpha(floor(170+(X-170)*Y/256),Y))/Y*scale_h+horizon,0,255)usamos clip para mantener los valores dentro de nuestro rango de altura 0-255.
Ahora, para trazar las lineas verticales en principio es necesario un bucle FOR iterando las distintas profundidades como se puede ver en el primer GIF. Esto en FFMPEG no es tan sencillo, aunque en principio existe una función "while" en la expresiones de filtros, me decantaré por usar varios IF-ELSEs encadenados para reducir la complejidad al máximo, aunque como resultado me quede un comando demasiado largo. Empecemos a partir de la profundidad 5 hasta el final en 255. Sería algo así:
if(gt(Y,alpha(X,5)),p(X,5),
if(gt(Y,alpha(X,6)),p(X,6),
if(gt(Y,alpha(X,7)),p(X,7),
if(gt(Y,alpha(X,8)),p(X,8),
if(gt(Y,alpha(X,9)),p(X,9),
if(gt(Y,alpha(X,10)),p(X,10),
...
Y este sería el resultado final: ¡Funcionó!ffmpeg -loop 1 -i c.png -loop 1 -i p.png -filter_complex "[0][1]alphamerge,rotate=n/50:bilinear=0,crop=340:256:64+32*cos(n/25),geq='p(floor(170+(X-170)*Y/256),Y)':a='clip((120-alpha(floor(170+(X-170)*Y/256),Y))/Y*280+0,0,255)',geq='
if(gt(Y,alpha(X,5)),p(X,5),
if(gt(Y,alpha(X,6)),p(X,6),
if(gt(Y,alpha(X,7)),p(X,7),
if(gt(Y,alpha(X,8)),p(X,8),
if(gt(Y,alpha(X,9)),p(X,9),
if(gt(Y,alpha(X,10)),p(X,10),
if(gt(Y,alpha(X,11)),p(X,11),
if(gt(Y,alpha(X,12)),p(X,12),
if(gt(Y,alpha(X,13)),p(X,13),
if(gt(Y,alpha(X,15)),p(X,15),
if(gt(Y,alpha(X,16)),p(X,16),
if(gt(Y,alpha(X,17)),p(X,17),
if(gt(Y,alpha(X,19)),p(X,19),
if(gt(Y,alpha(X,20)),p(X,20),
if(gt(Y,alpha(X,22)),p(X,22),
if(gt(Y,alpha(X,23)),p(X,23),
if(gt(Y,alpha(X,25)),p(X,25),
if(gt(Y,alpha(X,26)),p(X,26),
if(gt(Y,alpha(X,28)),p(X,28),
if(gt(Y,alpha(X,29)),p(X,29),
if(gt(Y,alpha(X,31)),p(X,31),
if(gt(Y,alpha(X,33)),p(X,33),
if(gt(Y,alpha(X,34)),p(X,34),
if(gt(Y,alpha(X,36)),p(X,36),
if(gt(Y,alpha(X,38)),p(X,38),
if(gt(Y,alpha(X,40)),p(X,40),
if(gt(Y,alpha(X,41)),p(X,41),
if(gt(Y,alpha(X,43)),p(X,43),
if(gt(Y,alpha(X,45)),p(X,45),
if(gt(Y,alpha(X,47)),p(X,47),
if(gt(Y,alpha(X,49)),p(X,49),
if(gt(Y,alpha(X,51)),p(X,51),
if(gt(Y,alpha(X,53)),p(X,53),
if(gt(Y,alpha(X,55)),p(X,55),
if(gt(Y,alpha(X,57)),p(X,57),
if(gt(Y,alpha(X,59)),p(X,59),
if(gt(Y,alpha(X,62)),p(X,62),
if(gt(Y,alpha(X,64)),p(X,64),
if(gt(Y,alpha(X,66)),p(X,66),
if(gt(Y,alpha(X,68)),p(X,68),
if(gt(Y,alpha(X,71)),p(X,71),
if(gt(Y,alpha(X,73)),p(X,73),
if(gt(Y,alpha(X,75)),p(X,75),
if(gt(Y,alpha(X,78)),p(X,78),
if(gt(Y,alpha(X,80)),p(X,80),
if(gt(Y,alpha(X,83)),p(X,83),
if(gt(Y,alpha(X,85)),p(X,85),
if(gt(Y,alpha(X,88)),p(X,88),
if(gt(Y,alpha(X,90)),p(X,90),
if(gt(Y,alpha(X,93)),p(X,93),
if(gt(Y,alpha(X,96)),p(X,96),
if(gt(Y,alpha(X,98)),p(X,98),
if(gt(Y,alpha(X,101)),p(X,101),
if(gt(Y,alpha(X,104)),p(X,104),
if(gt(Y,alpha(X,106)),p(X,106),
if(gt(Y,alpha(X,109)),p(X,109),
if(gt(Y,alpha(X,112)),p(X,112),
if(gt(Y,alpha(X,115)),p(X,115),
if(gt(Y,alpha(X,118)),p(X,118),
if(gt(Y,alpha(X,121)),p(X,121),
if(gt(Y,alpha(X,124)),p(X,124),
if(gt(Y,alpha(X,127)),p(X,127),
if(gt(Y,alpha(X,130)),p(X,130),
if(gt(Y,alpha(X,133)),p(X,133),
if(gt(Y,alpha(X,136)),p(X,136),
if(gt(Y,alpha(X,139)),p(X,139),
if(gt(Y,alpha(X,142)),p(X,142),
if(gt(Y,alpha(X,146)),p(X,146),
if(gt(Y,alpha(X,149)),p(X,149),
if(gt(Y,alpha(X,152)),p(X,152),
if(gt(Y,alpha(X,155)),p(X,155),
if(gt(Y,alpha(X,159)),p(X,159),
if(gt(Y,alpha(X,162)),p(X,162),
if(gt(Y,alpha(X,166)),p(X,166),
if(gt(Y,alpha(X,169)),p(X,169),
if(gt(Y,alpha(X,172)),p(X,172),
if(gt(Y,alpha(X,176)),p(X,176),
if(gt(Y,alpha(X,180)),p(X,180),
if(gt(Y,alpha(X,183)),p(X,183),
if(gt(Y,alpha(X,187)),p(X,187),
if(gt(Y,alpha(X,190)),p(X,190),
if(gt(Y,alpha(X,194)),p(X,194),
if(gt(Y,alpha(X,198)),p(X,198),
if(gt(Y,alpha(X,202)),p(X,202),
if(gt(Y,alpha(X,205)),p(X,205),
if(gt(Y,alpha(X,209)),p(X,209),
if(gt(Y,alpha(X,213)),p(X,213),
if(gt(Y,alpha(X,217)),p(X,217),
if(gt(Y,alpha(X,221)),p(X,221),
if(gt(Y,alpha(X,225)),p(X,225),
if(gt(Y,alpha(X,229)),p(X,229),
if(gt(Y,alpha(X,233)),p(X,233),
if(gt(Y,alpha(X,237)),p(X,237),
if(gt(Y,alpha(X,241)),p(X,241),
if(gt(Y,alpha(X,245)),p(X,245),
if(gt(Y,alpha(X,249)),p(X,249),
if(gt(Y,alpha(X,253)),p(X,253),145)))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))':a=255" -r 25 -qp 0 salida.mp4







