Obsah
1. Zobrazení objemových dat v POV-Rayi
2. Datový formát DF3 určený pro uložení objemových dat
3. První demonstrační příklad – vytvoření souboru typu DF3 pomocí céčkové utility
4. Použití objemových dat uložených v souborech typu DF3
5. Druhý demonstrační příklad – kvádr vyplněný voxely
6. Třetí demonstrační příklad – zvýraznění vybraných hodnot v prostorových datech
7. Odkazy na Internetu
8. Obsah další části seriálu
1. Zobrazení objemových dat v POV-Rayi
V předchozí části tohoto seriálu jsme si uvedli základní informace o problematice reprezentace a vykreslení objemových dat uložených ve formě pravidelné prostorové mřížky, která dělí prostor na objemové elementy – voxely. Objemová data je možné vykreslovat mnoha různými způsoby, přičemž nejzákladnější dělení je na algoritmy zobrazující povrchy a přímé objemové algoritmy. V POV-Rayi je podporováno přímé vykreslení objemových dat (Direct volume rendering algorithms) bez nutnosti vytváření pomocných povrchů. Jak si však ukážeme v další části seriálu, je možné – i když v omezené míře a většinou s velkými vizuálními chybami – přímo v POV-Rayi v objemových datech nalézt povrch a ten následně zobrazit jako izoplochu. Nejedná se sice o typickou oblast použití (podle mě je výhodnější použít předzpracování například algoritmem Marching Cubes či Marching Tetrahedra s vyhlazením výsledné trojúhelníkové sítě), ale jedná se o zajímavou a přitom poměrně neznámou vlastnost POV-Raye, takže by bylo škoda ji zde nezveřejnit.
Obrázek 1: Mraky v této scéně byly vykresleny na základě objemových dat, nejedná se tedy o 2D textury.
2. Datový formát DF3 určený pro uložení objemových dat
Objemová data reprezentovaná pravidelnou prostorovou mřížkou voxelů, je možné uchovávat a přenášet v souborech různých formátů. V praxi se poměrně často používají formáty určené především pro medicínské aplikace, ve kterých je mj. podporována komprimace prostorové mřížky, uložení voxelů v různých bitových hloubkách (často 12 bitů na voxel) a přidání metadat (například údajích o pacientech, nastavení CT či MR zařízení atd.). Autoři raytraceru POV-Ray namísto adaptace některého z těchto formátů (což by s sebou mohlo nést například pro volně šířený program neakceptovatelné licenční podmínky atd.) navrhli formát vlastní, který se vyznačuje především značnou jednoduchostí. Tento formát, jenž je nazýván podle koncovky souborů DF3, je podporován i několika dalšími utilitami a konverzními programy. Jak si ukážeme v další kapitole, je práce s tímto formátem přímočará a soubory typu DF3 je možné generovat pomocí prakticky jakéhokoli programovacího jazyka, který dokáže korektně pracovat s binárními soubory.
Obrázek 2: Další scéna obsahující skutečné „objemové“ mraky.
Formát DF3 se skládá ze dvou částí – hlavičky a vlastních objemových dat. Hlavička má vždy délku 6 byte a je v ní uloženo rozlišení prostorové mřížky. Jedná se o trojici šestnáctibitových hodnot, první hodnota udává rozlišení v x-ové ose, druhá hodnota rozlišení v ose y-ové a třetí hodnota v ose z-ové. Při práci se soubory typu DF3 na počítačích s architekturou x86 je nutné dát pozor na to, že dvojice bytů představujících šestnáctibitovou hodnotu je uložena v pořadí vyšší byte–nižší byte (rozdíl little endian vs big endian). Ihned za touto jednoduchou hlavičkou následují objemová data, která se skládají z jednotlivých voxelů. Počet bitů na jeden voxel může být 8 (256 různých hodnot), 16 (65536 hodnot) či 32 (4294967296 hodnot). Vzhledem k tomu, že počet bitů na voxel není uložen v hlavičce, musí POV-Ray správnou hodnotu zjistit sám. Udělá to velmi jednoduše – od celkové velikosti souboru odečte šest bytů hlavičky a takto zjištěnou hodnotu podělí celkovým počtem voxelů (ten získá vynásobením rozlišení mřížky). Výsledkem je hodnota 1, 2 nebo 4 odpovídající počtu bytů na jeden voxel. V případě, že má soubor odlišnou délku, tj. vydělením vznikne jiná hodnota než celé číslo 1, 2 či 4, je při zpracování scény nahlášena chyba.
Obrázek 3: Prostorová data představující výsledek počítačové simulace kosmologických jevů.
Pro představu s jakou mírou roste velikost souborů typu DF3, spolu s rostoucím rozlišením mřížky a počtem bytů, kterými je vyjádřena hustota voxelů, jsem vypracoval tabulku zobrazenou pod tímto odstavcem. Rozlišení prostorové mřížky, ve které jsou voxely uloženy, roste mezi jednotlivými řádky tabulky s mocninou dvou, pro jeden voxel může být (alespoň v současné verzi POV-Raye) alokován jeden byte, dva byty či čtyři byty a délka hlavičky je, jak jsme se již dozvěděli, vždy rovna šesti bytům. Při pohledu na tabulku je patrné, že celková velikost souboru je i pro relativně malé rozlišení mřížky (2563 voxelů) poměrně velká – v současnosti se například pomocí CT vytváří mřížky o rozlišení až 512×512×200 voxelů s bitovou hloubkou typicky 12 bitů (1,5 bytu) na voxel. V POV-Rayi je v mnoha případech (pokud se tedy nezobrazují data získaná z CT či MR) vhodnější pracovat spíše s menšími mřížkami; například pro vytváření modelů mraků či ohně většinou postačuje rozlišení 1283 voxelů s bitovou hloubkou jeden byte na voxel.
Rozlišení mřížky | Počet bytů na jeden voxel | Počet bytů v hlavičce | Celková velikost souboru |
---|---|---|---|
32× 32× 32 | 1 | 6 | 32774 |
32× 32× 32 | 2 | 6 | 65542 |
32× 32× 32 | 4 | 6 | 131078 |
64× 64× 64 | 1 | 6 | 262150 |
64× 64× 64 | 2 | 6 | 524294 |
64× 64× 64 | 4 | 6 | 1048582 |
128×128×128 | 1 | 6 | 2097158 |
128×128×128 | 2 | 6 | 4194310 |
128×128×128 | 4 | 6 | 8388614 |
256×256×256 | 1 | 6 | 16777222 |
256×256×256 | 2 | 6 | 33554438 |
256×256×256 | 4 | 6 | 67108870 |
Obrázek 4: Druhý demonstrační příklad, ve kterém je použita prostorová mřížka o velmi malém rozlišení 32×32×32 voxelů. Při vykreslování objemových dat nebyla použita interpolace (po zvětšení jsou jednotlivé voxely jasně viditelné).
3. První demonstrační příklad – vytvoření souboru typu DF3 pomocí céčkové utility
Před vysvětlením použití prostorových dat ve scénách vykreslovaných POV-Rayem si ukážeme, jakým způsobem je možné taková data vytvořit. Demonstrační program, jehož okomentovaný výpis můžete vidět níže, je naprogramován v céčku a slouží pro vytvoření prostorové mřížky o rozlišení 32×32×32 voxelů až 256×256×256 voxelů s následným uložením této mřížky do externího souboru. V programu se pracuje se strukturou t_voxels, která reprezentuje prostorovou mřížku, tj. její rozlišení i hodnoty jednotlivých voxelů, které jsou uloženy v trojrozměrném dynamickém poli. Paměť se pro mřížku alokuje pomocí funkce voxels_allocate(), maže (nuluje) funkcí voxels_erase() a ukládá do externího souboru ve funkci voxels_save(). Vzhledem k tomu, že hodnoty voxelů jsou uloženy ve formátu pohyblivé řádové čárky, je před uložením zapotřebí všechny voxely znormalizovat tak, aby jejich hodnoty ležely v rozsahu 0,0 až 1,0, což zajišťuje funkce voxels_normalize(). Poslední funkcí, která s prostorovou mřížkou pracuje, je funkce voxels_fill(), ve které se hodnoty voxelů naplňují na základě jednoduché prostorové funkce, kterou lze snadno změnit a dosáhnout tak zcela jiného obrázku po vykreslení voxelů POV-Rayem.
Obrázek 5: Druhý demonstrační příklad, ve kterém je použita prostorová mřížka o velmi malém rozlišení 32×32×32 voxelů. Při vykreslování objemových dat však byla použita trilineární interpolace, která i při takto malém rozlišení mřížky efektivně jednotlivé voxely „rozmaže“.
Následuje výpis zdrojového kódu výše popsané utility:
//-----------------------------------------------------------------------------
// Ukázkový příklad napsaný v céčku, který slouží pro vytvoření souboru ve
// formátu DF3. Překlad lze provést například takto:
// gcc -O3 -Wall make_df3.c
//-----------------------------------------------------------------------------
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#define MAX(x, y) ((x)>(y) ? (x) : (y))
#define MIN(x, y) ((x)<(y) ? (x) : (y))
#define HIBYTE(val) ((val) >> 8)
#define LOBYTE(val) ((val) & 0xff)
//-----------------------------------------------------------------------------
// Struktura nesoucí základní informace o prostorové mřížce a ukazatel na tuto
// strukturu (ten je použitý pro zjednodušení volání funkcí)
//-----------------------------------------------------------------------------
typedef struct
{
float *** volume; // prostorová mřížka - pole voxelů
int x_res; // rozlišení prostorové mřížky
int y_res;
int z_res;
} t_voxels, *p_voxels;
//-----------------------------------------------------------------------------
// Alokace paměti pro prostorovou mřížku
//-----------------------------------------------------------------------------
p_voxels voxels_allocate(const int x_res, const int y_res, const int z_res)
{
int i, j;
p_voxels voxels = malloc(sizeof(t_voxels));
// nastavení rozlišení prostorové mřížky
voxels->x_res = x_res;
voxels->y_res = y_res;
voxels->z_res = z_res;
// alokace paměti pro nejvyšší dimenzi
voxels->volume = malloc(x_res*sizeof(float **));
// alokace paměti pro druhou dimenzi
for (i=0; i<x_res; i++)
{
voxels->volume[i] = malloc(y_res*sizeof(float *));
}
// alokace paměti pro třetí dimenzi
for (i=0; i<x_res; i++)
{
for (j=0; j<y_res; j++)
{
voxels->volume[i][j] = malloc(z_res*sizeof(float));
}
}
return voxels;
}
//-----------------------------------------------------------------------------
// Vymazání prostorové mřížky - všechny voxely se nastaví na nulovou hodnotu
//-----------------------------------------------------------------------------
void voxels_erase(p_voxels voxels)
{
int i, j, k;
for (i=0; i<voxels->x_res; i++)
{
for (j=0; j<voxels->y_res; j++)
{
for (k=0; k<voxels->z_res; k++) // v případě IEEE formatu float
{ // lze nahradit voláním memset()
voxels->volume[i][j][k] = 0;
}
}
}
}
//-----------------------------------------------------------------------------
// Normalizace dat tak, aby padly do rozsahu 0-1
//-----------------------------------------------------------------------------
void voxels_normalize(p_voxels voxels)
{
float min=1e20, max=-1e20;
float delta;
int i, j, k;
// najdeme minimální a maximální hodnotu
for (i=0; i<voxels->x_res; i++)
{
for (j=0; j<voxels->y_res; j++)
{
for (k=0; k<voxels->z_res; k++)
{
min = MIN(min, voxels->volume[i][j][k]);
max = MAX(max, voxels->volume[i][j][k]);
}
}
}
// pokud jsou všechny hodnoty shodné, úprava minim a maxim
if (min >= max)
{
max=min+1;
min-=1;
}
printf("min=%6.3f max=%6.3f\n", min, max);
// druhy průchod mřížkou = vlastní normalizace
delta=max-min;
for (i=0; i<voxels->x_res; i++)
{
for (j=0; j<voxels->y_res; j++)
{
for (k=0; k<voxels->z_res; k++)
{
voxels->volume[i][j][k] = (voxels->volume[i][j][k]-min)/delta;
}
}
}
}
//-----------------------------------------------------------------------------
// Uložení mřížky do externího souboru
//-----------------------------------------------------------------------------
void voxels_save(p_voxels voxels, const char *filename)
{
int i, j, k;
FILE *fout=fopen(filename, "wb");
if (fout!=NULL)
{
// zápis hlavičky - nezapomenout na pořadí vyšší byte - nižší byte
fputc(HIBYTE(voxels->x_res), fout);
fputc(LOBYTE(voxels->x_res), fout);
fputc(HIBYTE(voxels->y_res), fout);
fputc(LOBYTE(voxels->y_res), fout);
fputc(HIBYTE(voxels->z_res), fout);
fputc(LOBYTE(voxels->z_res), fout);
// zápis vlastních dat uložených v prostorové mřížce
for (i=0; i<voxels->x_res; i++)
{
for (j=0; j<voxels->y_res; j++)
{
for (k=0; k<voxels->z_res; k++)
{
fputc(255.0*voxels->volume[i][j][k], fout);
}
}
}
printf("do souboru %s bylo zapsano %ld bytu\n", filename, ftell(fout));
fclose(fout);
}
}
//-----------------------------------------------------------------------------
// Vyplnění mřížky jednoduchým obrazcem
//-----------------------------------------------------------------------------
void voxels_fill(p_voxels voxels)
{
int i, j, k;
for (i=0; i<voxels->x_res; i++)
{
for (j=0; j<voxels->y_res; j++)
{
for (k=0; k<voxels->z_res; k++)
{
// normalizované souřadnice v prostorové mřížce
float x = (float)i/voxels->x_res;
float y = (float)j/voxels->y_res;
float z = (float)k/voxels->z_res;
// zde je možné si libovolně vyhrát
voxels->volume[i][j][k] = sin(x*2.0*M_PI)+sin(y*2.0*M_PI)+sin(z*2.0*M_PI);
}
}
}
}
//-----------------------------------------------------------------------------
// Hlavni funkce konzolové aplikace
//-----------------------------------------------------------------------------
int main(void)
{
int resolution;
// vytvoříme volumetrická data o různém rozlišení
for (resolution = 32; resolution<=256; resolution*=2)
{
char filename[20];
p_voxels voxels = voxels_allocate(resolution, resolution, resolution);
voxels_erase(voxels); // není v tomto případě zapotřebí
voxels_fill(voxels); // vlastní naplnění voxelů vypočtenými daty
voxels_normalize(voxels); // normalizace je nutná, předchozí funkce ji nemusela provést
sprintf(filename, "pokus%03d.df3", resolution);
voxels_save(voxels, filename);
}
return 0;
}
//-----------------------------------------------------------------------------
// finito
//-----------------------------------------------------------------------------
Obrázek 6: Vhodnou volbou funkce voxels_fill lze vytvořit i jednoduchý model kouře; zde je zobrazen v nepravých barvách.
4. Použití objemových dat uložených v souborech typu DF3
V případě, že již máme vytvořeny validní soubory typu DF3, které obsahují vypočtená či jiným způsobem zjištěná objemová data, lze přistoupit k jejich vykreslení pomocí POV-Raye. K tomuto účelu se nejčastěji používá nám již známý uzel media, kterým je možné specifikovat vlastnosti částic, jimiž je vyplněn vnitřek libovolného uzavřeného tělesa. V tomto uzlu se kromě několika důležitých parametrů určených pro algoritmus vykreslování specifikuje také funkce použitá pro výpočet hustoty částic v daném místě prostoru. Ovšem – a to je v našem případě důležité – místo nějaké matematické funkce či procedury (typu agate či bozo) lze specifikovat i externí soubor typu DF3, ve kterém jsou hustoty uloženy.
V tomto případě se předpokládá, že jsou v externím souboru uloženy informace o hustotách částic, které se nachází v prostoru omezeném jednotkovou krychlí, jejíž tělesová úhlopříčka končí v bodech [0,0,0] a [1,1,1] – to mj. znamená, že pokud se má objemovými daty vyplnit větší těleso či těleso, které se nachází na jiném místě prostoru, je nutné použít některou z lineárních transformací typu translate, rotate či scale. Pokud má objemová mřížka uložená v externím souboru malé rozlišení, je zapotřebí také nastavit správnou metodu interpolace objemových dat při jejich vykreslování. Pomocí atributu interpolate lze interpolaci buď zakázat (hodnota 0, nejrychlejší metoda), zvolit trilineární interpolaci (hodnota 1, většinou dává vyhovující výsledky) či interpolaci trikubickou (hodnota 2, v každém směru se hodnoty prokládají kubickou křivkou).
5. Druhý demonstrační příklad – kvádr vyplněný voxely
V dnešním druhém demonstračním příkladu je ukázán způsob vykreslení objemových dat pomocí uzlu media. Ve scéně se nachází jediné těleso – krychle s neviditelnými stěnami – která je vyplněna částicemi emitujícími a rozptylujícími světlo. Hustota částic a současně i barva emitovaného světla je odvozena z obsahu souboru typu DF3, který byl vytvořen pomocí prvního demonstračního příkladu (je možné použít kterýkoli vygenerovaný soubor, vizuální kvalita však bude závislá na rozlišení objemové mřížky, tj. celkovém počtu voxelů, kterými paprsek prochází). Při vykreslování objemové mřížky jsou hodnoty hustoty interpolovány pomocí lineární interpolace. Zvýšením hodnoty atributu intervals je možné docílit přesnějšího výpočtu, ovšem na úkor rychlosti vykreslování. Zdrojový kód druhého demonstračního příkladu má tvar:
// ------------------------------------------------------------
// Druhý demonstrační příklad na použití objemových dat
// v prostorových scénách vykreslovaných POV-Rayem.
//
// V této scéně je použitý jeden bodový zdroj světla, vnitřek
// kvádru je vyplněn částicemi emitujícími a rozptylujícími
// světlo, jejichž hustota (a přeneseně i barva) je načtena
// z externího souboru.
//
// rendering lze spustit příkazem:
// povray +W800 +H600 +B100 +FN +D +Iscena1.pov +Oscena1.png
// (pro náhled postačí zadat povray scena1.pov)
// ------------------------------------------------------------
#include "colors.inc"
// nastavení kamery
camera
{
location <0, 0, -10> // pozice kamery
look_at <0, 0, 0> // bod, na který se kamera dívá
angle 48 // zorný úhel
}
// zdroj světla
light_source
{
<500, 500, -500> // pozice zdroje světla
color White // barva světla
}
box // objemová data jsou představována kvádrem
{
<0,0,0>, <1,1,1>
texture
{
pigment
{
rgbt 1 // zcela průhledné stěny kvádru - zajímá nás pouze jeho vnitřek
}
}
interior
{
media
{
emission 0.5 // částice emitující světlo
scattering {1, 0.1} // částice rozptylující světlo
intervals 1 // vyšší hodnota - větší přesnost výpočtu, ovšem také delší čas
samples 5
method 3
density // definice hustoty částic na základě souboru s volumetrickými daty
{
density_file df3 "pokus032.df3" interpolate 1
color_map // mapování hustoty na barvu
{
[0 rgb 0]
[0.4 Blue]
[0.7 Yellow]
[1 Red]
}
}
}
}
hollow // aby výpočet rozptylu probíhal korektně, je důležité
// používat prázdná uzavřená tělesa
translate -0.5
scale 4
rotate <60,30,0>
}
// ------------------------------------------------------------
// finito
// ------------------------------------------------------------
Obrázek 7: Druhý demonstrační příklad, ve kterém je použita prostorová mřížka o rozlišení 256×256×256 voxelů. Je použita alternativní barvová mapa.
6. Třetí demonstrační příklad – zvýraznění vybraných hodnot v prostorových datech
Jednoduchou úpravou barvové mapy, pomocí níž se jednotlivým voxelům přiřazuje na základě jejich hustoty barva, lze docílit toho, že se v prostorových datech zvýrazní vybrané hodnoty, které většinou tvoří izoplochy, tj. souvislou plochu voxelů se stejnou či velmi podobnou hustotou. Vše probíhá následujícím způsobem: každému voxelu je při výpočtu barvy každého paprsku procházejícího scénou přiřazena hustota ležící v intervalu 0,0 až 1,0 na základě hodnoty přečtené z externího souboru typu DF3. Přitom nezáleží na tom, kolik bytů bylo pro jeden voxel alokováno. Jinými slovy je v případě uložení 1 byte na voxel hustota vypočtena vzorcem přečtený_byte/(28), v případě 2 bytů na voxel je použit vzorec přečtený_byte/(216) atd. Výsledkem je vždy hodnota ležící v již zmíněném intervalu 0,0 až 1,0, která je použita při čtení hodnoty barvy z barvové mapy. Důležité je, že tento výpočet je prováděn až v průběhu vykreslování, nikoli v přípravné fázi – to znamená, že se i prostorové mřížky s velkým rozlišením alokuje v operační paměti pouze tolik prostoru, jaká je velikost souboru typu DF3 na disku, i když samotné výpočty probíhají s hodnotami typu double (8 bytů).
Obrázek 8: Izoplocha spojující voxely s hustotou 0,50.
V případě, že je barvová mapa upravena tím způsobem, že obsahuje pouze několik „pruhů“ barev oddělených od sebe větším intervalem černé barvy, jsou po vykreslení objemových dat viditelné plochy právě v těch místech, které odpovídají barevnému pruhu. Při tomto způsobu vizualizace je většinou nutné zvolit vyšší hodnotu atributu intervals a samples, aby se zamezilo vzniku nežádoucích vizuálních artefaktů, které jsou způsobeny „kostkovitostí“ rastrové mřížky. Upravený zdrojový kód, ve kterém je pro objemová data vytvořena barvová mapa se třemi různými pruhy, má následující tvar:
// ------------------------------------------------------------
// Třetí demonstrační příklad na použití objemových dat
// v prostorových scénách vykreslovaných POV-Rayem.
//
// V této scéně je použitý jeden bodový zdroj světla, vnitřek
// kvádru je vyplněn částicemi emitujícími a rozptylujícími
// světlo, jejichž hustota (a přeneseně i barva) je načtena
// z externího souboru.
//
// rendering lze spustit příkazem:
// povray +W800 +H600 +B100 +FN +D +Iscena1.pov +Oscena1.png
// (pro náhled postačí zadat povray scena1.pov)
// ------------------------------------------------------------
#include "colors.inc"
// nastavení kamery
camera
{
location <0, 0, -10> // pozice kamery
look_at <0, 0, 0> // bod, na který se kamera dívá
angle 48 // zorný úhel
}
// zdroj světla
light_source
{
<500, 500, -500> // pozice zdroje světla
color White // barva světla
}
box // objemová data jsou představována kvádrem
{
<0,0,0>, <1,1,1>
texture
{
pigment
{
rgbt 1 // zcela průhledné stěny kvádru - zajímá nás pouze jeho vnitřek
}
}
interior
{
media
{
emission 0.5 // částice emitující světlo
scattering {1, 0.1} // částice rozptylující světlo
intervals 8 // vyšší hodnota - větší přesnost výpočtu, ovšem také delší čas
samples 10
method 3
density // definice hustoty částic na základě souboru s volumetrickými daty
{
density_file df3 "pokus256.df3" interpolate 1
color_map // mapování hustoty na barvu
{
[0.0 rgb 0]
[0.23 rgb 0] // první barevný pruh
[0.23 Blue]
[0.29 Blue]
[0.29 rgb 0]
[0.47 rgb 0] // druhý barevný pruh
[0.47 Yellow]
[0.53 Yellow]
[0.53 rgb 0]
[0.73 rgb 0] // třetí barevný pruh
[0.73 Red]
[0.79 Red]
[0.79 rgb 0]
[1 rgb 0]
}
}
}
}
hollow // aby výpočet rozptylu probíhal korektně, je důležité
// používat prázdná uzavřená tělesa
translate -0.5
scale 4
rotate <60,30,0>
}
// ------------------------------------------------------------
// finito
// ------------------------------------------------------------
Obrázek 9: Třetí demonstrační příklad, ve kterém je použita prostorová mřížka o rozlišení 256×256×256 voxelů.
7. Odkazy na Internetu
- Cloud generator (POV-Ray),
http://www.oyonale.com/modeles.php?lang=en&page=36 - ImageJ Plugins DF3,
http://fly.mpi-cbg.de/~saalfeld/df3.html - POVRay density (DF3) files or Using POVRay as a volume renderer,
http://local.wasp.uwa.edu.au/~pbourke/miscellaneous/df3/ - Scientific Visualizations with POV-Ray,
http://www.linuxjournal.com/article/7486 - df3 Python Class,
http://povray.tashcorp.net/tutorials/script_df3/
Obrázek 10: Odlišná barvová mapa vede ke zvýraznění jiných izoploch, konkrétně izoploch spojujících voxely s hustotou 0,62, 0,82 a 0,92.
8. Obsah další části seriálu
V následující části seriálu o raytraceru POV-Ray dokončíme část věnovanou vizualizaci objemových dat. Současně začneme s popisem alternativního způsobu výpočtu ambientní složky světla pomocí takzvané radiozitní (vyzařovací) metody. Jedná se o metodu, která byla do POV-Raye přidána ve verzi 3.1, my si však popíšeme novější variantu algoritmu výpočtu globálního osvětlení platnou pro verzi 3.5 a samozřejmě i pro verze vyšší.
Obrázek 11: Třetí demonstrační příklad vykreslený s využitím alternativní barvové mapy zvýrazňující hustoty 0,12, 0,22 a 0,32.