Obsah
1. Přidání elementů, které Vapory ve svém výchozím nastavení nepodporuje
2. Mapy barev a jejich použití s některými typy procedurálních textur
3. Testovací scéna napsaná přímo pro POV-Ray, která obsahuje torus
4. Shodná testovací scéna napsaná v Pythonu s využitím Vapory a nové třídy Torus
5. Zjednodušený zápis mapy barev – scéna popsaná v jazyku POV-Ray
6. Zjednodušený zápis mapy barev – scéna popsaná v jazyku Python s využitím Vapory
7. Využití implicitních ploch (blobs, metaballs) v POV-Rayi
8. Rozšíření možností CSG pomocí implicitních ploch
9. Konstrukce implicitních ploch s využitím kostry
10. Vykreslení implicitních ploch POV-Rayem
11. Vykreslení stejné scény s využitím knihovny Vapory
12. Idiomatický kód – použití n-tic namísto seznamů
13. Využití možností knihovny MoviePy pro jednodušší vytvoření animace
14. Úprava příkladu z předchozího článku: animace změny CSG tělesa a textury na toru
15. Příprava na animaci změn parametrů implicitních ploch
16. Shodná testovací scéna napsaná v Pythonu s využitím Vapory
17. Naprogramování animace – tvorba jednotlivých snímků později spojených do videa
18. Naprogramování animace – použití MoviePy
19. Repositář s demonstračními příklady
1. Přidání elementů, které Vapory ve svém výchozím nastavení nepodporuje
V dnešním článku tematicky navážeme na čtyři předchozí články, v nichž jsme se zabývali jak popisem možností nabízených knihovnou MoviePy určené pro zpracování video souborů (včetně tvorby souborů nových), tak i knihovny Vapory, která slouží pro přípravu 3D scén určených pro vykreslení raytracerem POV-Ray:
- Programová tvorba a nelineární editace videa s využitím knihovny MoviePy
https://www.root.cz/clanky/programova-tvorba-a-nelinearni-editace-videa-s-vyuzitim-knihovny-moviepy/ - Použití MoviePy společně s Matplotlibem pro tvorbu animovaných grafů
https://www.root.cz/clanky/pouziti-moviepy-spolecne-s-matplotlibem-pro-tvorbu-animovanych-grafu/ - Použití MoviePy společně Matplotlibem pro tvorbu animovaných grafů (dokončení)
https://www.root.cz/clanky/pouziti-moviepy-spolecne-matplotlibem-pro-tvorbu-animovanych-grafu-dokonceni/ - Projekt Vapory: kombinace možností Pythonu a POV-Raye
https://www.root.cz/clanky/projekt-vapory-kombinace-moznosti-pythonu-a-pov-raye/
Obrázek 1: Scéna pojmenovaná „Reach for the Stars“, která byla POV-Rayem vykreslena na Mezinárodní kosmické stanici (jedná se o variantu se zmenšeným rozlišením oproti originálu).
Obě zmíněné knihovny, tj. jak MoviePy, tak i Vapory, jsou určeny pro použití v programovacím jazyku Python, takže se přímo nabízí otázka, jestli je možné jejich možnosti nějakým vhodným způsobem zkombinovat. Ve skutečnosti to možné je, a to navíc relativně jednoduše. Nejdříve si však ukážeme další možnosti nabízené knihovnou Vapory, zejména použití takzvaných map barev (color map) a taktéž rozšíření této knihovny o další 3D entity (což je, jak uvidíme dále, až překvapivě snadné). Ve druhé třetině článku se zmíníme o použití implicitních ploch a v závěrečné části se pak budeme věnovat již slíbené přímé tvorbě video souborů přímo z Vapory.
Obrázek 2: Procedurální textury dřeva: od smrku přes dub až po mahagon. Právě v procedurálních texturách jsou použity mapy barev popsané v navazujících kapitolách.
Pokud nějaká 3D entita není ve Vapory podporována, je ji možné do knihovny přidat a to velmi snadno. Postačuje vytvořit novou třídu se stejným jménem, jako má příslušná entita, a tuto třídu odvodit od POVRayElement. Třída může být prázdná (vše se pouze zdědí), takže se vlastně jedná o dvouřádkovou deklaraci:
class Torus(POVRayElement): """Torus()"""
Nic dalšího není zapotřebí provádět!
2. Mapy barev a jejich použití s některými typy procedurálních textur
U mnoha procedurálních textur, jejichž ukázky budou použity v dalších demonstračních příkladech, je nutné nějakým způsobem specifikovat způsob použití barev, protože se jedná o textury zadané nějakou funkcí, která pro každý bod v 3D prostoru vrátí reálné číslo v rozsahu 0–1 (to je typické chování naprosté většiny procedurálních textur v POV-Rayi). Toto číslo je nutné nějakým způsobem převést na barvu. V případě diskrétních hodnot (uložených například v rastrových obrázcích typu BMP, GIF či PNG) se pro tento účel využívají barvové palety (color palettes), v POV-Rayi je pro převod reálných čísel na barvu použito takzvaných barvových map (color maps). Zadání barvových map v jazyku POV-Ray je poměrně jednoduché – specifikuje se vždy reálná hodnota a barva platná pro tuto hodnotu.
Obrázek 3: Testovací „panáci“ pokrytí jednoduchými procedurálními texturami (zleva doprava): lineárním gradientním přechodem, radiálním gradientním přechodem, sférickým gradientním přechodem a lineárním přechodem otočeným o 90°.
Mezi dvojicí reálných hodnot, které by měly být zadány v neklesajícím pořadí, je pak barva dopočítána pomocí lineární (interpolační) funkce. Je dokonce dovoleno vytvořit „skok“, a to tak, že se zadá ta stejná reálná hodnota s rozdílnou barvou. Pokud se budeme k této hodnotě přibližovat zespodu (od nižších hodnot), použije se první barva (první řádek ve specifikaci), při přibližování shora pak barva druhá. Jednoduchá mapa barev může vypadat následovně. Povšimněte si skoku mezi bílou a černou barvou na hodnotě 0,4:
// mapa barev color_map { [0.0 color rgb <0.8, 0.2, 0.2>] // hodnota 0 je dolní mezí, pro kterou lze specifikovat barvu [0.2 color rgb <0.4, 0.2, 0.2>] [0.4 color White] // skok [0.4 color Black] [0.6 color rgb <0.2, 0.6, 0.6>] [1.0 color rgb <0.8, 0.2, 0.2>] // hodnota 1 je naopak horní mezí }
Alternativně je možné na jeden řádek v barvové mapě zapsat obě barvy v krajních bodech rozsahu, což je možná čitelnější (a navíc nepatrně kratší):
// mapa barev color_map { // dvě barvy, které se na vzorku střídají [0.0 0.4 color red 0.36 green 0.20 blue 0.09 color red 0.36 green 0.20 blue 0.09 ] [0.4 1.01 color red 0.858824 green 0.576471 blue 0.439216 color red 0.858824 green 0.576471 blue 0.439216] }
Obrázek 4: Trojrozměrný model vytvořený pomocí programu Lparser a vykreslený POV-Rayem.
V knihovně Vapory se mapa barev zapisuje takovým způsobem, že se do konstruktoru třídy ColorMap předá libovolné množství vektorů nebo n-tic se specifikací jednotlivých barev v prostoru 0,1 až 1,0:
ColorMap([0.0, 'color', [0.8, 0.2, 0.2]], [0.2, 'color', [0.4, 0.2, 0.2]], [0.4, 'color', 'White'], [0.4, 'color', 'Black'], [0.6, 'color', [0.2, 0.6, 0.6]], [1.0, 'color', [0.8, 0.2, 0.2]]),
Obrázek 5: Vykreslený model L-systému Fern (kapradina) složený z trojúhelníků (non-realistic rendering).
3. Testovací scéna napsaná přímo pro POV-Ray, která obsahuje torus
Podívejme se nyní na první demonstrační příklad zapsaný přímo v jazyku POV-Raye. V tomto příkladu jsou použity oba dva prvky, s nimiž jsme se seznámili v předchozích kapitolách – torus (tedy nově přidaný element, který původně knihovna Vapory nepodporovala) a barvové mapy. Aby byla situace ještě komplikovanější, jsou u toru specifikovány dvě textury, přičemž horní textura je v některých místech průsvitná, takže jí v těchto oblastech prosvítá textura spodní:
// objekt ve scéně: torus (anuloid) torus { 7.0, 4.0 // geometrické informace // (poloměry celého toroidu a "trubky") // spodní (podkladová) textura se základním vzorkem texture { pigment { // definice vzorku textury bozo // typ vzorku color_map { // dvě barvy, které se na vzorku střídají [0.0 0.4 color red 0.36 green 0.20 blue 0.09 color red 0.36 green 0.20 blue 0.09 ] [0.4 1.01 color red 0.858824 green 0.576471 blue 0.439216 color red 0.858824 green 0.576471 blue 0.439216] } scale <4, 0.15, 0.15> // změna měřítka (velikosti) namapovaného vzorku rotate 45*y } } // horní textura, která přidává jemnější vzorek texture { ... ... ... } }
Obrázek 6: Výsledek renderingu dnešní první demonstrační scény v POV-Rayi.
Úplný zdrojový kód dnešní první demonstrační scény (v celkovém pořadí však již pátém příkladu) je možné nalézt na této adrese):
// ------------------------------------------------------------ // Jednoduchá scéna s uzavřeným objektem, plochou, dvojicí světel // a jednou kamerou (pozorovatelem) // // rendering lze spustit příkazem: // povray +W800 +H600 +B +FN +D +Iscene5.pov +Oscene5.png // (pro náhled postačí zadat povray scene5.pov) // // Založeno na souboru původně vytvořeném Danem Farmerem (leden 2002) // ------------------------------------------------------------ #version 3.5; // globální nastavení parametrů scény global_settings { assumed_gamma 2.2 } // načtení všech potřebných externích souborů #include "colors.inc" // nastavení kamery (pozorovatele) camera { location <0, 20, -15> // pozice kamery look_at <0, -2, 0> // bod, na který kamera směřuje } // první (silnější) světelný zdroj s bílou barvou light_source { <-50, 100, -80> // pozice světelného zdroje color rgb 1 // barva světla (všech tří složek) } // druhý (slabší) světelný zdroj light_source { <250, 25, -100> // pozice světelného zdroje color red 0.85 green 0.53 blue 0.10 // barva světla } // objekt ve scéně: torus (anuloid) torus { 7.0, 4.0 // geometrické informace // (poloměry celého toroidu a "trubky") // spodní (podkladová) textura se základním vzorkem texture { pigment { // definice vzorku textury bozo // typ vzorku color_map { // dvě barvy, které se na vzorku střídají [0.0 0.4 color red 0.36 green 0.20 blue 0.09 color red 0.36 green 0.20 blue 0.09 ] [0.4 1.01 color red 0.858824 green 0.576471 blue 0.439216 color red 0.858824 green 0.576471 blue 0.439216] } scale <4, 0.15, 0.15> // změna měřítka (velikosti) namapovaného vzorku rotate 45*y } } // horní textura, která přidává jemnější vzorek texture { finish { // vlastnosti materiálu phong 1 // intenzita a phong_size 100 // velikost odlesků brilliance 3 // míra změny odlesků s úhlem dopadu světelných paprsků ambient 0.2 // ambientní složka (pro simulaci všesměrového světla) diffuse 0.8 // difúzní složka (pro simulaci směrového světla) } pigment { // definice vzorku textury wood // typ vzorku turbulence 0.025 color_map { // čtyři barvy, které se ve vzorku střídají [0.00 0.15 color red 0.42 green 0.26 blue 0.15 color red 0.85 green 0.53 blue 0.10 ] [0.15 0.40 color red 0.85 green 0.53 blue 0.10 color rgbf 1 ] [0.40 0.80 color rgbf 1 color red 0.85 green 0.53 blue 0.10 ] [0.80 1.01 color red 0.85 green 0.53 blue 0.10 color red 0.42 green 0.26 blue 0.15 ] } scale <3.5, 1, 1> // změna měřítka a natočení vzorku translate -50*y rotate 1.5*z } } } plane { // rovina tvořící pozadí scény <0, 1, 0>, -6 // posun a orientace roviny texture { // textura - vlastnosti povrchu pigment { // vlastní vzorek textury checker color Gray color White*0.9 } finish { // optické vlastnosti materiálu reflection 0.10 } scale 4 } } // ------------------------------------------------------------ // finito // ------------------------------------------------------------
4. Shodná testovací scéna napsaná v Pythonu s využitím Vapory a nové třídy Torus
Nyní si pojďme předchozí scénu přepsat do Vapory. Ve skutečnosti to není nic složitého, jen nesmíme na začátku zapomenout na deklaraci nové třídy Torus, která bude odvozena od třídy POVRayElement:
class Torus(POVRayElement): """Torus()"""
Úplný zdrojový kód tohoto příkladu naleznete na adrese https://github.com/tisnik/vapory-examples/blob/master/scene5.py:
#!/usr/bin/env python3 # vim: set fileencoding=utf-8 from vapory import * class Torus(POVRayElement): """Torus()""" y = [0, 1, 0] z = [0, 0, 1] camera = Camera('location', [0, 20, -15], 'look_at', [0, -2, 0]) # dva světelné zdroje # první (silnější) světelný zdroj s bílou barvou light1 = LightSource([-50, 100, -80], 'color', [1.0, 1.0, 1.0]) # druhý (slabší) světelný zdroj light2 = LightSource([250, 25, -100], 'color', [0.85, 0.53, 0.10]) # objekt ve scéně: torus (anuloid) torus = Torus(7.0, 4.0, # spodní (podkladová) textura se základním vzorkem Texture( Pigment('bozo', ColorMap([0.0, 0.4, 'color', [0.36, 0.20, 0.09], 'color', [0.36, 0.20, 0.09]], [0.4, 1.01, 'color', [0.85, 0.57, 0.44], 'color', [0.85, 0.57, 0.44]]), 'scale', [4, 0.15, 0.15], 'rotate', '45*y')), # horní textura, která přidává jemnější vzorek Texture( Pigment('wood', 'turbulence', 0.025, # čtyři barvy, které se ve vzorku střídají ColorMap([0.00, 0.15, 'color', [0.42, 0.26, 0.15], 'color', [0.85, 0.53, 0.10]], [0.15, 0.40, 'color', [0.85, 0.53, 0.10], 'color', [1.00, 1.00, 1.00, 1.00]], [0.40, 0.80, 'color', [1.00, 1.00, 1.00, 1.00], 'color', [0.85, 0.53, 0.10]], [0.80, 1.01, 'color', [0.85, 0.53, 0.10], 'color', [0.42, 0.26, 0.15]]), 'scale', [3.5, 1, 1], 'translate', [0, -50, 0], 'rotate', [0, 0, 1.5] ), Finish('phong', 1, 'phong_size', 100, 'brilliance', 3, 'ambient', 0.2, 'diffuse', 0.8))) # druhý objekt - nekonečná rovina plane = Plane(y, -6, Texture( Pigment('checker', 'color', 'Gray', 'color', 'White*0.9'), Finish('reflection', 0.10), 'scale', 4)) scene = Scene(camera, objects=[light1, light2, plane, torus], included=['colors.inc'], global_settings=['assumed_gamma 2.2']) scene.render('scene5_vapory.png', width=400, height=300)
5. Zjednodušený zápis mapy barev – scéna popsaná v jazyku POV-Ray
V předchozím příkladu se pro deklaraci barvové mapy používal tento zápis, v němž se v každém vektoru specifikovaly dvě barvy pro dvě hodnoty z intervalu 0,0 až 1,0:
color_map { // dvě barvy, které se na vzorku střídají [0.0 0.4 color red 0.36 green 0.20 blue 0.09 color red 0.36 green 0.20 blue 0.09 ] [0.4 1.01 color red 0.858824 green 0.576471 blue 0.439216 color red 0.858824 green 0.576471 blue 0.439216] }
Samozřejmě můžeme použít i alternativní (delší) zápis, který by však měl vést k totožnému obrázku:
color_map { // dvě barvy, které se na vzorku střídají [0.0 color red 0.36 green 0.20 blue 0.09] [0.4 color red 0.36 green 0.20 blue 0.09] [0.4 color red 0.858824 green 0.576471 blue 0.439216] [1.01 color red 0.858824 green 0.576471 blue 0.439216] }
Obrázek 7: Tento obrázek je (v každém pixelu) totožný s obrázkem číslo 6, i když barvová mapa byla deklarována odlišným způsobem.
Pro jistotu se podívejme na úplný zdrojový kód upravené scény, který je možné nalézt na této adrese):
// ------------------------------------------------------------ // Jednoduchá scéna s uzavřeným objektem, plochou, dvojicí světel // a jednou kamerou (pozorovatelem) // // rendering lze spustit příkazem: // povray +W800 +H600 +B +FN +D +Iscene6.pov +Oscene6.png // (pro náhled postačí zadat povray scene6.pov) // // Založeno na souboru původně vytvořeném Danem Farmerem (leden 2002) // ------------------------------------------------------------ #version 3.5; // globální nastavení parametrů scény global_settings { assumed_gamma 2.2 } // načtení všech potřebných externích souborů #include "colors.inc" // nastavení kamery (pozorovatele) camera { location <0, 20, -15> // pozice kamery look_at <0, -2, 0> // bod, na který kamera směřuje } // první (silnější) světelný zdroj s bílou barvou light_source { <-50, 100, -80> // pozice světelného zdroje color rgb 1 // barva světla (všech tří složek) } // druhý (slabší) světelný zdroj light_source { <250, 25, -100> // pozice světelného zdroje color red 0.85 green 0.53 blue 0.10 // barva světla } // objekt ve scéně: torus (anuloid) torus { 7.0, 4.0 // geometrické informace // (poloměry celého toroidu a "trubky") // spodní (podkladová) textura se základním vzorkem texture { pigment { // definice vzorku textury bozo // typ vzorku color_map { // dvě barvy, které se na vzorku střídají [0.0 color red 0.36 green 0.20 blue 0.09] [0.4 color red 0.36 green 0.20 blue 0.09] [0.4 color red 0.858824 green 0.576471 blue 0.439216] [1.01 color red 0.858824 green 0.576471 blue 0.439216] } scale <4, 0.15, 0.15> // změna měřítka (velikosti) namapovaného vzorku rotate 45*y } } // horní textura, která přidává jemnější vzorek texture { finish { // vlastnosti materiálu phong 1 // intenzita a phong_size 100 // velikost odlesků brilliance 3 // míra změny odlesků s úhlem dopadu světelných paprsků ambient 0.2 // ambientní složka (pro simulaci všesměrového světla) diffuse 0.8 // difúzní složka (pro simulaci směrového světla) } pigment { // definice vzorku textury wood // typ vzorku turbulence 0.025 color_map { // čtyři barvy, které se ve vzorku střídají [0.00 color red 0.42 green 0.26 blue 0.15] [0.15 color red 0.85 green 0.53 blue 0.10] [0.40 color rgbf 1] [0.80 color red 0.85 green 0.53 blue 0.10] [1.01 color red 0.42 green 0.26 blue 0.15] } scale <3.5, 1, 1> // změna měřítka a natočení vzorku translate -50*y rotate 1.5*z } } } plane { // rovina tvořící pozadí scény <0, 1, 0>, -6 // posun a orientace roviny texture { // textura - vlastnosti povrchu pigment { // vlastní vzorek textury checker color Gray color White*0.9 } finish { // optické vlastnosti materiálu reflection 0.10 } scale 4 } } // ------------------------------------------------------------ // finito // ------------------------------------------------------------
6. Zjednodušený zápis mapy barev – scéna popsaná v jazyku Python s využitím Vapory
Přepis předchozího příkladu do Pythonu s použitím knihovny Vapory dopadne následovně:
#!/usr/bin/env python3 # vim: set fileencoding=utf-8 from vapory import * class Torus(POVRayElement): """Torus()""" y = [0, 1, 0] z = [0, 0, 1] camera = Camera('location', [0, 20, -15], 'look_at', [0, -2, 0]) # dva světelné zdroje # první (silnější) světelný zdroj s bílou barvou light1 = LightSource([-50, 100, -80], 'color', [1.0, 1.0, 1.0]) # druhý (slabší) světelný zdroj light2 = LightSource([250, 25, -100], 'color', [0.85, 0.53, 0.10]) # objekt ve scéně: torus (anuloid) torus = Torus(7.0, 4.0, # spodní (podkladová) textura se základním vzorkem Texture( Pigment('bozo', ColorMap([0.00, 'color', [0.36, 0.20, 0.09]], [0.40, 'color', [0.36, 0.20, 0.09]], [0.40, 'color', [0.85, 0.57, 0.44]], [1.01, 'color', [0.85, 0.57, 0.44]]), 'scale', [4, 0.15, 0.15], 'rotate', '45*y')), # horní textura, která přidává jemnější vzorek Texture( Pigment('wood', 'turbulence', 0.025, # čtyři barvy, které se ve vzorku střídají ColorMap([0.00, 'color', [0.42, 0.26, 0.15]], [0.15, 'color', [0.85, 0.53, 0.10]], [0.40, 'color', [1.00, 1.00, 1.00, 1.00]], [0.80, 'color', [0.85, 0.53, 0.10]], [1.01, 'color', [0.42, 0.26, 0.15]]), 'scale', [3.5, 1, 1], 'translate', [0, -50, 0], 'rotate', [0, 0, 1.5] ), Finish('phong', 1, 'phong_size', 100, 'brilliance', 3, 'ambient', 0.2, 'diffuse', 0.8))) # druhý objekt - nekonečná rovina plane = Plane(y, -6, Texture( Pigment('checker', 'color', 'Gray', 'color', 'White*0.9'), Finish('reflection', 0.10), 'scale', 4)) scene = Scene(camera, objects=[light1, light2, plane, torus], included=['colors.inc'], global_settings=['assumed_gamma 2.2']) scene.render('scene6_vapory.png', width=400, height=300)
7. Využití implicitních ploch (blobs, metaballs) v POV-Rayi
Raytracer POV-Ray podporuje, podobně jako některé další modelovací a renderovací nástroje (například Blender), modelování složitých těles s využitím takzvaných implicitních ploch. Jedná se o velmi zajímavou techniku, která byla převzata z jiného (dnes již nevyvíjeného) raytraceru nazvaného Polyray Alexandra Enzmanna, ve kterém byly implicitní plochy použity v mnohem obecnějším pojetí než v POV-Rayi. Při popisu tělesa s využitím implicitních ploch se těleso nepopisuje hranicí (jak je tomu například u klasických polygonálních či parametrických reprezentací) ani vyčíslením obsazeného objemu v prostoru, ale množinou implicitních funkcí spolu se specifikací jejich vzájemné kombinace.
Obrázek 8: Implicitní plocha vytvořená pomocí tří prvků kostry.
Jen ve stručnosti si řekněme teorii, na níž je vykreslování implicitních ploch založeno. Základem je implicitní funkce, která je určena rovnicí, která každému bodu v prostoru přiřazuje určitou hodnotu. Tuto hodnotu můžeme považovat například za hustotu tělesa či intenzitu síly v daném místě prostoru. V rovnici popisující implicitní plochu vystupují implicitní funkce, jejichž hodnota je závislá na poloze bodu (v Euklidovském trojrozměrném prostoru E3 jde o běžně používané souřadnice x, y a z). Vlastní vykreslení spočívá ve volbě hranice (threshold) zmíněné hustoty/síly, pomocí které je určena izoplocha (isosurface), která je s využitím analytických či numerických metod vykreslena. Díky funkci „hustoty“ je navíc zřejmé, která část prostoru leží uvnitř tělesa a která vně.
Obrázek 9: Model vytvořený především z (jediné) implicitní plochy v dnes již historickém modelovacím programu. Vytvoření modelu bylo otázkou doslova několika minut, protože modelovací možnosti implicitních ploch jsou poměrně vysoké.
8. Rozšíření možností CSG pomocí implicitních ploch
Implicitní plochy lze považovat také za určitou alternativu či rozšíření ke konstruktivní geometrii těles (CSG), o které jsme se zmínili již minule. Z tohoto důvodu se také modelování s využitím implicitních ploch někdy označuje jako Implicit Solid Modelling neboli ISM. Výhodou ISM oproti CSG je poměrně jednoduchý výpočet celkového tělesa, které vzniká sloučením základních primitiv. Zatímco v CSG reprezentaci se musí (často složitě traverzací n-árního stromu) počítat průsečíky jednotlivých základních těles, v ISM reprezentaci se pozice průsečíků nemusí vyčíslit, protože se celkový tvar tělesa vyjadřuje pomocí jednoduché směšovací funkce (blending function). Nelze tak sice dosáhnout numericky naprosto přesných výsledků, ale v mnoha praktických aplikacích je kvalita a přesnost vizuálního výsledku více než dostačující.
Obrázek 10: Složitější model, jehož ústřední část tvoří implicitní plochy.
9. Konstrukce implicitních ploch s využitím kostry
Implicitní plocha se konstruuje pomocí takzvané kostry (skeleton). Kostra se skládá z jednodušších částí nazývaných prvky kostry, kde každý prvek odpovídá jedné implicitní funkci. Prvky kostry jsou ve své nejjednodušší podobě vyjádřeny svým středem a intenzitou, kterou působí na své okolí. Tato intenzita je největší ve středu prvku kostry a se vzrůstající vzdáleností od tohoto středu se snižuje.
Obrázek 11: Vykreslený model L-systému Airhorse složený z více objektů s bloby.
V původní definici funkce pro vyjádření intenzity byla intenzita v každém bodě prostoru kladná (a tedy nenulová), protože pro popis intenzity se používala exponenciální funkce. Pro urychlení výpočtů se zavádí jistá zjednodušení, která zaručí, že od určité vzdálenosti od středu prvku kostry bude intenzita nulová. Definici implicitní plochy lze rozšířit tak, že k prvku kostry povolíme přiřazovat zápornou intenzitu, což nám dovolí jednoduše modelovat i děravé objekty nebo objekty vzniklé odebíráním hmoty od základního tvaru (což POV-Ray umožňuje).
Obrázek 12: Ukázka vzájemného prolínání dvou bodových prvků kostry při tvorbě izoplochy. V dolní části animace jsou zobrazeny dva bodové prvky kostry spolu s poloměrem dosahu jejich sil. Při vzájemném přibližování dochází ke sčítání sil a „slévání“ obou tvarů. V horní části je zobrazen průběh sil v řezu procházejícím přes oba bodové prvky kostry se silou=0,7, přičemž je na grafu zvýrazněna prahová hodnota threshold=0,5.
Pro algoritmy pracující s implicitními plochami je důležité vyjádřit hranici tělesa. Proto je definována globální proměnná T (označení T je odvozeno od slova threshold – hranice hodnot, práh), která určuje mez, kde se nachází hranice (obálka) implicitní plochy. Je-li intenzita v určitém bodě v prostoru větší než zadaná mez, leží bod uvnitř tělesa, je-li naopak intenzita menší než mez, leží bod vně tělesa. Body, jejichž intenzita je rovna proměnné T, leží přesně na povrchu implicitní plochy – tvoří izoplochu. Pro osamocené body, které tvoří prvky kostry, nabývá obálka implicitní plochy tvar kružnice v ploše a koule v prostoru, pro úsečky má obálka tvar válce s polokulovitými základnami atd.
Obrázek 13: Ukázka vzájemného prolínání dvou bodových prvků kostry při zvýšení sil obou bodových prvků kostry na hodnotu 1,0.
10. Vykreslení implicitních ploch POV-Rayem
Jak jsme si již řekli v předchozích kapitolách, podporuje POV-Ray přímou deklaraci implicitních ploch. Pro tento účel se používá 3D entita nazvaná blob, která může obsahovat prakticky libovolné množství prvků kostry, přičemž každý prvek kostry se zapisuje slovem component, za nímž následuje specifikace poloměru, síly a umístění prvku v 3D prostoru. Nesmíme zapomenout ani na deklaraci hraniční hodnoty (threshold), která je pochopitelně pro všechny prvky kostry konstantní:
blob { threshold 0.6 // hraniční hodnota component 1.0, 1.0, < 0.750, 0, 0> // prvek kostry: síla, poloměr, souřadnice v prostoru component 1.0, 1.0, <-0.375, 0.64952, 0> // druhý prvek kostry component 1.0, 1.0, <-0.375, -0.64952, 0> // třetí prvek kostry ... ... ... }
Výsledek může po doplnění kamery, osvětlení a nezbytných textur vypadat následovně:
Obrázek 14: Výsledek tohoto demonstračního příkladu vypočtený POV-Rayem.
Opět se podívejme na úplný zdrojový kód této scény, který je možné nalézt na této adrese):
// ------------------------------------------------------------ // Jednoduchá scéna s jednou implicitní plochou vymodelovanou // pomocí tří prvků kostry. // // rendering lze spustit příkazem: // povray +W800 +H600 +B +FN +D +Iscene7.pov +Oscene7.png // (pro náhled postačí zadat povray scene7.pov) // ------------------------------------------------------------ #version 3.5; #include "colors.inc" global_settings { assumed_gamma 2.2 } camera { location <0, 0, -2.5> // umístění kamery up y // a nahoru right 4/3 * x // vektor směřující doprava look_at <0, 0, 0> // bod, na který se kamera zaměřila } light_source { // první světelný zdroj <2, 10, -10> // pozice světelného zdroje color red 0.7 green 0.7 blue 0.7 // barva světelného zdroje } light_source { // druhý světelný zdroj <0, 0, -10000> // pozice světelného zdroje color red 0.7 green 0.7 blue 0.7 // barva světelného zdroje shadowless } plane { // rovina tvořící pozadí scény <0, 0, 1>, 2 // posun a orientace roviny // hollow on pigment { // procedurální textura agate agate_turb 0.3 } finish { // optické vlastnosti materiálu povrchu ambient 0.1 diffuse 0.4 } } blob { threshold 0.6 // hraniční hodnota component 1.0, 1.0, < 0.750, 0, 0> // prvek kostry: síla, poloměr, souřadnice v prostoru component 1.0, 1.0, <-0.375, 0.64952, 0> // druhý prvek kostry component 1.0, 1.0, <-0.375, -0.64952, 0> // třetí prvek kostry texture { pigment { color red 0 green 0 blue 1 // barva materiálu } finish { // optické vlastnosti materiálu povrchu ambient 0.2 diffuse 0.4 specular 0.6 phong 0.6 phong_size 3 reflection 0 } } } // ------------------------------------------------------------ // finito // ------------------------------------------------------------
11. Vykreslení stejné scény s využitím knihovny Vapory
Předchozí scénu popsanou v jazyku POV-Raye samozřejmě můžeme přepsat do Vapory a to relativně snadno. Nejdříve musíme vytvořit novou třídu nazvanou Blob, která bude představovat jakoukoli implicitní plochu:
class Blob(POVRayElement): """Blob()"""
Následně, a to bez nutnosti jakýchkoliv dalších úprav, je již možné vytvořit novou implicitní plochu s libovolným počtem prvků kostry:
# první objekt - blob/metaball blob = Blob('threshold', 0.6, 'component', 1.0, 1.0, [0.750, 0.000, 0.000], 'component', 1.0, 1.0, [-.375, 0.64952, 0.000], 'component', 1.0, 1.0, [-.375, -0.64952, 0.000], ... ... ...
Opět se podívejme na úplný zdrojový kód tohoto demonstračního příkladu, který v případě potřeby naleznete na adrese https://github.com/tisnik/vapory-examples/blob/master/scene7.py:
#!/usr/bin/env python3 # vim: set fileencoding=utf-8 from vapory import * class Blob(POVRayElement): """Blob()""" y = [0, 1, 0] z = [0, 0, 1] camera = Camera('location', [ 0.0, 0.0, -2.5], 'up', [ 0.0, 1.0, 0.0], 'right', [ 4/3, 0.0, 0.0], 'look_at', [ 0.0, 0.0, 0.0]) # dva světelné zdroje light1 = LightSource([2, 10, -10], 'color', [0.7, 0.7, 0.7]) light2 = LightSource([0, 0, -10000], 'color', [0.7, 0.7, 0.7], 'shadowless') # první objekt - blob/metaball blob = Blob('threshold', 0.6, 'component', 1.0, 1.0, [0.750, 0.000, 0.000], 'component', 1.0, 1.0, [-.375, 0.64952, 0.000], 'component', 1.0, 1.0, [-.375, -0.64952, 0.000], Texture( Pigment('color', [0, 0, 1]), Finish('ambient', 0.2, 'diffuse', 0.4, 'specular', 0.6, 'phong', 0.6, 'phong_size', 3, 'reflection', 0))) # druhý objekt - nekonečná rovina plane = Plane(z, 2, 'hollow', 'on', Texture( Pigment('agate', 'agate_turb', 0.3), Finish('ambient', 0.1, 'diffuse', 0.4))) scene = Scene(camera, objects=[light1, light2, plane, blob], included=['colors.inc'], global_settings=['assumed_gamma 2.2']) scene.render('scene7_vapory.png', width=400, height=300)
12. Idiomatický kód – použití n-tic namísto seznamů
Ve všech předchozích příkladech naprogramovaných v Pythonu s využitím knihovny Vapory jsme pro popis vektorů (světelných složek, souřadnic v 3D prostoru, vektorů kamery atd.) používali datovou strukturu seznam (list). Vzhledem k tomu, že se v naprosté většině případů jedná o neměnné (immutable) datové struktury, bude lepší provést přepis a namísto seznamů na všech místech použít n-tice (tuple). Upravený příklad bude vypadat následovně (jeho funkcionalita se v tomto případě nijak nezmění):
#!/usr/bin/env python3 # vim: set fileencoding=utf-8 from vapory import * class Blob(POVRayElement): """Blob()""" y = (0, 1, 0) z = (0, 0, 1) camera = Camera('location', ( 0.0, 0.0, -2.5), 'up', ( 0.0, 1.0, 0.0), 'right', ( 4/3, 0.0, 0.0), 'look_at', ( 0.0, 0.0, 0.0)) # dva světelné zdroje light1 = LightSource((2, 10, -10), 'color', (0.7, 0.7, 0.7)) light2 = LightSource((0, 0, -10000), 'color', (0.7, 0.7, 0.7), 'shadowless') # první objekt - blob/metaball blob = Blob('threshold', 0.6, 'component', 1.0, 1.0, (0.750, 0.000, 0.000), 'component', 1.0, 1.0, (-.375, 0.64952, 0.000), 'component', 1.0, 1.0, (-.375, -0.64952, 0.000), Texture( Pigment('color', (0, 0, 1)), Finish('ambient', 0.2, 'diffuse', 0.4, 'specular', 0.6, 'phong', 0.6, 'phong_size', 3, 'reflection', 0))) # druhý objekt - nekonečná rovina plane = Plane(z, 2, 'hollow', 'on', Texture( Pigment('agate', 'agate_turb', 0.3), Finish('ambient', 0.1, 'diffuse', 0.4))) scene = Scene(camera, objects=(light1, light2, plane, blob), included=('colors.inc',), global_settings=('assumed_gamma 2.2',)) scene.render('scene7_vapory_B.png', width=400, height=300)
13. Využití možností knihovny MoviePy pro jednodušší vytvoření animace
Poslední zajímavou vlastností projektu Vapory je podpora pro přímé vytváření video souborů. Již minule jsme si ukázali, že nám pochopitelně nic nebrání vytvářet (renderovat) jednotlivé snímky, ty postupně ukládat do formátu PNG či TGA a následně z těchto snímků vytvořit výsledné video například s využitím externího nástroje ffmpeg. Připomeňme si, že celý postup vypadal zhruba takto – scénu nově vytváříme pro každý snímek (frame) a ukládáme ji do očíslovaného souboru, aby ffmpeg dovedl jednotlivé snímky korektně zařadit:
def construct_scene(t): ... ... příprava objektů pro scénu ... return Scene(camera, objects=[light1, light2, light3, csg_object, plane], included=["colors.inc", "stones.inc", "woods.inc"], global_settings=["assumed_gamma 2.2"]) FRAMES = 100 # vykreslení všech snímků for frame in range(0, FRAMES): t = frame / float(FRAMES) scene = construct_scene(t) filename = "frame_{:03d}.png".format(frame) scene.render(filename, width=400, height=300)
Ve skutečnosti však máme ještě jednu alternativu, a to přímo zkombinovat možnosti projektu Vapory s knihovnou MoviePy. Nejprve musíme provést korektní import všech potřebných modulů:
from vapory import * from moviepy.editor import VideoClip
Následně budeme postupovat naprosto stejným způsobem, jako při tvorbě jakéhokoli jiného videa v MoviePy – připravíme si callback funkci nazvanou make_frame, která bude automaticky volána při renderingu videa a bude se jí předávat čas snímku (reálné číslo):
# parametry animace DURATION = 10 FPS = 20 def make_frame(t): scene = construct_scene(t / DURATION) return scene.render(width=400, height=300, antialiasing=0.001) animation = VideoClip(make_frame, duration=DURATION) animation.write_videofile('scene4.ogv', fps=FPS, progress_bar=True, bitrate="800000")
Povšimněte si, že funkce make_frame vrací objekt představující snímek PO vykreslení. Interně se jedná o datovou strukturu typu ndarray, a to z toho důvodu, že jsme metodě Scene.render() nepředali jméno výstupního souboru. A jak již víme z úvodního článku, zpracovává MoviePy snímky reprezentované právě polem typu ndarray:
class Scene: """ A scene contains Items and can be written to a file. def render(self, outfile=None, height=None, width=None, quality=None, antialiasing=None, remove_temp=True, auto_camera_angle = True ): """ Renders the scene to a PNG, a numpy array, or the IPython Notebook. Parameters ------------ outfile Name of the output: - "myfile.png" to output a PNG file - None to output a numpy array (if numpy is installed). - 'ipython' (and call this function last in an IPython Notebook) height height in pixels width width in pixels """
14. Úprava příkladu z předchozího článku: animace změny CSG tělesa a textury na toru
Minule jsme si ukazovali jednoduchou animaci založenou na postupné změně pozice těles, která se vzájemně kombinují s využitím CSG (Constructive Solid Geometry). Animace byla vytvořena z jednotlivých snímků, které musely být nejdříve uloženy na disk a teprve poté se spustil ffmpeg, aby je zpracoval a vytvořil z nich soubor s videem (použití klasické pipeline v tomto případě není možné, protože snímky do kodeku obecně vstupují v náhodném pořadí). Pokud ovšem zkombinujeme možnosti nabízené knihovnami Vapory a MoviePy, celý kód s deklarací scény se do jisté míry zjednoduší.
Výsledné video:
https://tisnik.github.io/moviepy-videos/video11.htm
Zdrojový kód upraveného příkladu vypadá následovně:
#!/usr/bin/env python3 # vim: set fileencoding=utf-8 from vapory import * from moviepy.editor import VideoClip y = [0, 1, 0] z = [0, 0, 1] camera = Camera('location', [1.65, 5.5,-5.0], 'up', [ 0.0, 1.0, 0.0], 'right', [ 4/3, 0.0, 0.0], 'look_at', [ 0.0, 0.5,-1.0]) # tři světelné zdroje light1 = LightSource([-30, 11, 20], 'color', 'White') light2 = LightSource([31, 12, -20], 'color', 'White') light3 = LightSource([32, 11, -20], 'color', 'LightGray') VEL = 1.45 # velikost krychle box = Box([-VEL, -VEL, -VEL], [VEL, VEL, VEL], Texture( 'T_Wood23', Finish('phong', 1, 'phong_size', 300, 'reflection', 0.15))) # druhý objekt - nekonečná rovina plane = Plane(y, -1.5, Texture( 'T_Stone1', Pigment('octaves', 3, 'rotate', [i * 90 for i in z]), Finish('reflection', 0.10))) def construct_scene(t): sphere = Sphere([0, 3.3 - 7.0*t, 0], 1.8, Texture( 'T_Wood24', Finish('phong', 1, 'phong_size', 300, 'reflection', 0.15))) csg_object = Difference(box, sphere) return Scene(camera, objects=[light1, light2, light3, csg_object, plane], included=["colors.inc", "stones.inc", "woods.inc"], global_settings=["assumed_gamma 2.2"]) # parametry animace DURATION = 10 FPS = 20 def make_frame(t): scene = construct_scene(t / DURATION) return scene.render(width=400, height=300, antialiasing=0.001) animation = VideoClip(make_frame, duration=DURATION) animation.write_videofile('scene4.ogv', fps=FPS, progress_bar=True, bitrate="800000")
Další video…
https://tisnik.github.io/moviepy-videos/video12.htm
vzniklo z této scény:
#!/usr/bin/env python3 # vim: set fileencoding=utf-8 from vapory import * from moviepy.editor import VideoClip class Torus(POVRayElement): """Torus()""" y = [0, 1, 0] z = [0, 0, 1] camera = Camera('location', [0, 20, -15], 'look_at', [0, -2, 0]) # dva světelné zdroje # první (silnější) světelný zdroj s bílou barvou light1 = LightSource([-50, 100, -80], 'color', [1.0, 1.0, 1.0]) # druhý (slabší) světelný zdroj light2 = LightSource([250, 25, -100], 'color', [0.85, 0.53, 0.10]) # objekt - nekonečná rovina plane = Plane(y, -6, Texture( Pigment('checker', 'color', 'Gray', 'color', 'White*0.9'), Finish('reflection', 0.10), 'scale', 4)) # objekt ve scéně: torus (anuloid) def create_torus(t): return Torus(7.0, 4.0, # spodní (podkladová) textura se základním vzorkem Texture( Pigment('bozo', ColorMap([0.0, 0.4, 'color', [0.36, 0.20, 0.09], 'color', [0.36, 0.20, 0.09]], [0.4, 1.01, 'color', [0.85, 0.57, 0.44], 'color', [0.85, 0.57, 0.44]]), 'scale', [4, 0.15, 0.15], 'rotate', [0, 45, 0])), # horní textura, která přidává jemnější vzorek Texture( Pigment('wood', 'turbulence', 0.025, # čtyři barvy, které se ve vzorku střídají ColorMap([0.00, 0.15, 'color', [0.42, 0.26, 0.15], 'color', [0.85, 0.53, 0.10]], [0.15, 0.40, 'color', [0.85, 0.53, 0.10], 'color', [1.00, 1.00, 1.00, 1.00]], [0.40, 0.80, 'color', [1.00, 1.00, 1.00, 1.00], 'color', [0.85, 0.53, 0.10]], [0.80, 1.01, 'color', [0.85, 0.53, 0.10], 'color', [0.42, 0.26, 0.15]]), 'scale', [3.5, 1, 1], 'translate', [0, -50, 0], 'rotate', [0, 0, 10.0 * t] ), Finish('phong', 1, 'phong_size', 100, 'brilliance', 3, 'ambient', 0.2, 'diffuse', 0.8))) def construct_scene(t): torus = create_torus(t) return Scene(camera, objects=[light1, light2, plane, torus], included=['colors.inc'], global_settings=['assumed_gamma 2.2']) # parametry animace DURATION = 10 FPS = 20 def make_frame(t): scene = construct_scene(t / DURATION) return scene.render(width=400, height=300, antialiasing=0.001) animation = VideoClip(make_frame, duration=DURATION) animation.write_videofile('scene5.ogv', fps=FPS, progress_bar=True, bitrate="800000")
15. Příprava na animaci změn parametrů implicitních ploch
Nyní když víme, jakým způsobem lze velmi snadno generovat video soubory přímo z Vapory, si můžeme ukázat další příklad, v němž se budou postupně měnit parametry výše popsaných implicitních ploch (blobs, metaballs).
Výsledkem by mělo být toto video:
https://tisnik.github.io/moviepy-videos/video13.htm
Nejprve si ukažme statickou podobu této scény:
// ------------------------------------------------------------ // Jednoduchá scéna s několika implicitními plochami (blobs), // které se liší pouze hodnotou threshold. // // rendering lze spustit příkazem: // povray +W800 +H600 +B100 +FN +D +Iscene8.pov +Oscene8.png // (pro náhled postačí zadat povray scene8.pov) // ------------------------------------------------------------ #version 3.5; #include "colors.inc" global_settings { assumed_gamma 2.2 } camera { orthographic // vypnutí perspektivy location <0, 0, -1> // umístění kamery right 5*4/3 * x // vektor směřující doprava up y*5 // a nahoru look_at <0, 0, 0> // bod, na který se kamera zaměřila } light_source { // první světelný zdroj <2, 10, -10> // pozice světelného zdroje color red 0.7 green 0.7 blue 0.7 // barva světelného zdroje } light_source { // druhý světelný zdroj <0, 0, -10000> // pozice světelného zdroje color red 0.7 green 0.7 blue 0.7 // barva světelného zdroje shadowless } plane { // rovina tvořící pozadí scény <0, 0, 1>, 2 // posun a orientace roviny hollow on pigment { // procedurální textura agate agate_turb 0.9 } finish { // optické vlastnosti materiálu povrchu ambient 0.1 diffuse 0.4 } } #declare Tex = texture { pigment { color red 0.6 green 0.8 blue 1 } finish { ambient 0.2 diffuse 0.4 phong 0.5 phong_size 5 } } blob { threshold 0.4 // hraniční hodnota component 1.0, 1.0, < 0, -0.6, 0> // prvek kostry: síla, poloměr, souřadnice v prostoru component 1.0, 1.0, < 0, 0.6, 0> // druhý prvek kostry texture {Tex} translate <-2.0, -1.2, 0> } blob { threshold 0.5 // hraniční hodnota component 1.0, 1.0, < 0, -0.6, 0> // prvek kostry: síla, poloměr, souřadnice v prostoru component 1.0, 1.0, < 0, 0.6, 0> // druhý prvek kostry texture {Tex} translate <-0.65, -1.2, 0> } blob { threshold 0.6 // hraniční hodnota component 1.0, 1.0, < 0, -0.6, 0> // prvek kostry: síla, poloměr, souřadnice v prostoru component 1.0, 1.0, < 0, 0.6, 0> // druhý prvek kostry texture {Tex} translate < 0.65, -1.2, 0> } blob { threshold 0.7 // hraniční hodnota component 1.0, 1.0, < 0, -0.6, 0> // prvek kostry: síla, poloměr, souřadnice v prostoru component 1.0, 1.0, < 0, 0.6, 0> // druhý prvek kostry texture {Tex} translate < 2.0, -1.2, 0> } blob { threshold 0.75 // hraniční hodnota component 1.0, 1.0, < 0, -0.6, 0> // prvek kostry: síla, poloměr, souřadnice v prostoru component 1.0, 1.0, < 0, 0.6, 0> // druhý prvek kostry texture {Tex} translate <-2.0, 1.2, 0> } blob { threshold 0.8 // hraniční hodnota component 1.0, 1.0, < 0, -0.6, 0> // prvek kostry: síla, poloměr, souřadnice v prostoru component 1.0, 1.0, < 0, 0.6, 0> // druhý prvek kostry texture {Tex} translate <-0.65, 1.2, 0> } blob { threshold 0.82 // hraniční hodnota component 1.0, 1.0, < 0, -0.6, 0> // prvek kostry: síla, poloměr, souřadnice v prostoru component 1.0, 1.0, < 0, 0.6, 0> // druhý prvek kostry texture {Tex} translate < 0.65, 1.2, 0> } blob { threshold 0.9 // hraniční hodnota component 1.0, 1.0, < 0, -0.6, 0> // prvek kostry: síla, poloměr, souřadnice v prostoru component 1.0, 1.0, < 0, 0.6, 0> // druhý prvek kostry texture {Tex} translate < 2.0, 1.2, 0> } // ------------------------------------------------------------ // finito // ------------------------------------------------------------
Obrázek 15: Výsledek renderingu předchozího příkladu.
16. Shodná testovací scéna napsaná v Pythonu s využitím Vapory
Přepis do Vapory je snadný a nečekají zde na nás žádné problémy. Jen si dovolím poukázat na imperativní způsob tvorby metaballs, který je kratší a vlastně i obecnější, než ruční specifikace jednotlivých prvků kostry tak, jak jsme to viděli v případě zdrojového kódu napsaného přímo v jazyku POV-Raye:
#!/usr/bin/env python3 # vim: set fileencoding=utf-8 from vapory import * class Blob(POVRayElement): """Blob()""" y = (0, 1, 0) z = (0, 0, 1) camera = Camera('orthographic', 'location', (0, 0, -1), 'right', '5*4/3*x', 'up', 'y*5', 'look_at', (0, 0, 0)) # dva světelné zdroje light1 = LightSource((2, 10, -10), 'color', (0.7, 0.7, 0.7)) light2 = LightSource((0, 0, -10000), 'color', (0.7, 0.7, 0.7), 'shadowless') # objekt - nekonečná rovina plane = Plane(z, 2, 'hollow', 'on', Texture( Pigment('agate', 'agate_turb', 0.9), Finish('ambient', 0.1, 'diffuse', 0.4))) def new_blob(threshold, dx, dy): return Blob('threshold', threshold, 'component', 1.0, 1.0, (0, -0.6, 0), 'component', 1.0, 1.0, (0, 0.6, 0), Texture( Pigment('color', (0.6, 0.8, 1.0)), Finish('ambient', 0.2, 'diffuse', 0.4, 'phong', 0.5, 'phong_size', 5)), 'translate', (dx, dy, 0)) objects=[light1, light2, plane] objects.append(new_blob(0.40, -2.00, -1.2)) objects.append(new_blob(0.50, -0.65, -1.2)) objects.append(new_blob(0.60, 0.65, -1.2)) objects.append(new_blob(0.70, 2.00, -1.2)) objects.append(new_blob(0.75, -2.00, 1.2)) objects.append(new_blob(0.80, -0.65, 1.2)) objects.append(new_blob(0.82, 0.65, 1.2)) objects.append(new_blob(0.90, 2.00, 1.2)) scene = Scene(camera, objects=objects, included=('colors.inc',), global_settings=('assumed_gamma 2.2',)) scene.render('scene8_vapory.png', width=400, height=300, auto_camera_angle=False)
17. Naprogramování animace – tvorba jednotlivých snímků později spojených do videa
Celý postup pro vytvoření animace z jednotlivých snímků již známe, takže jen krátce:
#!/usr/bin/env python3 # vim: set fileencoding=utf-8 from vapory import * from math import * class Blob(POVRayElement): """Blob()""" y = (0, 1, 0) z = (0, 0, 1) camera = Camera('orthographic', 'location', (0, 0, -1), 'right', '5*4/3*x', 'up', 'y*5', 'look_at', (0, 0, 0)) # dva světelné zdroje light1 = LightSource((2, 10, -10), 'color', (0.7, 0.7, 0.7)) light2 = LightSource((0, 0, -10000), 'color', (0.7, 0.7, 0.7), 'shadowless') # objekt - nekonečná rovina plane = Plane(z, 2, 'hollow', 'on', Texture( Pigment('agate', 'agate_turb', 0.9), Finish('ambient', 0.1, 'diffuse', 0.4))) def new_blob(threshold, dx, dy, t): delta = 0.3 * sin(2*pi*t) return Blob('threshold', threshold, 'component', 1.0, 1.0, (0, -0.5 - delta, 0), 'component', 1.0, 1.0, (0, 0.5 + delta, 0), Texture( Pigment('color', (0.6, 0.8, 1.0)), Finish('ambient', 0.2, 'diffuse', 0.4, 'phong', 0.5, 'phong_size', 5)), 'translate', (dx, dy, 0)) def construct_scene(t): objects = [light1, light2, plane] objects.append(new_blob(0.40, -2.00, -1.2, t)) objects.append(new_blob(0.50, -0.65, -1.2, t)) objects.append(new_blob(0.60, 0.65, -1.2, t)) objects.append(new_blob(0.70, 2.00, -1.2, t)) objects.append(new_blob(0.75, -2.00, 1.2, t)) objects.append(new_blob(0.80, -0.65, 1.2, t)) objects.append(new_blob(0.82, 0.65, 1.2, t)) objects.append(new_blob(0.90, 2.00, 1.2, t)) return Scene(camera, objects=objects, included=('colors.inc',), global_settings=('assumed_gamma 2.2',)) FRAMES = 200 for frame in range(0, FRAMES): t = frame / float(FRAMES) scene = construct_scene(t) filename = "frame_{:03d}.png".format(frame) scene.render(filename, width=400, height=300, auto_camera_angle=False)
18. Naprogramování animace – použití MoviePy
A na samotný závěr vytvoření videa s využitím knihovny MoviePy:
#!/usr/bin/env python3 # vim: set fileencoding=utf-8 from vapory import * from moviepy.editor import VideoClip from math import * class Blob(POVRayElement): """Blob()""" y = (0, 1, 0) z = (0, 0, 1) camera = Camera('orthographic', 'location', (0, 0, -1), 'right', '5*4/3*x', 'up', 'y*5', 'look_at', (0, 0, 0)) # dva světelné zdroje light1 = LightSource((2, 10, -10), 'color', (0.7, 0.7, 0.7)) light2 = LightSource((0, 0, -10000), 'color', (0.7, 0.7, 0.7), 'shadowless') # objekt - nekonečná rovina plane = Plane(z, 2, 'hollow', 'on', Texture( Pigment('agate', 'agate_turb', 0.9), Finish('ambient', 0.1, 'diffuse', 0.4))) def new_blob(threshold, dx, dy, t): delta = 0.3 * sin(2*pi*t) return Blob('threshold', threshold, 'component', 1.0, 1.0, (0, -0.5 - delta, 0), 'component', 1.0, 1.0, (0, 0.5 + delta, 0), Texture( Pigment('color', (0.6, 0.8, 1.0)), Finish('ambient', 0.2, 'diffuse', 0.4, 'phong', 0.5, 'phong_size', 5)), 'translate', (dx, dy, 0)) def construct_scene(t): objects = [light1, light2, plane] objects.append(new_blob(0.40, -2.00, -1.2, t)) objects.append(new_blob(0.50, -0.65, -1.2, t)) objects.append(new_blob(0.60, 0.65, -1.2, t)) objects.append(new_blob(0.70, 2.00, -1.2, t)) objects.append(new_blob(0.75, -2.00, 1.2, t)) objects.append(new_blob(0.80, -0.65, 1.2, t)) objects.append(new_blob(0.82, 0.65, 1.2, t)) objects.append(new_blob(0.90, 2.00, 1.2, t)) return Scene(camera, objects=objects, included=('colors.inc',), global_settings=('assumed_gamma 2.2',)) # parametry pro animaci DURATION = 15 FPS = 20 def make_frame(t): scene = construct_scene(t / DURATION) return scene.render(width=400, height=300, antialiasing=0.001, auto_camera_angle=False) animation = VideoClip(make_frame, duration=DURATION) animation.write_videofile('scene8.ogv', fps=FPS, progress_bar=True, bitrate="800000")
18. Repositář s demonstračními příklady
Zdrojové kódy všech dnes popsaných demonstračních příkladů určených pro Python 3 byly uloženy do Git repositáře dostupného na adrese https://github.com/tisnik/vapory-examples (stále na GitHubu :-). V případě, že nebudete chtít klonovat celý repositář (ten je ovšem stále velmi malý, stále doslova několik kilobajtů), můžete namísto toho použít odkazy na jednotlivé příklady, které naleznete v následující tabulce:
Demonstrační příklad | Cesta |
---|---|
scene4_moviepy.py | https://github.com/tisnik/vapory-examples/blob/master/scene4_moviepy.py |
scene5.py | https://github.com/tisnik/vapory-examples/blob/master/scene5.py |
scene5_moviepy.py | https://github.com/tisnik/vapory-examples/blob/master/scene5_moviepy.py |
scene6.py | https://github.com/tisnik/vapory-examples/blob/master/scene6.py |
scene7.py | https://github.com/tisnik/vapory-examples/blob/master/scene7.py |
scene7_update.py | https://github.com/tisnik/vapory-examples/blob/master/scene7_update.py |
scene8.py | https://github.com/tisnik/vapory-examples/blob/master/scene8.py |
scene8_anim.py | https://github.com/tisnik/vapory-examples/blob/master/scene8_anim.py |
scene8_moviepy.py | https://github.com/tisnik/vapory-examples/blob/master/scene8_moviepy.py |
Následuje tabulka s projekty vytvořenými přímo v jazyku POV-Ray (pro jejich vykreslení tedy postačuje pouze instalace POV-Raye):
Demonstrační příklad | Cesta |
---|---|
scene5.pov | https://github.com/tisnik/vapory-examples/blob/master/scene5.pov |
scene6.pov | https://github.com/tisnik/vapory-examples/blob/master/scene6.pov |
scene7.pov | https://github.com/tisnik/vapory-examples/blob/master/scene7.pov |
scene8.pov | https://github.com/tisnik/vapory-examples/blob/master/scene8.pov |
scene9.pov | https://github.com/tisnik/vapory-examples/blob/master/scene9.pov |
Výsledná videa:
- https://tisnik.github.io/moviepy-videos/video11.htm
- https://tisnik.github.io/moviepy-videos/video12.htm
- https://tisnik.github.io/moviepy-videos/video13.htm
20. Odkazy na Internetu
- MoviePy 0.2.3.3 na PyPi
https://pypi.org/project/moviepy/ - MoviePy na GitHubu
https://github.com/Zulko/moviepy - MoviePy – dokumentace
http://zulko.github.io/moviepy/ - MoviePy – galerie
http://zulko.github.io/moviepy/gallery.html - Data Animations With Python and MoviePy
https://zulko.github.io/blog/2014/11/29/data-animations-with-python-and-moviepy/ - Porovnání formátů Ogg Theora a H.264
https://www.root.cz/zpravicky/porovnani-formatu-ogg-theora-a-h-264/ - Případ GIF
https://www.root.cz/clanky/pripad-gif/ - Pravda a mýty o GIFu
https://www.root.cz/clanky/pravda-a-myty-o-gifu/ - Anatomie grafického formátu GIF
https://www.root.cz/clanky/anatomie-grafickeho-formatu-gif/ - GIF: animace a konkurence
https://www.root.cz/clanky/gif-animace-a-konkurence/ - Two python modules : MoviePy and images2gif – part 001
http://free-tutorials.org/two-python-modules-moviepy-and-images2gif-part-001/ - images2gif
https://pypi.org/project/images2gif/ - Making GIFs from video files with Python
https://www.devbattles.com/en/sand/post-345-Making+GIFs+From+Video+Files+With+Python - GIF89a specification
https://www.w3.org/Graphics/GIF/spec-gif89a.txt - MPEG-4 Part 14
https://en.wikipedia.org/wiki/MPEG-4_Part14 - Theora video compression
https://www.theora.org/ - Theora
https://en.wikipedia.org/wiki/Theora - NumPy
http://www.numpy.org/ - numpy 1.14.2 (on PyPi)
https://pypi.org/project/numpy/ - Integrovaná vývojová prostředí ve Fedoře: praktické použití IPython Notebooku a knihovny Numpy
https://mojefedora.cz/integrovana-vyvojova-prostredi-ve-fedore-prakticke-pouziti-ipython-notebooku-a-knihovny-numpy/ - Integrovaná vývojová prostředí ve Fedoře: praktické použití IPython Notebooku a knihovny Numpy (2.část)
https://mojefedora.cz/integrovana-vyvojova-prostredi-ve-fedore-prakticke-pouziti-ipython-notebooku-a-knihovny-numpy-2-cast/ - Non-linear editing system
https://en.wikipedia.org/wiki/Non-linear_editing_system - Lorenzův atraktor
http://www.root.cz/clanky/fraktaly-v-pocitacove-grafice-iii/#k03 - Popis barvových map modulu matplotlib.cm
https://gist.github.com/endolith/2719900#id7 - Ukázky (palety) barvových map modulu matplotlib.cm
http://matplotlib.org/examples/color/colormaps_reference.html - Lorenz system
https://en.wikipedia.org/wiki/Lorenz_system - Customising contour plots in matplotlib
https://philbull.wordpress.com/2012/12/27/customising-contour-plots-in-matplotlib/ - Graphics with Matplotlib
http://kestrel.nmt.edu/~raymond/software/python_notes/paper004.html - Systémy lineárních rovnic
http://www.matematika.cz/systemy-linearnich-rovnic - NumPy Home Page
http://www.numpy.org/ - NumPy v1.10 Manual
http://docs.scipy.org/doc/numpy/index.html - NumPy (Wikipedia)
https://en.wikipedia.org/wiki/NumPy - Matplotlib Home Page
http://matplotlib.org/ - vapory na GitHubu
https://github.com/Zulko/vapory - Seriál na Rootu: Vykreslujeme 3D scény s POV-Ray
https://www.root.cz/serialy/vykreslujeme-3d-sceny-s-pov-ray/ - Animace v POV-Rayi
https://www.root.cz/clanky/animace-v-pov-rayi/ - Tvorba pokročilejších animací v POV-Rayi
https://www.root.cz/clanky/tvorba-pokrocilejsich-animaci-v-pov-rayi/ - The POV-Ray Cyclopedia:
http://www.spiritone.com/~english/cyclopedia/index.html - POV-Ray New Ring:
http://webring.povray.org/ - Animations with POV-Ray:
http://www.f-lohmueller.de/pov_tut/animate/pov_anie.htm - The POV-Ray Objects Collection:
http://objects.povworld.org/ - POV-Ray Texture Library 4.0:
http://texlib.povray.org/ - Galerie modelů vytvořených v Lparseru:
http://home.wanadoo.nl/laurens.lapre/lparser2.html - Charlie Chernohorsky :-) L-systémy ve FractIntu:
http://fractint.oblivion.cz/ - POV-Ray Hall of Fame,
http://hof.povray.org/ - matplotlib (Wikipedia)
https://en.wikipedia.org/wiki/Matplotlib - The cell magics in IPython
http://nbviewer.jupyter.org/github/ipython/ipython/blob/1.x/examples/notebooks/Cell%20Magics.ipynb - Taylorův polynom
https://algoritmy.net/article/1576/Tayloruv-polynom - Taylor series
https://en.wikipedia.org/wiki/Taylor_series - Taylor Series Approximation to Cosine
https://www.cut-the-knot.org/Curriculum/Calculus/TaylorSeries.shtml - Fourier series
https://en.wikipedia.org/wiki/Fourier_series - mpmath
http://mpmath.org/ - Gallery of mathematical functions
http://mpmath.org/gallery/ - 3D visualization of complex functions with matplotlib
http://fredrikj.net/blog/2009/08/3d-visualization-of-complex-functions-with-matplotlib/ - Animating the Lorenz System in 3D
https://jakevdp.github.io/blog/2013/02/16/animating-the-lorentz-system-in-3d/ - Lorenz example
http://docs.enthought.com/mayavi/mayavi/auto/example_lorenz.html - Color Graphs of Complex Functions
http://fs2.american.edu/lcrone/www/ComplexPlot.html - Tekno Frannansa,
http://www.zazzle.com/tekf - Internet Raytracing Competition,
http://www.irtc.org/ - POVRay Short Code Contest – Round 3,
http://local.wasp.uwa.edu.au/~pbourke/exhibition/scc3/final/ - SCC4: POVRay Short Code Contest,
http://local.wasp.uwa.edu.au/~pbourke/exhibition/scc4/final/ - SCC5: POVRay Short Code Contest #5 – The animation round!,
http://local.wasp.uwa.edu.au/~pbourke/exhibition/scc5/final.html - POV-Ray posters,
http://www.povray.org/posters/ - Parametric Constructive Solid Geometry
http://c-csg.com/