Jakoś tak się złożyło, że kiedy ja zabrałem się za bibliotekę VPython w 2016 roku, projekt był właśnie porzucony, a developerzy skupili się na wersji opartej o Jupytera i
GlowScript-a.
GlowScript-a umożliwia, kompilację kodu python-a do JavaScipt-u. Powstał nawet bardzo fajny serwis
trinket.io, który zamyka cały proces tworzenia w przeglądarce. Jako narzędzie edukacyjne, używanie VPythona stało się jeszcze wygodniejsze. Wraz z wersją Ubuntu 18.4, paczka VPythona została usunięta z repozytorium.
Trochę szukania i okazuje się, że nie jest tak źle. Strona
VPython informuje o projekcie
IVisual, jednak stackoverflow sugerował wersję
VPython. Nie wiem jak jest między nimi różnica, zacząłem od tego drugiego i przy nim pozostałem.
Instalacja poszła z pewnymi problemami. Zanim zostanie zainstalowany VPython, trzeba zainstalować paczkę Jupyter (w tej kolejności), jest to spowodowane jakimś bugiem:
mkdir vpython3
cd vpython3/
virtualenv -p python3 venv
source venv/bin/activate
pip3 install jupyter
pip3 install vpython
W API pojawiły się pewne drobne zmiany, dlatego postanowiłem przepisać
standardowy przykład "bouncing ball" na nową wersję. Przy okazji odkryłem na stronie projektu informacje o module
povexport do przechwytywania obrazu animacji. Wcześniej przechwytywałem obraz z ekranu (za pomocą
PIL) i wycinałem obszar w którym pojawia się okno animacji.
Przykład:
import vpython as vp
import povexport
scene = vp.canvas(title='3D scene',
x=0, y=0, width=400, height=400,
center=vp.vector(0, 0, -0), autoscale=False)
vp.display(scene)
starting_height = 4
floor = vp.box(pos=vp.vector(0, 0, 0), length=4, height=0.5, width=4, color=vp.color.green)
ball = vp.sphere(pos=vp.vector(0, starting_height, 0), radius=1, color=vp.color.red)
ball.velocity = vp.vector(0, -1, 0)
dt = 0.01
frame = 0
while 1:
vp.rate(100)
ball.pos = ball.pos + ball.velocity*dt
if ball.pos.y < ball.radius:
ball.velocity.y = abs(ball.velocity.y)
else:
ball.velocity.y = ball.velocity.y - 9.8*dt
file_name = 'img-%04d.pov' % frame
inclist = ['colors.inc', 'stones.inc', 'woods.inc', 'metals.inc']
povexport.export(scene, filename=file_name, include_list=inclist)
frame += 1
if ball.pos.y > starting_height:
break
Co się zmieniło (dla mnie):
- nowy moduł importu (zamiast visual jest vpython)
- parametry przyjmują explicit obiekt vector i nie tolerują trójelementowej krotki
- funkcji display zmieniła nazwę na canvas
Słów kilka o
povexport. Sprytna biblioteka, która zapisuje informacje o bryłach i powierzchni w tekstowym języku/formacie SDL (Scene Description Language). Dane te następnie można przetworzyć za pomocą programu jak
povray na format graficzny np. png i dalej skonstruować animację. Daje to całkiem ciekawe możliwości, bowiem VPython w założeniu oferuje tylko proste możliwości graficzne,
povray daje dużo więcej możliwości jeżeli chodzi o renderowanie i ray tracing.
Nie obyło się bez małej ingerencji w kod, gdyż domyślnie obliczania ustawień odległości kamery w
povexport były zbyt małe. Może coś robiłem nie tak, ostatecznie zmieniłem jedną linijkę.
# ...
# cpos = 1.5*displayscale*canv.camera.pos # 1.5 is a not understood fudge factor
cpos = 10*displayscale*canv.camera.pos
# ...
W zasadzie, można pokusić się również o stworzenie skryptu, który edytuje pliki .pov i zmienia linijki z ustawieniami kamery. Zmiana w pliku .pov, którą uzyskałem:
camera {
right <-image_width/image_height, 0, 0> // vpython uses right-handed coord. system
location <0.000000, 0.000000, 173.205081>
up <0.000000, 1.000000, 0.000000>
look_at <0.000000, 0.000000, 0.000000>
angle 60.000000
}
Przerobienie plików z formatu .pov na .png:
for i in *.pov; do povray Height=400 Width=400 Display=false Output_File_Type=N $i 2>/dev/null; done
Nowa wersja skryptu (
[1],
[2]) do tworzenia animacji video/gif. Wyeliminowałem straty, które pojawiały się podczas składaniu video z plików .png, przez zastosowania kodeka H.264 i manipulacji "Constant Rate Factor" (uwaga,
z ustawieniami CRF trzeba ostrożnie)
#!/bin/sh
PALETTE_FILE="tmp_pallete.png"
VIDEO_FILE="output_video.mp4"
INPUT_FILES="img-%4d.png"
OUTPUT_FILE="output.gif"
FILTERS="fps=25"
ffmpeg -r 100 -i $INPUT_FILES -c:v libx264 -crf 0 -preset veryslow $VIDEO_FILE
ffmpeg -v warning -i $VIDEO_FILE -vf "$FILTERS,palettegen" -y $PALETTE_FILE
ffmpeg -v warning -i $VIDEO_FILE -i $PALETTE_FILE -lavfi "$FILTERS [x]; [x][1:v] paletteuse" -y $OUTPUT_FILE
Efekt końcowy: