Hlavní navigace

Projekt Vapory: kombinace možností Pythonu a POV-Raye

31. 5. 2018
Doba čtení: 21 minut

Sdílet

V dnešním článku se seznámíme s projektem Vapory. Jedná se o knihovnu určenou pro Python, která umožňuje popis 3D scén pro raytracer POV-Ray přímo v Pythonu, což možnosti POV-Raye do značné míry rozšiřuje.

Obsah

1. Projekt Vapory: kombinace možností Pythonu a POV-Raye

2. Průběh zpracování celé 3D scény

3. Instalace modulu vapory i raytraceru POV-Ray

4. První testovací scéna napsaná přímo pro POV-Ray

5. Shodná testovací scéna napsaná v Pythonu s využitím Vapory

6. Dočasný soubor vygenerovaný modulem vapory

7. Druhý příklad – použití externích souborů s deklarací barev a textur

8. Varianta naprogramovaná ve Vapory

9. Vylepšení předchozího příkladu – deklarace hodnoty pro gama korekci

10. Konstruktivní geometrie těles v POV-Rayi

11. Třetí příklad – POV-Ray varianta

12. Třetí příklad – varianta používající Vapory

13. Úprava předchozího příkladu: použití samostatných objektů, na něž se aplikují CSG operace

14. Základ tvorby jednoduchých animací

15. Vygenerování série snímků ve Vapory

16. Vytvoření animace ze série snímků

17. Úplný zdrojový kód čtvrtého příkladu

18. Makefile soubor určený pro vykreslení všech scén POV-Rayem

19. Repositář s demonstračními příklady

20. Odkazy na Internetu

1. Projekt Vapory: kombinace možností Pythonu a POV-Raye

S raytracerem POV-Ray jsme se již na stránkách Rootu seznámili v poměrně podrobném seriálu. Připomeňme si ve stručnosti, že se v tomto raytraceru pro popis trojrozměrných scén (které mohou být i animované) používá vlastní jazyk, jehož syntaxe je vzdáleně podobná Céčku a od něj odvozených jazyků. Původně byl tento jazyk čistě deklarativní, ovšem s postupným vývojem schopností POV-Raye do něj začaly být přidávány konstrukce pro tvorbu programových smyček apod. Většina aplikací, které POV-Ray využívají jako svůj renderovací engine, tedy všechny 3D scény (světla, kamery, textury, tělesa atd.), nejprve musí exportovat do jazyka POV-Raye a teprve poté je možné provést vlastní vykreslení.

scena1

Obrázek 1: Implicitní plocha zapsaná obecným polynomem šestého stupně. I takové plochy je možné v POV-Rayi vykreslit bez nutnosti jejich rozdělení na trojúhelníky.

Do této kategorie nástrojů spadá i dnes popisovaný projekt Vapory, který umožňuje, aby byla celá trojrozměrná scéna popsaná přímo v jazyku Python s tím, že se překlad (transpřeklad) mezi Pythonem a POV-Rayem provede zcela automaticky a prakticky bez zásahu uživatele. Také je možné spojit možnosti Vapory s již popsaným [1] [2] [3] projektem MoviePy, což je poměrně zajímavé téma, kterému se budeme věnovat příště.

scena2

Obrázek 2: Příklad terciární struktury biomolekuly vymodelovaný v POV-Ray.

2. Průběh zpracování celé 3D scény

Průběh zpracování celé 3D scény při použití kombinace Vapory s POV-Rayem vypadá následovně:

Obrázek 3: Zpracování 3D scény při použití Vapory a POV-Raye.

V navazujících kapitolách si ukážeme, jak se Vapory může použít, a to nejenom při tvorbě statických 3D scén, ale i při programování jednodušších animací (jednodušších pouze z toho prostého důvodu, že rozsah článku je omezený).

3. Instalace modulu vapory i raytraceru POV-Ray

Nástroj Vapory je distribuován ve formě běžného balíčku pro Python, takže je dostupný i na PyPi. To mj. znamená, že instalace bude velmi jednoduchá a vystačíme si s nástrojem pip, popř. pip3. Instalaci postačuje provést pouze pro právě aktivního uživatele:

$ pip3 install --user vapory
Downloading/unpacking vapory
  Downloading Vapory-0.1.01.tar.gz
  Running setup.py (path:/tmp/pip_build_tester/vapory/setup.py) egg_info for package vapory
 
    warning: no files found matching '*.txt' under directory 'examples'
Installing collected packages: vapory
  Running setup.py install for vapory
  
    warning: no files found matching '*.txt' under directory 'examples'
Successfully installed vapory
Cleaning up...

V případě raytraceru POV-Ray máme dvě možnosti. Buď je možné tento nástroj nainstalovat přímo z balíčku připraveného pro danou distribuci Linuxu, což je nejjednodušší řešení. Druhý způsob je sice nepatrně složitější, ovšem získáte díky němu poslední verzi POV-Raye: naklonujte si repositář https://github.com/POV-Ray/povray/tree/3.7-stable (ideálně právě větev 3.7-stable, na kterou odkaz vede) a instalaci provést následující sérií kroků:

cd unix/
./prebuild.sh
cd ../
./configure COMPILED_BY="jméno"
make
make install

Zapotřebí je mít k dispozici překladač C++ a několik knihoven ve vývojářské verzi: Boost, zLib, libpng, libjpeg a libTIFF (většina těchto knihoven se používá pro ukládání výsledných obrázků). Po make install by již měl být POV-Ray připraven pro další použití, takže jen pro rychlé ověření zjistíme jeho verzi:

$ povray --version
POV-Ray 3.7.1-beta.7695039.unofficial
 
This is an unofficial version compiled by:
 Pavel Tisnovsky <ptisnovs@redhat.com>
 The POV-Ray Team is not responsible for supporting this version.
 
Copyright 1991-2018 Persistence of Vision Raytracer Pty. Ltd.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 
Built-in features:
  I/O restrictions:          enabled
  X Window display:          disabled
  Supported image formats:   gif tga iff ppm pgm hdr png jpeg tiff
  Unsupported image formats: openexr
 
Compilation settings:
  Build architecture:  x86_64-unknown-linux-gnu
  Built/Optimized for: x86_64-unknown-linux-gnu (using -march=native)
  Compiler vendor:     gnu
  Compiler version:    g++ 4.8
  Compiler flags:      -pipe -Wno-multichar -Wno-write-strings -fno-enforce-eh-specs -Wno-non-template-friend -s -O3 -ffast-math -march=native -pthread
scena4

Obrázek 4: Všimněte si především techniky vymodelování řetězu, z hlediska raytraceru se jedná o velmi složitý objekt.

4. První testovací scéna napsaná přímo pro POV-Ray

Začněme porovnáním způsobu deklarace 3D scény v POV-Rayi a ve Vapory. Jednoduchou scénu, v níž jsou umístěny dva objekty, kamera a světelný zdroj, je možné popsat následovně (úplný zdrojový kód lze nalézt zde):

// ------------------------------------------------------------
// Jednoduchá scéna s jedním uzavřeným objektem, jedním nekonečným
// objektem, jedním světlem a jednou kamerou (pozorovatelem)
//
// rendering lze spustit příkazem:
//     povray +W800 +H600 +B +FN +D +Iscena2.pov +Oscena2.png
// (pro náhled postačí zadat povray scena1.pov)
// ------------------------------------------------------------
 
#version 3.5;
 
// globální nastavení parametrů scény
global_settings {
    assumed_gamma 2.2
    max_trace_level 5
}
 
// nastavení kamery (pozorovatele)
camera {
    location  <0.0, 2.0,-3.0>        // pozice kamery
    up        <0.0, 1.0, 0.0>        // vektor směřující vzhůru
    right     <4/3, 0.0, 0.0>        // vektor směřující doprava
    look_at   <0.0, 1.0, 2.0>        // bod, na který kamera směřuje
}
 
// světelný zdroj
light_source {
    <2, 4, -3>                       // pozice světelného zdroje
    color <1,1,1>                    // barva světla
}
 
// jediný uzavřený objekt ve scéně - zelená koule
sphere {
    <0, 1, 2>,                           // souřadnice středu koule
    2.00                                 // poloměr koule
    texture {                            // textura - povrch koule
        pigment {
            color <1.0, 1.0, 0.0>  // barva povrchu
        }
        finish {                         // optické vlastnosti materiálu
            phong 1                      // velikost a síla odlesků
            phong_size 300
            reflection 0.15              // odrazivost
        }
    }
}
 
// druhý objekt - nekonečná rovina
plane {
    y,                                   // orientace roviny
    -1.5                                 // vzdálenost od počátku
    texture {                            // textura - vlastnosti povrchu
        pigment {
            color <1,1,1>                   // barva povrchu
        }
        finish {                         // optické vlastnosti materiálu
            reflection 0.10
        }
    }
}
 
 
 
// ------------------------------------------------------------
// finito
// ------------------------------------------------------------

Vykreslení (rendering) této scény se provede následujícím příkazem (předpokladem samozřejmě je, že máte POV-Ray nainstalovaný nebo přeložený ze zdrojových kódů):

povray +W640 +H480 +B +FN +D +Iscene1.pov +Oscene1.png

Samotný průběh renderingu – maximálně několik sekund – nás nemusí příliš zajímat, ale jen pro jistotu:

Persistence of Vision(tm) Ray Tracer Version 3.7.1-alpha.7695039.unofficial
 ...
 ...
 ...
==== [Parsing...] ==========================================================
----------------------------------------------------------------------------
Parser Statistics
----------------------------------------------------------------------------
Finite Objects:            1
Infinite Objects:          1
Light Sources:             1
Total:                     3
----------------------------------------------------------------------------
Parser Time
  Parse Time:       0 hours  0 minutes  0 seconds (0.001 seconds)
              using 1 thread(s) with 0.000 CPU-seconds total
  Bounding Time:    0 hours  0 minutes  0 seconds (0.000 seconds)
              using 1 thread(s) with 0.000 CPU-seconds total
----------------------------------------------------------------------------
Render Options
  Quality:  9
  Bounding boxes.......On   Bounding threshold: 3
  Antialiasing.........Off
==== [Rendering...] ========================================================
 ...
 ...
 ...
----------------------------------------------------------------------------
Render Statistics
Image Resolution 640 x 480
----------------------------------------------------------------------------
Pixels:           307200   Samples:               0   Smpls/Pxl: 0.00
Rays:             620784   Saved:              5730   Max Level: 3/5
----------------------------------------------------------------------------
Ray->Shape Intersection          Tests       Succeeded  Percentage
----------------------------------------------------------------------------
Plane                           877300          278626     31.76
Sphere                          833770          330783     39.67
Bounding Box                    877300          612405     69.81
----------------------------------------------------------------------------
Shadow Ray Tests:            300278   Succeeded:                 38040
Shadow Cache Hits:            38032
Reflected Rays:              313584
----------------------------------------------------------------------------
----------------------------------------------------------------------------
Render Time:
  Photon Time:      No photons
  Radiosity Time:   No radiosity
  Trace Time:       0 hours  0 minutes  0 seconds (0.255 seconds)
              using 4 thread(s) with 0.786 CPU-seconds total
POV-Ray finished

Důležitější je výsledek:

Obrázek 5: Výsledek renderingu 3D scény raytracerem POV-Ray.

5. Shodná testovací scéna napsaná v Pythonu s využitím Vapory

Stejnou scénu je možné v Pythonu s využitím modulu/knihovny Vapory zapsat takto. Při zápisu jsem se snažil do co největší míry dodržet strukturu zdrojového kódu POV-Raye:

from vapory import *
 
y = [0, 1, 0]
 
 
camera = Camera('location', [0.0, 2.0, -3.0],
                'up',       [0.0, 1.0, 0.0],
                'right',    [4/3, 0.0, 0.0],
                'look_at',  [0.0, 1.0, 2.0])
 
light = LightSource([2, 4, -3],
                    'color', [1, 1, 1])
 
sphere = Sphere([0, 1, 2],
                2.0,
                Texture(
                    Pigment('color', [1, 1, 0]),
                    Finish('phong', 1,
                           'phong_size', 300,
                           'reflection', 0.15)))
 
plane = Plane(y,
              -1.5,
              Texture(
                  Pigment('color', [1, 1, 1]),
                  Finish('reflection', 0.10)))
 
scene = Scene(camera,
              objects=[light, sphere, plane])
 
scene.render("scene1_vapory.png", width=640, height=480)

Povšimněte si, že všechny objekty, které tvoří 3D scénu, tj. (nekonečné) plochy, trojrozměrná tělesa, světla i kamera, jsou tvořeny instancemi tříd definovaných ve Vapory. Jedná se o tyto třídy: Camera, LightSource, Sphere a Plane. Následně jsou všechny objekty přidány do scény představované instancí třídy Scene a scéna je ihned poté vykreslena metodou Scene.render(). Není tedy nutné explicitně volat POV-Ray; tento krok za nás Vapory provede automaticky.

Obrázek 6: Porovnání shodné scény – jednou zapsané v jazyku POV-Raye, podruhé v Pythonu.

6. Dočasný soubor vygenerovaný modulem vapory

V případě, že se vám podaří násilím přerušit rendering, můžete si prohlédnout dočasný soubor, který byl vygenerovaný modulem vapory z popisu scény. Tento soubor sice není příliš čitelný kvůli absenci mezer, to ovšem POV-Rayi vůbec nevadí a soubor zpracuje zcela korektně:

light_source {
<2,4,-3>
color
<1,1,1>
}
sphere {
<0,1,2>
2.0
texture {
pigment {
color
<1,1,0>
}
finish {
phong
1
phong_size
300
reflection
0.15
}
}
}
plane {
<0,1,0>
( -1.5 )
texture {
pigment {
color
<1,1,1>
}
finish {
reflection
0.1
}
}
}
camera {
location
<0.0,2.0,-3.0>
up
<0.0,1.0,0.0>
right
<1.3333333333333333,0.0,0.0>
look_at
<0.0,1.0,2.0>
right
<1.3333333333333333,0,0>
}
global_settings{

}

7. Druhý příklad – použití externích souborů s deklarací barev a textur

Ve druhém příkladu si ukážeme, jak lze využít externí soubory obsahující deklarace barev, textur popř. i parametrů těles. Těchto souborů obsahuje POV-Ray i ve standardní instalaci poměrně velké množství a lze je do scény snadno vložit příkazem #include:

// načtení všech potřebných externích souborů
#include "colors.inc"
#include "stones.inc"
#include "glass.inc"

Příklad použití (viz zvýrazněné identifikátory):

light_source {
    <-30, 11,  20>                       // pozice světelného zdroje
    color White                          // barva světla
}
 
plane {
    y,                                   // orientace roviny
    -1.5                                 // vzdálenost od počátku
    texture {                            // textura - vlastnosti povrchu
        T_Stone1                         // definováno v externím souboru
        pigment {                        // vlastní vzorek textury
            octaves 3                    // modifikace procedurálního vzorku
            rotate 90*z
        }
        finish {                         // optické vlastnosti materiálu
            reflection 0.10
        }
    }
}

Obrázek 7: Druhý demonstrační příklad vykreslený POV-Rayem.

Úplný zdrojový kód druhého příkladu ve variantě přímo pro POV-Ray vypadá takto:

// ------------------------------------------------------------
// Jednoduchá scéna s jedním uzavřeným objektem, jedním nekonečným
// objektem, trojicí světel a jednou kamerou (pozorovatelem)
//
// Založeno na souboru původně vytvořeném Danem Farmerem (leden 2002)
//
// rendering lze spustit příkazem:
//     povray +W800 +H600 +B +FN +D +Iscena2.pov +Oscena2.png
// (pro náhled postačí zadat povray scena2.pov)
// ------------------------------------------------------------
 
#version 3.5;
 
// globální nastavení parametrů scény
global_settings {
    assumed_gamma 2.2
    max_trace_level 5
}
 
// načtení všech potřebných externích souborů
#include "colors.inc"
#include "stones.inc"
#include "glass.inc"
 
// nastavení kamery (pozorovatele)
camera {
    location  <1.65, 5.5, -5.0>          // pozice kamery
    up        <0.0,  1.0,  0.0>          // vektor směřující vzhůru
    right     <4/3,  0.0,  0.0>          // vektor směřující doprava
    look_at   <0,    0.5, -1.0>          // bod, na který kamera směřuje
}
 
// tři světelné zdroje
light_source {
    <-30, 11,  20>                       // pozice světelného zdroje
    color White                          // barva světla
}
 
light_source {
    < 31, 12, -20>                       // pozice světelného zdroje
    color White                          // barva světla
}
 
light_source {
    < 32, 11, -20>                       // pozice světelného zdroje
    color LightGray                      // barva světla
}
 
// jediný uzavřený objekt ve scéně - zelená skleněná koule
sphere {
    <0, 0, 0>,                           // souřadnice středu koule
    1.75                                 // poloměr koule
    interior {                           // vlastnosti "vnitřku" koule
        caustics 1.0                     // světelná "prasátka"
        ior 1.5                          // index lomu
    }
    texture {                            // textura - povrch koule
        T_Glass1                         // definováno v externím souboru
        pigment {
            color green 0.90 filter 0.85 // barva povrchu
        }
        finish {                         // optické vlastnosti materiálu
            phong 1                      // velikost a síla odlesků
            phong_size 300
            reflection 0.15              // odrazivost
        }
    }
}
 
// druhý objekt - nekonečná rovina
plane {
    y,                                   // orientace roviny
    -1.5                                 // vzdálenost od počátku
    texture {                            // textura - vlastnosti povrchu
        T_Stone1                         // definováno v externím souboru
        pigment {                        // vlastní vzorek textury
            octaves 3                    // modifikace procedurálního vzorku
            rotate 90*z
        }
        finish {                         // optické vlastnosti materiálu
            reflection 0.10
        }
    }
}
 
 
 
// ------------------------------------------------------------
// finito
// ------------------------------------------------------------

8. Varianta naprogramovaná ve Vapory

Při použití Pythonu a knihovny Vapory je nutné v deklaraci scény uvést všechny vkládané soubory v nepovinném parametru included, který se předává konstruktoru třídy Scene. Hodnotou tohoto parametru je seznam vkládaných souborů:

scene = Scene(camera,
              objects=[light1, light2, light3, sphere, plane],
              included=["colors.inc", "stones.inc", "glass.inc"])

Úplný zápis druhého příkladu vypadá následovně:

from vapory import *
 
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')
 
# jediný uzavřený objekt ve scéně - zelená skleněná koule
sphere = Sphere([0, 0, 0],
                1.75,
                Interior(
                    'caustics', 1.0,
                    'ior', 1.5),
                Texture(
                    'T_Glass1',
                    Pigment('color', 'green', 0.9,
                            'filter', 0.85),
                    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)))
 
scene = Scene(camera,
              objects=[light1, light2, light3, sphere, plane],
              included=["colors.inc", "stones.inc", "glass.inc"])
 
scene.render("scene2_vapory.png", width=640, height=480)

9. Vylepšení předchozího příkladu – deklarace hodnoty pro gama korekci

Ve skutečnosti jsou výsledné obrázky vykreslené POV-Rayem odlišné, protože první varianta (vytvořená přímo v jazyku POV-Raye) obsahovala deklaraci hodnoty gmma, kdežto druhá varianta (Vapory) nikoli – viz též Gama korekce na Wiki:

Obrázek 8: Obrázek vykreslený při použití gama=2.2.

Obrázek 9: Obrázek vykreslený při použití výchozí hodnoty gama.

V POV-Rayi je nastavení výchozí hodnoty gama velmi snadné:

// globální nastavení parametrů scény
global_settings {
    assumed_gamma 2.2
}

Ve Vapory musíme použít další nepovinný parametr předávaný do konstruktoru třídy Scene. Tento parametr se jmenuje global_settings a jeho hodnotou je seznam řetězců, které se předávají POV-Rayi:

scene = Scene(camera,
              objects=[light1, light2, light3, sphere, plane],
              included=["colors.inc", "stones.inc", "glass.inc"],
              global_settings=["assumed_gamma 2.2"])

Opět si ukažme úplný zdrojový kód tohoto příkladu:

from vapory import *
 
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')
 
# jediný uzavřený objekt ve scéně - zelená skleněná koule
sphere = Sphere([0, 0, 0],
                1.75,
                Interior(
                    'caustics', 1.0,
                    'ior', 1.5),
                Texture(
                    'T_Glass1',
                    Pigment('color', 'green', 0.9,
                            'filter', 0.85),
                    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)))
 
scene = Scene(camera,
              objects=[light1, light2, light3, sphere, plane],
              included=["colors.inc", "stones.inc", "glass.inc"],
              global_settings=["assumed_gamma 2.2"])
 
scene.render("scene2B_vapory.png", width=640, height=480)

10. Konstruktivní geometrie těles v POV-Rayi

POV-Ray, podobně jako mnohé další raytracery, podporuje tzv. CSG. Reprezentace modelů pomocí konstruktivní geometrie těles (Constructive Solid Geometry – CSG) je založena na analytickém popisu těles jejich objemem, tj. podmnožinou trojrozměrného prostoru ležícího uvnitř tělesa. V literatuře se lze občas setkat s tvrzením, že konstruktivní geometrie těles patří mezi metody popisující hranici těles. To však není zcela přesné, protože v CSG můžeme pro libovolný bod jednoduše zjistit, zda leží uvnitř či vně tělesa, což se v POV-Rayi velmi často používá.

sceneX

Obrázek 10: Jednoduché těleso vytvořené z koule a krychle spojené operací rozdílu (difference).

Na druhou stranu se pro mnohá tělesa složitě zjišťují souřadnice bodů na povrchu těles, mnohdy je dokonce nutné použít numerické metody. Minimálně těmito vlastnostmi se tedy CSG odlišuje od běžně chápané hraniční reprezentace. Vývoj konstruktivní geometrie byl zahájen v roce 1973 s cílem definovat složité útvary pomocí operací sjednocení, průniku, rozdílu a hladkého napojení nad základními tělesy (primitivy).

sceneX

Obrázek 11: Jednoduché těleso vytvořené z koule a krychle spojené operací průniku (intersection).

Jako primitiva se většinou používají objekty popsatelné pomocí jedné či soustavou několika nerovnic, v nichž se vyskytují polynomy nízkého stupně. Typicky se jedná o následující objekty: koule, válec, kužel, elipsoid (vše jsou to kvadriky), toroid a poloprostor. Model vychází ze skutečnosti, že těleso v prostoru lze popsat jako množinu bodů, které splňují určité vlastnosti. Ty mohou být u jednoduchých objektů popsány jednou nerovnicí či soustavou nerovnic.

sceneX

Obrázek 12: Jednoduché těleso vytvořené z koule a krychle spojené operací spojení (union).

Popisovat složitý objekt s mnoha nerovnostmi na povrchu pomocí soustavy nerovnic může být velmi obtížné a může vést ke komplikacím při vyhodnocování (vykreslování) scény. Aby k uvedeným problémům nedocházelo, skládají se v CSG reprezentaci složitější objekty z objektů jednoduchých pomocí množinových operací. Množinovými operacemi, které při popisu přicházejí v úvahu, jsou binární nebo n-ární průnik (∩), sjednocení (∪) a rozdíl (-). Často se používá i unární operace doplněk ('), která znamená „převrácení“ tělesa, tedy smyslu bodů ležících uvnitř a vně tělesa se zachováním hranice.

sceneX

Obrázek 13: Základní 3D tělesa, která lze zkombinovat pomocí CSG.

Složité těleso je tedy v CSG reprezentaci popsáno stromem, který obsahuje ve svých listech geometrické, popř. i doplňkové informace o primitivních tělesech. V uzlech tohoto stromu jsou uloženy množinové operace, popřípadě i transformace. Lineárními transformacemi může být popsána změna polohy, popř. změna měřítka části tělesa ležícího v daném podstromu. Kromě lineárních transformací lze použít i obecnější transformace, které potom mohou reprezentovat například různé deformace buď celého tělesa nebo jeho části (zde se však již jedná o poměrně složitou technologii).

11. Třetí příklad – POV-Ray varianta

V POV-Rayi se CSG zapisuje jednoduše. Následuje ukázka operace rozdílu:

difference {
    těleso1
    těleso2
    transformace
}

Například pro vytvoření rozdílového tělesa mezi krychlí a koulí použijeme:

difference {
    box {
        <-VEL, -VEL, -VEL>               // jeden z vrcholů krychle na tělesové úhlopříčce
        < VEL,  VEL,  VEL>               // druhý z vrcholů krychle na tělesové úhlopříčce
        texture {                        // textura - povrch krychle
            T_Wood23                     // definováno v externím souboru
            finish {                     // optické vlastnosti materiálu
                phong 1                  // velikost a síla odlesků
                phong_size 300
                reflection 0.15          // odrazivost
            }
        }
    }
    sphere {
        <0, 0, 0>,                       // souřadnice středu koule
        1.8                              // poloměr koule
        texture {                        // textura - povrch koule
            T_Wood24                     // definováno v externím souboru
            finish {                     // optické vlastnosti materiálu
                phong 1                  // velikost a síla odlesků
                phong_size 300
                reflection 0.15          // odrazivost
            }
        }
    }
    rotate <0,45,0>                      // rotace výsledného "dvojtělesa"
}

Obrázek 14: Výsledné rozdílové těleso.

Úplný zdrojový kód třetího příkladu vypadá následovně:

// ------------------------------------------------------------
// Aplikace CSG operace rozdílu koule a krychle
//
// Založeno na souboru původně vytvořeném Danem Farmerem (leden 2002)
//
// rendering lze spustit příkazem:
//     povray +W800 +H600 +B +FN +D +Iscena2.pov +Oscena2.png
// (pro náhled postačí zadat povray scena3.pov)
// ------------------------------------------------------------
 
#version 3.5;
 
// globální nastavení parametrů scény
global_settings {
    assumed_gamma 2.2
    max_trace_level 5
}
 
// načtení všech potřebných externích souborů
#include "colors.inc"
#include "stones.inc"
#include "glass.inc"
#include "woods.inc"
 
// nastavení kamery (pozorovatele)
camera {
    location  <1.65, 5.5, -5.0>          // pozice kamery
    up        <0.0,  1.0,  0.0>          // vektor směřující vzhůru
    right     <4/3,  0.0,  0.0>          // vektor směřující doprava
    look_at   <0,    0.5, -1.0>          // bod, na který kamera směřuje
}
 
// tři světelné zdroje
light_source {
    <-30, 11,  20>                       // pozice světelného zdroje
    color White                          // barva světla
}
 
light_source {
    < 31, 12, -20>                       // pozice světelného zdroje
    color White                          // barva světla
}
 
light_source {
    < 32, 11, -20>                       // pozice světelného zdroje
    color LightGray                      // barva světla
}
 
#declare VEL=1.45;                       // velikost krychle
 
difference {
    box {
        <-VEL, -VEL, -VEL>               // jeden z vrcholů krychle na tělesové úhlopříčce
        < VEL,  VEL,  VEL>               // druhý z vrcholů krychle na tělesové úhlopříčce
        texture {                        // textura - povrch krychle
            T_Wood23                     // definováno v externím souboru
            finish {                     // optické vlastnosti materiálu
                phong 1                  // velikost a síla odlesků
                phong_size 300
                reflection 0.15          // odrazivost
            }
        }
    }
    sphere {
        <0, 0, 0>,                       // souřadnice středu koule
        1.8                              // poloměr koule
        texture {                        // textura - povrch koule
            T_Wood24                     // definováno v externím souboru
            finish {                     // optické vlastnosti materiálu
                phong 1                  // velikost a síla odlesků
                phong_size 300
                reflection 0.15          // odrazivost
            }
        }
    }
    rotate <0,45,0>                      // rotace výsledného "dvojtělesa"
}
 
// druhý objekt - nekonečná rovina
plane {
    y,                                   // orientace roviny
    -1.5                                 // vzdálenost od počátku
    texture {                            // textura - vlastnosti povrchu
        T_Stone1                         // definováno v externím souboru
        pigment {                        // vlastní vzorek textury
            octaves 3                    // modifikace procedurálního vzorku
            rotate 90*z
        }
        finish {                         // optické vlastnosti materiálu
            reflection 0.10
        }
    }
}
 
 
 
// ------------------------------------------------------------
// finito
// ------------------------------------------------------------ 

12. Třetí příklad – varianta používající Vapory

Přepis do Vapory je snadný, protože například rozdíl těles (CSG operace) se zapíše konstruktorem Difference:

csg_object = Difference(
    Box([-VEL, -VEL, -VEL],
        [VEL, VEL, VEL],
        Texture(
            'T_Wood23',
            Finish('phong', 1,
                   'phong_size', 300,
                   'reflection', 0.15))),
    Sphere([0, 0, 0],
           1.8,
           Texture(
               'T_Wood24',
               Finish('phong', 1,
                      'phong_size', 300,
                      'reflection', 0.15)))
)

Přepis celého demonstračního příkladu do Pythonu/Vapory může vypadat takto:

from vapory import *
 
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
 
csg_object = Difference(
    Box([-VEL, -VEL, -VEL],
        [VEL, VEL, VEL],
        Texture(
            'T_Wood23',
            Finish('phong', 1,
                   'phong_size', 300,
                   'reflection', 0.15))),
    Sphere([0, 0, 0],
           1.8,
           Texture(
               'T_Wood24',
               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)))
 
scene = Scene(camera,
              objects=[light1, light2, light3, csg_object, plane],
              included=["colors.inc", "stones.inc", "woods.inc"],
              global_settings=["assumed_gamma 2.2"])
 
scene.render("scene3_vapory.png", width=640, height=480)

13. Úprava předchozího příkladu: použití samostatných objektů, na něž se aplikují CSG operace

Předchozí příklad je možné upravit takovým způsobem, že nejdříve vytvoříme objekty představující samotná 3D tělesa vstupující do CSG:

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)))
 
sphere = Sphere([0, 0, 0],
                1.8,
                Texture(
                    'T_Wood24',
                    Finish('phong', 1,
                           'phong_size', 300,
                           'reflection', 0.15)))

Teprve poté se na tato dvě tělesa aplikuje vybraná operace. Předností je, že jedno těleso lze použít vícekrát, popř. jednodušší tvorba animací:

csg_object = Difference(box, sphere)

Výsledný kód naleznete na adrese https://github.com/tisnik/vapory-examples/blob/master/scene3_update.py:

from vapory import *
 
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)))
 
sphere = Sphere([0, 0, 0],
                1.8,
                Texture(
                    'T_Wood24',
                    Finish('phong', 1,
                           'phong_size', 300,
                           'reflection', 0.15)))
 
csg_object = Difference(box, sphere)
 
# 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)))
 
scene = Scene(camera,
              objects=[light1, light2, light3, csg_object, plane],
              included=["colors.inc", "stones.inc", "woods.inc"],
              global_settings=["assumed_gamma 2.2"])
 
scene.render("scene3B_vapory.png", width=640, height=480)

14. Základ tvorby jednoduchých animací

Jeden z důvodů, proč používat Vapory namísto přímé dekarace 3D scén v POV-Rayi, spočívá ve snadnější tvorbě animací, protože v Pythonu můžeme provádět různě složité výpočty, pracovat s nelineárním časem atd. atd. Samozřejmě si opět ukážeme příklad, který bude vycházet z příkladu předchozího, tj. i zde se bude využívat CSG (Constructive solid geometry). Ovšem celá scéna bude vytvářena a vykreslována v programové smyčce, v níž se bude postupně měnit souřadnice středu koule, která společně s krychlí tvoří těleso získané právě pomocí CSG. Koule je zpočátku umístěna mimo krychli a v navazujících snímcích postupně krychlí prolétá:

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"])

15. Vygenerování série snímků ve Vapory

Pokud výše uvedenou funkci spoustíte ve smyčce:

FRAMES = 100
 
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)

Vytvoří se série snímků ve formátu PNG:

Obrázek 14: Snímek číslo 0.

Obrázek 15: Snímek číslo 8.

Obrázek 16: Snímek číslo 16.

Obrázek 17: Snímek číslo 24.

Obrázek 18: Snímek číslo 32.

Obrázek 19: Snímek číslo 40.

Obrázek 20: Snímek číslo 48.

Obrázek 21: Snímek číslo 56.

Obrázek 22: Snímek číslo 64.

Obrázek 23: Snímek číslo 72.

Obrázek 24: Snímek číslo 80.

16. Vytvoření animace ze série snímků

Z těchto snímků je nutné vytvořit animaci, k čemuž (prozatím) využijeme nástroj ffmpeg:

ffmpeg -r 10 -i frame_%03d.png -b:v 700k scene4.ogg

Výsledkem by tedy měla být tato animace:
https://tisnik.github.io/moviepy-videos/video10.htm

17. Úplný zdrojový kód čtvrtého příkladu

Úplný zdrojový kód čtvrtého příkladu naleznete na adrese https://github.com/tisnik/vapory-examples/blob/master/scene4.py:

CS24_early

from vapory import *
 
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"])
 
FRAMES = 100
 
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)

18. Makefile soubor určený pro vykreslení všech scén POV-Rayem

Pro zajímavost si ještě ukažme soubor Makefile použitý pro vytvoření rastrových obrázků ze všech zdrojových kódů určených přímo pro POV-Ray, tj. ze souborů s koncovkou .pov. Tento soubor je součástí repositáře:

# seznam vsech zdrojovych v POV-Rayi
SOURCES := $(wildcard *.pov)
 
# seznam souboru, ktere se maji vygenerovat
GENERATED := $(patsubst %.pov,%.png,$(SOURCES))
 
all: ${GENERATED}
 
clean:
        rm *.png
 
%.png:  %.pov
        povray +W640 +H480 +B +FN +D +I$< +O$@
 
.PHONY: clean

19. Repositář s demonstračními příklady

Zdrojové kódy všech dnes popsaných demonstračních příkladů určených pro POV-Ray i Vapory byly uloženy do Git repositáře dostupného na adrese https://github.com/tisnik/vapory-examples. 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:

20. Odkazy na Internetu

  1. vapory na GitHubu
    https://github.com/Zulko/vapory
  2. Seriál na Rootu: Vykreslujeme 3D scény s POV-Ray
    https://www.root.cz/seria­ly/vykreslujeme-3d-sceny-s-pov-ray/
  3. Animace v POV-Rayi
    https://www.root.cz/clanky/animace-v-pov-rayi/
  4. Tvorba pokročilejších animací v POV-Rayi
    https://www.root.cz/clanky/tvorba-pokrocilejsich-animaci-v-pov-rayi/
  5. The POV-Ray Cyclopedia:
    http://www.spiritone.com/~en­glish/cyclopedia/index.html
  6. POV-Ray New Ring:
    http://webring.povray.org/
  7. Animations with POV-Ray:
    http://www.f-lohmueller.de/pov_tut/ani­mate/pov_anie.htm
  8. The POV-Ray Objects Collection:
    http://objects.povworld.org/
  9. POV-Ray Texture Library 4.0:
    http://texlib.povray.org/
  10. Galerie modelů vytvořených v Lparseru:
    http://home.wanadoo.nl/lau­rens.lapre/lparser2.html
  11. Charlie Chernohorsky :-) L-systémy ve FractIntu:
    http://fractint.oblivion.cz/
  12. POV-Ray Hall of Fame,
    http://hof.povray.org/
  13. Tekno Frannansa,
    http://www.zazzle.com/tekf
  14. Internet Raytracing Competition,
    http://www.irtc.org/
  15. POVRay Short Code Contest – Round 3,
    http://local.wasp.uwa.edu­.au/~pbourke/exhibition/scc3/fi­nal/
  16. SCC4: POVRay Short Code Contest,
    http://local.wasp.uwa.edu­.au/~pbourke/exhibition/scc4/fi­nal/
  17. SCC5: POVRay Short Code Contest #5 – The animation round!,
    http://local.wasp.uwa.edu­.au/~pbourke/exhibition/scc5/fi­nal.html
  18. POV-Ray posters,
    http://www.povray.org/posters/
  19. Parametric Constructive Solid Geometry
    http://c-csg.com/

Byl pro vás článek přínosný?

Autor článku

Vystudoval VUT FIT a v současné době pracuje na projektech vytvářených v jazycích Python a Go.