39
UNIVERSITATEA POLITEHNICA BUCUREȘTI FACULTATEA DE AUTOMATICĂ ȘI CALCULATOARE Proiect de licență RayTracing Distribuit Dragoș Papavă; Andrei Zanfir 7 Iulie 2010 Coordonator științific: Prof. Dr. Ing. Florica Moldoveanu

Raytracing Distribuit

Embed Size (px)

Citation preview

Page 1: Raytracing Distribuit

UNIVERSITATEA POLITEHNICA BUCUREȘTI

FACULTATEA DE AUTOMATICĂ ȘI CALCULATOARE

Proiect de licență

RayTracing Distribuit

Dragoș Papavă; Andrei Zanfir 7 Iulie 2010

Coordonator științific: Prof. Dr. Ing. Florica Moldoveanu

Page 2: Raytracing Distribuit

1

Cuprins 1. Introducere ................................................................................................................................ 2

2. Prezentare OpenCL .................................................................................................................... 2

2.1. Limbajul OpenCL ................................................................................................................... 3

2.2. Arhitectura OpenCL .............................................................................................................. 4

2.2.1. Modelul Platformă .......................................................................................................... 5

2.2.2. Modelul Memorie ............................................................................................................ 5

2.2.3. Modelul Execuție ............................................................................................................. 7

2.2.4. Modelul de Programare .................................................................................................. 8

3. Algoritmul RayTracing ............................................................................................................... 8

3.1. Descriere detaliată ............................................................................................................. 10

3.2. Detalierea algoritmului RayCasting ................................................................................... 10

3.3. Detalierea algoritmului RayTracing ................................................................................... 11

3.4. Avantajele oferite de RayTracing ....................................................................................... 11

3.5. Dezavantajele generate de RayTracing ............................................................................. 11

4. Arhitectura programului ......................................................................................................... 12

4.1. Comunicarea intre calculatoare ......................................................................................... 12

4.2. Distribuirea scenei 3D ........................................................................................................ 13

4.3. Distribuirea sarcinilor ......................................................................................................... 13

4.4. Colectarea rezultatelor randării ......................................................................................... 13

5. Post-procesarea imaginii finale .............................................................................................. 14

5.1. HDR (High Dynamic Range) Rendering .............................................................................. 16

5.2. Algoritmul HDR ................................................................................................................... 18

5.3. Metode de stocare a valorilor ce depășesc intervalul [0,1] .............................................. 20

5.4. Corecția gamma .................................................................................................................. 22

5.5. Calculul luminanței scenei ................................................................................................. 23

5.6. Adaptarea la lumină ........................................................................................................... 25

5.7. Tone Mapping ..................................................................................................................... 27

5.8. Filtrul Bright-Pass ............................................................................................................... 30

5.9. Gaussian Blur ...................................................................................................................... 31

5.10. Imaginea finală ................................................................................................................... 34

6. Evaluarea performanțelor ....................................................................................................... 36

7. Concluzii ................................................................................................................................... 37

Bibliografie ......................................................................................................................................... 38

Page 3: Raytracing Distribuit

2

1. Introducere

În industria de azi a divertismentului, imaginile de înaltă calitate generate pe

calculator sunt de cele mai multe ori o necesitate. De aici apare nevoia generării unor

imagini foto-realistice, adică niște imagini care reușesc să păcălească ochiul uman, creând

senzația de autenticitate.

Dacă reușim să cream astfel de imagini, ar fi un lucru și mai indrăzneț de încercat,

anume să le generăm în timpi interactivi. O plimbare printr-o pădure foto-realistă valorează

mai multe cuvinte decât o simplă fotografie a ei, în orice caz. Scopul final al acestei lucrări

este în mare parte realizarea acestui deziderat. Mai precis, lucrarea de față își propune să

realizeze o aplicație a algoritmului RayTracing.

Pentru a-i mări performanțele, aplicația va rula distribuit pe mai multe calculatoare

într-o arhitectură client-server, atât pe CPU-ul cât și pe GPU-ul fiecăruia, folosind toată

puterea de calcul disponibilă. Mai departe, rezultatele randării sunt post-procesate folosind

algoritmul HDR, pentru a îmbunătăți calitatea imaginilor rezultate.

Atât algoritmul RayTracing, cât și post-procesarea HDR au fost bine studiate de-a

lungul timpului, și există foarte multe implementări ale acestora. Noi ne-am propus să

facem o implementare a lor folosind standardul în computație nou apărut OpenCL, ce

permite folosirea plăcilor grafice de către aplicațiile intensiv computaționale.

Eu m-am ocupat atât cu arhitectura aplicației, ce presupune diviziunea eficientă a

sarcinilor către calculatoarele client și preluarea rezultatelor randării de la fiecare calculator

de către server, cât și cu post-procesarea imaginii rezultate, prin aplicarea a diverse filtre

imaginii și combinarea rezultatelor acestora. Colegul meu, Andrei Zanfir, a implementat

algoritmul de RayTracing pe CPU și GPU, iar detalii mai multe se pot citi în lucrarea lui.

2. Prezentare OpenCL

OpenCL (Open Computing Language) este o arhitectură pentru scrierea programelor

pe calculator care sunt executate pe platforme heterogene consistând din CPU, procesorul

plăcii grafice (GPU) și alte tipuri de procesoare. Scrierea codului OpenCL se realizează

Page 4: Raytracing Distribuit

3

folosind un limbaj bazat pe C99, în paralel cu un API ce administrează platformele

disponibile, compilează codul și-l lansează în execuție.

OpenCL asigură prelucrări paralele de date folosind un paralelism bazat atât pe

sarcini, cât și pe date. Arhitectura sa imparte o gamă de interfețe cu cea a doi competitori,

NVidia CUDA (Compute Unified Device Architecture) și DirectCompute al Microsoft.

OpenCL permite oricărei aplicații acces la GPU pentru orice tip de calcule, respectiv

calcule non-grafice. Placa grafică a fost inițial dezvoltată numai pentru aplicațiile grafice dar

între timp, puterea de calcul tot mai mare a acesteia a început să fie folosită într-o gamă

largă de aplicații. Un alt aspect ce ține de realizarea calculelor pe GPU este legat de memoria

sa, mai rapidă decât memoria RAM a sistemului. În felul acesta, OpenCL extinde puterea

unui GPU dincolo de grafică.

În momentul actual, OpenCL este administrat de consorțiul de tehnologie Khronos

Group. El a fost inițial dezvoltat de compania Apple, care deține drepturile de trademark, și

rafinat într-o propunere inițială, în colaborare cu echipele tehnice de la AMD, IBM, Intel și

NVidia. Apple a înaintat această propunere inițială grupului Khronos. Specificațiile versiunii

OpenCL 1.0 au fost lansate pe data de 18 noiembrie 2008. Companiile producătoare de plăci

grafice, AMD și NVidia, au decis să susțină OpenCL în paralel cu DirectX 11, într-o încercare

de a depăși modelul de programare actual al unui GPU, strâns legat de arhitectura sa.

2.1. Limbajul OpenCL

ANSI C este standardul publicat de către Institutul Național American de Standarde

(ANSI) pentru limbajul de programare C. Dezvoltatorii software care scriu cod C sunt

încurajați să se conformeze cerințelor deoarece este incurajată în acest fel portabilitatea

codului.

C89 este standardul adoptat în 1989 de către ANSI pentru limbajul C, iar în 1990 de

către Organizația Internațională de Standarde (ISO), în care a fost denumit C90. Prin urmare,

denumirile de C89, C90, ANSI C, ISO C și standard C, se referă la unul și același limbaj.

C99 este limbajul adoptat în martie 2000 de către ANSI și ISO, pe care il folosește

OpenCL. El extinde versiunea precedentă pentru a utiliza mai bine resursele hardware

Page 5: Raytracing Distribuit

4

disponibile și pentru o mai bună integrare a ultimelor progrese tehnologice în domeniul

compilatoarelor. Unele extensii oferă modalități mai convenabile de exprimare a unor

construcții particulare anumitor algoritmi. Altele oferă un control mai bun al optimizărilor și

calculelor numerice.

C99 este, în cea mai mare parte, compatibil cu C89 dar este mai strict în anumite

direcții. În particular, declarațiile care omit un tip de date nu mai sunt implicit presupuse ca

având tipul int. C99 introduce câteva caracteristici noi, multe din ele fiind deja

implementate in compilatoarele existente, precum:

Funcții inline

Amestecarea declarațiilor cu codul. Declarațiile variabilelor nu mai trebuie

făcute la începutul unui fișier, sau al începutul unui bloc de cod

Câteva noi tipuri de date, incluzând long long int, boolean, și complex

Funcții matematice generice, independente de tipul de date.

Suport îmbunătățit pentru virgula mobilă, IEEE 754

OpenCL utilizează un subset al acestui limbaj de programare (C99), cu extensii proprii

pentru paralelism.

2.2. Arhitectura OpenCL

OpenCL este un standard deschis pentru programarea unei colecții heterogene de

CPU, GPU, și alte dispozitive de calcul, organizate într-o singură platformă. El este mai mult

decât un limbaj, este o arhitectură pentu prelucrări paralele de date ce conține limbajul, API

și librării. Folosind OpenCL, un programator poate scrie algoritmi ce se pot executa pe plăci

grafice fără a fi nevoie ca acesta să ii modifice pentru a accesa API-ul specializat pe grafică

3D, precum DirectX sau OpenGL.

Ținta OpenCL este cea a programatorilor experți care doresc să scrie cod portabil dar

eficient. Prin urmare, OpenCL furnizează o abstractizare low-level a hardware-ului, plus un

cadru de programare și de expunere a unor detalii ale hardware-ului subiacent. Următoarele

modele descriu ideile de bază din spatele OpenCL:

Modelul Platformă

Page 6: Raytracing Distribuit

5

Modelul Memorie

Modelul Execuție

Modelul de Programare

2.2.1. Modelul Platformă

Modelul Platformă constă dintr-un host conectat la unul sau mai multe dispozitive

OpenCL. Un dispozitiv OpenCl este divizat într-unul sau mai multe unități de computare

(Compute Units – CUs), care sunt divizate mai departe într-unul sau mai multe elemente de

procesare (Processing Elements – PEs). Calculele efectuate de un device au loc pe

elementele de procesare.

O aplicație OpenCL ruleaza pe host, calculatorul gazdă, potrivit modelelor de

programere pe acesta. Aplicația OpenCL trimite apoi comenzi de pe host pentru a executa

calcule pe elementele de procesare al dispozitivului. Acestea pot lucra fie în modul SIMD, fie

în modul MIMD.

2.2.2. Modelul Memorie

Memoria ce poate fi accesată de work-items, threadurile de lucru, se împarte în

patru regiuni distincte:

Memoria Globală

- Permite acces de scriere și citire din parte oricărui work-item

Page 7: Raytracing Distribuit

6

- În funcție de capabilități, ea poate avea cache, sau nu

Memoria Constantă

- Este o regiune de Memorie Globală al cărei conținut rămâne constant

- Utilizarea acesteia permite optimizări din partea compilatorului

Memoria Locală

- Este o zonă de memorie mapată unui work-group.

- Este folosită pentru a aloca variabile împărțite de toate work-items dintr-

un work-group

- Este foarte rapidă

Memoria Privată

- Variabilele definite în această zonă nu pot fi accesate dintr-un alt work-item

Page 8: Raytracing Distribuit

7

2.2.3. Modelul Execuție

Execuția unui program OpenCL are loc în două părți: kernelul care este executat pe

unul sau mai multe device-uri OpenCL, și programul host care este executat pe calculatorul

gazdă. Programul host inițializează device-urile, definește contextul în care acestea rulează

codul kernel și lansează în execuție kernel-urile.

Baza modelului de execuție OpenCL este definită de modul în care kernel-urile sunt

executate. Atunci când un kernel este prezentat execuției, este definit un spațiu index. O

instanță a kernel-ului este executată pentru fiecare punct din acest index. Această instanță a

unui kernel este denumită un work-item și este identificată printr-un punct in spațiul index,

astfel creând un ID global pentru fiecare work-item. Fiecare work-item rulează același cod,

dar calea de execuție specifică fiecăruia poate să varieze de la un work-item la altul. De

asemenea, pot să varieze și datele operate de fiecare work-item. Acest spațiu index poate

avea de la una până la 3 dimensiuni.

Un exemplu în care un kernel este lansat în execuție într-un spațiu index

bidimensional este prezentat în figura de mai jos. Dacă spațiul index are o dimensiune de

( , ) work-items, iar acestea sunt organizate în ( , ) work-groups, se definesc

următoarele formule, unde ( , ) reprezintă ID-ul global, ( , ) reprezintă ID-ul work-

group-ului din care face parte work-item-ul, iar ( , ) reprezintă ID-ul work-item-ului în

cadrul work-group-ului:

( ) ( )

( ) ( ⁄ ⁄ )

( ) (( ) ⁄ ( ) ⁄ )

Page 9: Raytracing Distribuit

8

2.2.4. Modelul de Programare

Modelul de Execuție OpenCL suportă două modele de programare, unul orientat pe

date, iar al doilea, orientat pe task-uri.

Modelul de programare paralel de date definește calculele în termenii unei secvențe

de instrucțiuni aplicate mai multor elemente concomitent.

Modelul de programare orientat pe task-uri definește un model în care o singură

instanță a unui kernel este executată pe o unitate de calcul de către un work-item

aparținând unui work-group. Sub acest model, programatorii exprimă paralelismul prin

folosirea tipurilor de date vectoriale, sau prin lansarea in execuție a mai multor task-uri în

același timp.

3. Algoritmul RayTracing

În grafica pe calculator, RayTracing este o tehnică pentru generarea unei imagini prin

urmărirea căii luminii prin pixelii unui plan de imagine și simularea efectelor întâlnirii sale cu

obiectele virtuale. Această tehnică este capabilă să producă un grad foarte mare de realism,

mult mai ridicat decât cel obținut prin metodele tradiționale de tipul Scanline Rendering, dar

la un cost computațional mai mare. Acest lucru face RayTracing-ul potrivit pentru aplicațiile

Page 10: Raytracing Distribuit

9

unde imaginea poate fi randată lent, precum imaginile statice sau filmele de televiziune, și

deloc potrivit pentru aplicațiile în timp real, cum ar fi jocurile pe calculator unde viteza este

esențială. RayTracing este capabil să simuleze o mare varietate de efecte optice, de genul

reflexiilor, refracțiilor, împrăștierii luminii sau aberațiilor cromatice.

RayTracing-ul optic descrie o metodă pentru producerea de imagini vizuale

construite în mediile 3D ale graficii pe calculator, cu mai mult fotorealism decât RayCasting

sau Scanline Rendering. Acesta funcționează prin trasarea unei căi de la un ochi imaginar

prin fiecare pixel într-un ecran virtual, și calcularea culorii obiectului vizibil prin acea rază,

precum în imaginea următoare:

Scenele în RayTracing sunt descrise matematic de un programator sau de către un

artist vizual. Ele pot să includă date din alte imagini precum texturile sau modele capturate

prin fotografiere digitală. De obicei, fiecare rază trebuie testată pentru intersecție cu un

subset al tuturor obiectelor dintr-o scenă. Odata ce cel mai apropiat obiect a fost identificat,

algoritmul estimează lumina primită în punctul de intersecție, examinează proprietățile de

material ale obiectului, și combină aceste informații pentru a calcula culoarea și intensitatea

luminii ce ajung în pixelul respectiv. Unele proprietăți ale materialelor, precum cele

translucide, transparente sau reflective necesită trimiterea de raze suplimentare în spațiul

scenei.

Page 11: Raytracing Distribuit

10

Ar putea părea contraintuitiv să trimiți raze mai departe de cameră decât în ea, așa

cum face lumina în realitate. Dar, acest lucru este de multe ordine de mărime mai eficient.

Deoarece majoritatea razelor de lumină de la o sursă nu ajung direct în ochiul privitorului, o

astfel de simulare ar pierde o mare cantitate de putere de calcul pe razele de lumină ce nu

sunt niciodată înregistrate.

3.1. Descriere detaliată

În natură, o sursă de lumină emite o rază care se deplasează în cele din urmă la o

suprafață care întrerupe evoluția acesteia. Se poate gândi această rază ca un flux de fotoni

care călătoresc pe aceeași cale; într-un vid perfect, această rază va fi o linie dreaptă. În

realitate, orice combinație de următoarele patru lucruri se pot întâmpla cu raza: absorbție,

reflexie, refracție și fluorescență. O suprafață poate reflecta totul sau doar o parte din raza

de lumină, ducând la o pierdere de intensitate a luminii reflectate sau refractate. Dacă

suprafața are orice proprietăți transparente sau translucide, ea refractă o porțiune din

fasciculul de lumină în interiorul său, în timp ce absoarbe o parte sau tot spectrul razei,

alterând culoarea sa. Mai puțin obișnuit este fenomenul când o suprafață poate să absoarbă

o porțiune a razei și s-o reemită cu o altă lungime de undă, într-o direcție oarecare, fenomen

ce se numește fluorescență. El este atât de rar, încât este omis din majoritatea motoarelor

de randare.

3.2. Detalierea algoritmului RayCasting

Primul algoritm de RayCasting folosit pentru randare a fost prezentat de Arthur

Appel în 1968. Ideea din spatele RayCasting-ului este de a trimite raze dinspre ochiul

privitorului, câte una per pixel, și de a găsi cel mai apropiat obiect care blochează drumul

fiecărei raze. Folosind proprietățile de material și efectul dat de luminile existente în scenă,

algoritmul poate calcula culoarea obiectului.

Un avantaj important asupra algoritmilor mai vechi de genul Scanline este abilitatea

de a trata ușor suprafețele neplanare precum conurile și sferele. Dacă o suprafață

matematică poate fi intersectată cu o rază, ea poate fi randată folosind RayCasting.

Page 12: Raytracing Distribuit

11

3.3. Detalierea algoritmului RayTracing

Următoarea descoperire importantă în domeniu a venit de la Turner Whitted în

1980. Algoritmii anteriori parcurgeau razele emise din centrul ochiului virtual al privitorului

până la obiectele întâlnite, dar nu mai departe. Whitted a continuat procesul: când o rază

întâlnește o suprafață, ea poate să genereze trei noi tipuri de raze, reflexie, refracție și de

umbră. O rază reflectată continuă în direcția reflexiei de tip oglindă dintr-o suprafață

strălucitoare. Ea este apoi intersectată cu obiectele din scenă, iar cel mai apropiat obiect

întâlnit va fi văzut în reflexie.

Un alt fel de rază, cel de umbră, este folosit pentru a testa daca o suprafață este

vizibilă luminii. Dacă orice obiect este găsit între suprafață și sursa de lumină, atunci

suprafața inițială este în umbră și nu va fi luminată.

Acest strat nou de calcule folosind raze a adăugat un realism crescut imaginilor

obținute prin trasare de raze.

Mai departe, despre implementarea algoritmului de RayTracing, se poate citi în

proiectul de licență al colegului meu, Andrei Zanfir.

3.4. Avantajele oferite de RayTracing

Popularitatea RayTracing-ului s-a dezvoltat datorită simulării realistice a iluminatului

peste alte metode de randare, cum ar fi Scanline Rendering sau RayCasting). Efectele

precum reflexiile și umbrele, care sunt dificil de simulat folosind alți algoritmi, sunt un

rezultat natural al algoritmului de RayTracing.

Este relativ simplu de implementat și oferă rezultate vizuale impresionante.

Independența computațională a fiecărei raze face RayTracing-ul paralelizabil cu un minim de

efort.

3.5. Dezavantajele generate de RayTracing

Un dezavantaj serios al tehnicii de randare RayTracing este performanța. Algoritmii

de tip Scanline, precum și alții, folosesc coerența datelor pentru a împărți unele calcule între

pixeli, în timp ce RayTracing tratează fiecare pixel independent. Chiar dacă RayTracing se

ocupă bine de interreflexii și unele efecte obtice, cum ar fi refracția exactă, fotorealismul nu

Page 13: Raytracing Distribuit

12

se obține atât de ușor. El se conturează atunci când ecuația de randare este foarte bine

aproximată sau implementată total. Această ecuație definește fiecare efect fizic al fluxului

de lumină. Însă, până și buna simulare a ecuației este irealizabilă, dacă ne gândim la

resursele de computației implicate.

4. Arhitectura programului

Într-o încercare de a preveni și a elimina timpii mari de rulare ai algoritmului

RayTracing, am ales o arhitectură hibridă, formată din CPU, GPU, și mai multe stații de lucru.

Ideea de la care am pornit a fost să folosim toate resursele disponibile ale unui calculator,

distribuind algoritmul RayTracing atât pe procesorul sistemului, cât și pe placa grafică,

folosind OpenCL. Mai departe, am divizat munca de randare pe mai multe calculatoare din

aceeași rețea sau din internet, după modelul client-server.

4.1. Comunicarea intre calculatoare

Diferitele stații de lucru ce doresc să ajute procesul de randare se pot conecta în

orice moment la serverul ce randează deja, ca apoi sa li se distribuie o zonă unică de imagine

pe care acestea o vor randa.

Protocolul de comunicație ales este TCP/IP, pentru a putea determina simplu și

instantaneu momentul când un calculator se deconectează de la server. Dacă acela nu a

reușit să își transfere întreaga imagine randată serverului, pentru a nu umple acea zonă de

imagine cu o culoare precum cea neagră, serverul refolosește imaginea randată la frame-ul

trecut. În acest fel, deconectarea unui calculator în timpul randării scenei este insesizabilă

pentru scenele statice sau foarte puțin mișcate.

Peste protocolul TCP/IP am dezvoltat un protocol propriu, ce conține un header și un

payload. Header-ul anunță serverul sau calculatoarele străine ce tip de pachet a ajuns și ce

dimensiune are acesta. Dintre tipurile de pachete, amintesc: ComputerPower-puterea de

calcul a unui calculator obținută ca un benchmark, Work-coordonatele imaginii ce va fi

randată de calculatorul la care ajunge acest pachet, sau ACK-confirmarea de primire a unui

pachet pentru a putea calcula timpi intermediari de genul latenței pe rețea sau calculul

vitezei de transfer a imaginii.

Page 14: Raytracing Distribuit

13

4.2. Distribuirea scenei 3D

Deoarece la momentul redactării acestui document nu m-am pus de acord cu colegul

meu referitor la formatul scenei 3D și a tipurilor de obiecte ce vor fi randate, precum

meshuri formate din triunghiuri sau obiecte matematice descrise prin ecuații de genul

sferei, cubului sau suprafețelor complexe, scena 3D nu este distribuită dinamic de la server

la clienți, ci este încărcată de către fiecare dintr-un fișier existând pe fiecare calculator.

4.3. Distribuirea sarcinilor

Atunci când fiecare client se conectează la server, el trimite acestuia informații

despre puterea sa de calcul, informații obținute printr-un benchmark al puterii de calcul a

procesorului și a plăcii video. Serverul, apoi, calculează câte linii de imagine să aloce pentru

randat fiecărui client în parte, proporțional cu puterea fiecăruia de calcul.

Distribuirea sarcinilor ar fi putut fi realizată și în mod dinamic, ținând cont în plus și

de latența pe rețea, bandwidth-ul fiecărei conexiuni în parte sau distribuția obiectelor pe

ecran (dacă toate obiectele sunt în partea de sus a ecranului, atunci unele calculatoare vor

lua mai mult din volumul de calcule, chiar dacă numărul de pixeli alocați este mai mic sau

egal cu al altora).

Motivul pentru care am ales o divizare statică a sarcinilor este datorat ideii inițiale de

la care a pornit dezvoltarea aplicației. Datorită faptului că se dorește obținerea de timpi de

rulare interactivi sau în timp real, un calcul preliminar de determinare a poziției fiecărui

obiect în cadrul imaginii ar lua prea mult timp. Apoi, din cauza timpilor foarte mici de rulare,

orice program care rulează în background îi poate perturba ușor, făcând grea măsurarea lor

precisă.

4.4. Colectarea rezultatelor randării

După ce serverul și clienții și-au terminat sarcinile alocate, serverul intră într-o stare

blocantă, așteptând să primească imaginile de la fiecare dintre aceștia. Ca optimizare,

bufferul în care se scriu imaginile primite este același cu bufferul de la scena precedentă, ca

atunci când un client se deconectează în timpul randării, serverul să refolosească imaginea

trecută.

Page 15: Raytracing Distribuit

14

După ce acest proces este terminat, atât serverul cât și clienții afișează imaginile pe

ecran, copiindu-le într-o textură DirectX 10. Clienții afișează doar imaginea randată de ei,

dar serverul afișează toată imaginea.

În funcție de configurarea serverului, acesta poate sau nu să post-proceseze

imaginea obținută.

5. Post-procesarea imaginii finale

Termenul de post-precesare este folosit în industria de film pentru a denumi

metodele de îmbunătățire a unei imagini prin procesarea ei de către algoritmi specializați,

după ce aceasta a fost generată.

Post-procesarea video este procesul de schimbare a calității percepute a unui film

sau a unei imagini. Acesta presupune pași precum editarea imaginii, adăugarea de efecte

speciale, schimbarea tonului imaginii, mărirea sau micșorarea rezoluției acesteia, alterarea

luminanței imaginii, ș.a.m.d.

Modelarea pe calculator a lumii înconjurătoare necesită reproducerea pe un

dispozitiv cu o gamă redusă de luminanță, precum monitorul calculatorului, a imaginilor cu o

gamă foarte ridicată de luminanțe (sau imagini HDR). Tradițional, aceste imagini sunt

rezultatul unor algoritmi complecși, corecți din punct de vedere fizic, dar foarte costisitori.

Progresul recent în domeniu permite atât capturarea de imagini HDR cât și randarea

acestora în timp real. Totuși, contrastul acestor imagini depășeste posibilitățile de afișare a

dispozitivelor convenționale precum monitoarele de calculator, iar afișarea lor directă nu

este posibilă.

Din fericire, s-au făcut studii intensive în prelucrarea imaginilor HDR, iar importanța

unor algoritmi de procesare a lor este înțeleasă pe scară largă. Au fost dezvoltați mulți

algoritmi, dintre care unii dintre ei au implementări ce permit rularea în timp real.

Abordările curente sunt departe de a fi perfecte datorită multitudinii de efecte perceptuale

pe care ochiul uman le manifestă, iar unele dintre acestea sunt neglijate. Pentru un

observator obișnuit al imaginilor post-procesate prin HDR este evident faptul că o scenă din

natură cu lumină puternică trebuie să fie foarte aprinsă și colorată, iar o scenă nocturnă

devine întunecată și colorată în nuanțe de gri.

Page 16: Raytracing Distribuit

15

Chiar dacă unele efecte perceptuale pe timp de noapte, precum pierderea acuității

vizuale, când marginile obiectelor devin șterse. Au fost obținute rezultate atrăgătoare

folosind operatori locali de schimbare a tonului [Devlin et al. 2002].

Pe de altă parte, efecte perceptuale precum orbirea din cauza luminii puternice nu

pot fi create pe calculator din varii motive: gama de luminanțe pe care o poate afișa un

monitor este foarte redusă, iar chiar dacă s-ar putea, ochii privitorului ar deveni foarte

obosiți. Totuși, suntem foarte obișnuiți cu un astfel de fenomen, încât adăugarea unui efect

de orbire obținut prin împrăștierea pixelilor cu o luminanță mare într-o vecinătate a lor

poate mări strălucirea percepută a unei imagini [Spencer et al. 1995].

Este evident faptul că predicția și simularea acestor efecte, în decursul procesului de

mapare a unei game ridicate de luminanțe către o gamă redusă a dispozitivelor tipice de

afișare, sunt cruciale în transmiterea unei impresii realistice în vizualizarea imaginilor.

În capitolele următoare este prezentată o metodă eficientă de a combina cele mai

importante efecte perceptuale în contextul percepției imaginilor cu un contrast ridicat.

Pentru implementare am folosit platforma OpenCL în detrimentul shaderilor, pentru

a rula această post-procesare pe placa grafică. Avantajele oferite precum independența de

hardware, abilitatea de a manevra seturi mari de date, precum șirul de pixeli, mai eficient

decât unele programe shader, sau susținerea și dezvoltarea activă a platformei OpenCL de

către marile companii precum Intel, NVidia și AMD, au fost suficiente pentru alegerea

acestei platforme.

Page 17: Raytracing Distribuit

16

5.1. HDR (High Dynamic Range) Rendering

În lumea reală, diferența dintre cel mai luminos punct dintr-o scenă și cel mai

întunecat punct poate fi mult mai mare decât cea dintr-o imagine afișată pe ecranul

calculatorului. Raportul dintre cea mai mare luminanță a unui punct al scenei și cea mai

mică se numește gamă dinamică (dynamic range).

Gama de luminanțe pe care o putem remarca în lumea reală este vastă. Poate ajunge

până la 1012:1 pentru raportul dintre luminanța zăpezii luminată de soare și scenele

nocturne luminate doar de lumina stelelor. Dar o asemenea scenă nu o putem concepe

deoarece este necesară, în același timp, prezența si absența soarelui. Până una alta, scenele

privite de noi zi de zi au în general o gamă dinmică de 104:1. O particularitate a ochiului

uman este aceea că el poate percepe într-un anumit moment de timp o gamă de doar 103:1,

între zonele luminate și umbrele dintr-o scenă observată. Pentru a putea vedea lumina

răspândită într-o gamă dinamică ridicată, sistemul vizual uman ajustează automat

expunerea la lumină, selectând o gamă redusă de luminanțe, bazându-se pe intensitatea

luminii primite. Acest proces de adaptare a cantității de lumină ce ajunge pe retină este

Page 18: Raytracing Distribuit

17

întârziat, după cum se poate observa într-o zi însorită, atunci când cineva intră într-o cameră

întunecată, venind de afară, sau vice-versa, iar acest fenomen este cunoscut drept ajustarea

expunerii. Totuși, gama de luminanțe pe care o poate reproduce monitorul calculatorului

este in jur de 300:1, dar vom reveni asupra acestui aspect trei paragrafe mai jos.

O altă proprietate a sistemului vizual uman se observă atunci când sunt privite zone

extrem de luminoase. Într-o astfel de situație, nervul optic se poate încărca excesiv și poate

produce un efect de orbire.

Ochiul uman este alcătuit din două tipuri principale de fotoreceptori, conuri și

bastonașe. Atunci când este privită o zonă luminată slab, bastonașele se închid, iar toată

percepția se realizează prin conuri. Acuitatea vizuală scade datorită acestor conuri care pot

detecta o singură lungime de undă, cea corespunzătoare culorii albastre, iar creierul uman

percepe o astfel de imagine într-o culoare gri, cu tente albastre. Nu numai că există o

schimbare a tentei culorilor percepute, dar, de asemenea, din moment ce sunt mai puțini

fotoni ce ajung pe retină, imaginea conține zgomot și prezintă o pierdere de detaliu

generală.

Datorită gamei dinamice restrânse a monitoarelor, calculele de iluminare sunt

mărginite în intervalul de gamă îngust al dispozitivului, astfel încât imaginile afișate arată

spălate, suprasaturate, sau întunecate pe monitor. În plus, un monitor nu este capabil să

reproducă fenomene de genul orbirii, arderii retiniene, sau pierderii de percepție asociate

cu schimbările rapide in luminozitatea scenei. Sistemele de randare care încearcă să

Page 19: Raytracing Distribuit

18

reproducă aceste fenomene sunt numite sisteme de înaltă gamă dinamică (High Dynamic

Range) sau pe scurt, HDR.

Un sistem de randare ce folosește HDR își menține o gamă înaltă de luminanțe pe

parcursul procesului de randare, și comprimă această gamă la intervalul mic de luminanțe

care poate fi afișat pe monitor, proces ce se numește tone mapping.

5.2. Algoritmul HDR

În grafica 3D, high dynamic range rendering (HDRR sau HDR Rendering) reprezintă

randarea scenelor prin utilizarea unei game de valori ridicată pentru calculele de iluminare.

Acest lucru permite păstrarea de detalii care pot fi pierdute din cauza unei rate de contrast

scăzute. Jocurile video, filmele generate pe calculator și jocurile beneficiază de acesta

deoarece creează scene realiste folosind modele de iluminat simpliste.

Compania de procesoare grafice NVidia a rezumat motivația pentru HDRR în trei

puncte:

Lucrurile luminoase pot fi foarte luminoase

Lucrurile întunecate pot fi foarte întunecate

Detaliile scenei pot fi observate peste tot

Unul din avantajele primare ale randării HDR este acela că detaliile unei scene cu o

rată mare de contrast sunt conservate. Fără HDR, zonele care sunt foarte întunecate sunt

limitate la valoarea negru, iar zonele foarte luminoase sunt limitate la valoarea alb. Aceste

valori sunt reprezentate de hardware, în virgulă mobilă, drept 0.0 și 1.0 pentru negrul pur,

respectiv albul pur.

Un alt aspect al randării HDR este adăugarea de indicii perceptuale care măresc

luminozitatea aparentă. În randarea HDR se pot simula fenomene precum radierea din jurul

becurilor incandescente. Totodată, este afectat modul în care iluminarea este păstrată de

fenomenele optice de tipul reflexiilor și refracțiilor, precum și în materialele transparente de

genul sticlei. Nefolosind HDR, reflexia unei surse de lumină puternice precum soarele, a

cărei intensitate este, deci, 1.0, are un rezultat cu o intensitate luminoasă mai mică decât

1.0, ceea ce-i conferă o culoare gri-alb. Cu toate acestea, folosirea HDR-ului permite ca

Page 20: Raytracing Distribuit

19

sursele de lumină să aibă intensități mult mai mari decât 1, simulând valorile lor actuale, iar

atât reflexiile cât și celelalte fenomene generează rezultate realistice.

Implementarea algoritmului HDR presupune, în prealabil, calcularea luminanței

medii și a luminanței maxime pentru imaginea randată. Aceste două valori sunt apoi

interpolate cu valorile calculate frame-ul trecut pentru a simula adaptarea ochilor spre

lumină mai slabă sau spre lumină mai puternică. Fără acest pas, luminanța finală de la o

scenă la alta nu ar mai varia continuu, ci s-ar produce un efect stroboscopic datorită

posibilelor salturi în luminanță între scenele succesive.

De valorile luminanței unei scene depind pașii următori, Bright Pass, respectiv Tone

Mapping. Filtrul Bright Pass recunoaște pixelii care sunt prea aprinși față de luminanța

medie a unei scene. Pixelii a căror intensitate luminoasă nu depășește un anumit prag sunt

setați ca având culoarea neagră. Mai departe, se obține o imagine ce conține numai pixelii

din imaginea inițială suficient de aprinși astfel încât privirea lor să producă un halo precum

privitul spre un bec sau spre un televizor luminos, noaptea. Acest halo se obține aplicând,

asupra acestei imagini produse de filtrul Bright Pass, două treceri succesive a filtrului

Gaussian Blur ce împrăștie pixelii aprinși în jur, simulând astfel efectul de orbire. Rezultatul

până acum este o imagine ce conține doar zonele luminoase din scenă și halo-urile din jurul

acestora.

În final, filtrul de Tone Mapping ce modifică tonul imaginii se aplică imaginii inițiale

peste al cărui rezultat se adaugă imaginea obținută din filtrul Gaussian Blur. Filtrul de

modificare a tonului imaginii comprimă toți pixelii din intervalul [0, în intervalul

[0,1], pentru a putea fi afișați pe ecran. Această comprimare nu se face liniar, ci după niște

ecuații complexe ce încearcă să simuleze percepția umană asupra culorilor întunecate,

medii, sau luminoase.

Imaginea inițială Calculul

luminanței Adaptarea la

lumină

Input pentru:

•Bright Pass

•Tone Mapping

Imaginea inițială Filtru Bright

Pass

Gaussian Blur

Orizontal

Gaussian Blur Vertical

Tone Mapping

Imaginea Finală

Page 21: Raytracing Distribuit

20

Despre calculul luminanței medii și maxime a unei imagini, cât și despre fiecare din

aceste filtre se va discuta în capitolele următoare.

5.3. Metode de stocare a valorilor ce depășesc intervalul [0,1]

Calitatea maximă a unei imagini RGB se obține atunci când aceasta este memorată

într-o zonă de memorie cu 3 canale de tip float, având în total 3 x 32biți = 96biți pentru

reprezentarea fiecărui pixel. Pe lângă faptul că 96 de biți reprezintă o mare risipă de spațiu,

o astfel de reprezentare este extrem de lentă deoarece prezintă probleme mari la alinierea

în memorie, făcând . Un alt motiv pentru care nu am folosit această reprezentare provine

din arhitectura programului, fiind necesar ca transferurile de date între calculatoare să fie

cât mai mici și cât mai puține, cu scopul de a minimiza timpul de așteptare pe rețea.

Una din soluțiile găsite este aceea de a folosi formatul RGBE, cunoscut ca și Radiance

RGBE, descris pentru prima dată de către Greg Ward. El presupune folosirea a 32 de biți per

pixel, câte 8 biți pentru fiecare canal al imaginii. Avantajele presupun o bună aliniere a

datelor in memorie și utilizarea eficientă a spațiului de memorie. RGBE stochează un

exponent comun in canalul alpha, iar mantisele culorilor în canalele RGB. Acest format

codifică valorile în virgulă mobilă obținute prin randare prin împărțirea celorlalte două

canale la cea mai mare valoare dintre cele trei canale. Astfel se obțin trei valori între 0.0 și

1.0, una din ele fiind chiar 1.0. Factorul comun la care s-au împărțit valorile celor trei canale

se memorează ca exponent in canalul alpha, urmând ca la citirea din memorie a culorii unui

pixel, aceasta să fie reconstruită prin multiplicarea valorilor RGB cu valoarea din canalul

alpha. Rezultatele obținute au fost în general bune, cu excepția cazurilor când unul dintre

canalele imaginii avea o intensitate mare a culorii relativ la celelalte două canale, pierzându-

se astfel precizie în reprezentare datorită folosirii unui exponent comun. Prin împărțirea

unei valori mici la o valoare mare și apoi memorarea acesteia într-un byte, la reconstruirea

culorii se pierdea foarte mult din precizie.

În locul folosirii reprezentării RGBE, Greg Ward a venit cu ideea unei noi reprezentări,

numită LogLUV, ce folosește spațiul de culori CIE 1976 (L*, u*, v*), prescurtat CIELUV. În

colorimetrie, spațiul de culori CIELUV a fost adoptat de către Comisia Internațională de

Iluminare (CIE) in anul 1976, cu scopul de produce uniformitate perceptuală între culori. Pe

scurt, acest spațiu se obține din spațiul RGB printr-o transformare liniară, iar canalele LUV

Page 22: Raytracing Distribuit

21

ale sale reprezintă L—intensitatea luminoasă a unui pixel, iar UV—crominanța unui pixel.

Deoarece oamenii disting mult mai greu diferențele de culoare decât diferențele de

luminanță, această reprezentare cu două canale pentru informația de culoare este ideală.

Astfel, componenta de luminanță devine cel mai important purtător de informație și este

componenta pentru care ochiul uman este cel mai sensibil. Din această cauză, LogLUV alege

o reprezentare pe 32 de biți, unde canalul L are 16 biți și reprezintă logaritmul in baza 2 a

canalului L din spatiul CIELUV, iar canalele U si V au câte 8 biți. Memorarea logaritmului lui L

creează o compresie mai bună a valorilor de luminanță.

Codul pentru transformările din spațiul RGB în LogLUV și din LogLUV în RGB este

următorul:

// M matrix, for encoding const static float3x3 M = float3x3( 0.2209, 0.3390, 0.4184, 0.1138, 0.6780, 0.7319, 0.0102, 0.1130, 0.2969); // Inverse M matrix, for decoding const static float3x3 InverseM = float3x3( 6.0013, -2.700, -1.7995, -1.332, 3.1029, -5.7720, 0.3007, -1.088, 5.6268); float4 RGBtoLOGLUV (float3 vRGB) {

float4 vResult; float3 Xp_Y_XYZp = mul(vRGB, M); Xp_Y_XYZp = max(Xp_Y_XYZp, float3(1e-6, 1e-6, 1e-6)); vResult.xy = Xp_Y_XYZp.xy / Xp_Y_XYZp.z; float Le = 2 * log2(Xp_Y_XYZp.y) + 127; vResult.w = frac(Le); vResult.z = (Le - (floor(vResult.w*255.0f))/255.0f)/255.0f; return vResult;

} float3 LOGLUVtoRGB (float4 vLogLuv) {

float Le = vLogLuv.z * 255 + vLogLuv.w; float3 Xp_Y_XYZp; Xp_Y_XYZp.y = exp2((Le - 127) / 2); Xp_Y_XYZp.z = Xp_Y_XYZp.y / vLogLuv.y; Xp_Y_XYZp.x = vLogLuv.x * Xp_Y_XYZp.z; float3 vRGB = mul(Xp_Y_XYZp, InverseM); return max(vRGB, 0);

}

Page 23: Raytracing Distribuit

22

5.4. Corecția gamma

Televizoarele și monitoarele cu tub catodic au o oarecare deficiență: intensitatea

unui pixel ( ) generat cu o rază de electroni nu variază liniar în raport cu tensiunea

semnalului de alimentare ( ), ci după o lege matematică de putere,

. La dispozitivele

LCD nu apare acest fenomen, dar pentru a păstra o anumită standardizare, conțin cipuri

pentru simularea fenomenului ce se petrece la tubul catodic. Pentru a contracara

fenomenul, filmele și imaginile ce sunt redate de aceste dispozitive sunt codificate în spațiul

invers,

, unde este voltajul sursă, iar este voltajul corectat. În mod uzual,

televizoarele și monitoarele folosesc valoarea . Acest lucru intervine în mod negativ în

algoritmii de post-procesare a imaginilor deoarece intensitatea observată a unui pixel ce are

valorea de 0.5 nu reprezintă jumătate din intensitatea observată a culorii albe. Astfel,

pentru a măsura corect luminanța unui pixel, necesară în algoritmul HDR, trebuie ca

texturile să fie transformate în spațiul liniar pentru a putea fi corect interpretate.

Deoarece derivata funcției este pentru iar a funcției inverse este , în

anul 1996, Microsoft și HP au creat spațiul de culori sRGB, pentru a fi folosit de monitoare,

imprimante și pe internet. Transformarea în și din acest spațiu are o regiune liniară în

apropierea lui și aproximează foarte bine curba , eliminând astfel problemele

precedente.

{

( ) ⁄

{

(

)

Page 24: Raytracing Distribuit

23

Valoarea aleasă de Microsoft si HP pentru variabila este 0.055. Mai departe este

prezentat codul implementat și optimizat vectorial:

float4 SRGBtoLinear(float4 Color) {

return ((Color <= (float4)0.03928f) ? (Color / 12.92f) : pow((Color + 0.055f) / 1.055f, (float4)2.4f)); } float4 LinearToSRGB(float4 Color) {

return (Color <= (float4)0.00304f) ? (Color * 12.92f) : (1.055f * pow(Color, (float4)(1.0f/2.4f)) - 0.055f); }

5.5. Calculul luminanței scenei

Pentru a măsura luminanța scenei, sunt implicați doi pași. În primul rând, luminanţa

din întreaga scenă se calculează pentru fiecare pixel din imagine, iar apoi se calculează atât

maximul cât și media aritmetică a valorile de luminanţă ale pixelilor. Preluarea valorilor de

luminanță a unui pixel se face folosind produsul scalar:

( ) ( ) ( )

deoarece luminozitatea măsurată și normalizată a culorilor primare RGB (1,0,0), (0,1,0) și

(0,0,1) este 0.2125, 0.7154, respectiv 0.0721. Erik Reinhard et. all au dezvoltat o metodă

care permite însumarea valorilor de luminanță pentru toată scena, atât corect din punct de

vedere perceptual, cât și precis din punct de vedere matematic, deoarece simpla mediere a

valorilor de luminanță ar fi dus la imprecizii matematice pentru scenele puternic luminate.

Medierea luminanței pentru toată scena se face după formula:

(

∑ ( ( ))

)

unde reprezintă numărul total de pixeli, și reprezintă coordonata pixelului curent,

este o valoare pozitivă mică pentru ocolirea singularității în punctul , iar ( ) reprezintă

luminanța pixelului calculată cu formula precedentă. La o privire mai atentă, ultima formulă

reprezintă chiar media geometrică a luminanțelor.

Page 25: Raytracing Distribuit

24

Codul OpenCL pentru calculul luminanței unei imagini, împreună cu corecția gamma:

inline float4 g10(float4 Color) { return ((Color <= (float4)0.03928f) ? Color / 12.92f : native_powr((Color + 0.055f) / 1.055f, (float4)2.4f)); } #define _USE_GAMMA_CORRECTION_ __kernel void ComputeLuminance(__global const uchar4* Image, __global float2* LuminancesOUT, const int width, const int height, __write_only image2d_t Image2, __local float2* SharedMem) { int groupID = get_group_id(0); // 4 int globalID = get_global_id(0); // width int localID = get_local_id(0); // width/4 Image += globalID; float averageLum = 0.0f; float maximumLum = 0.0f; for(int i=0; i<height; i++) { uchar4 color = *Image; color = max(color, (uchar4)(1,1,1,0)); float4 color2 = (convert_float4_rte(color)/255.0f); #ifdef _USE_GAMMA_CORRECTION_ color2 = g10(color2); #endif float Luminance; Luminance = dot(color2, weight); averageLum += native_log(Luminance); maximumLum = max(maximumLum, Luminance); int2 texCoord = (int2)(globalID, i); write_imagef(Image2, texCoord, color2); Image += width; } averageLum = native_exp(averageLum / height); SharedMem[localID] = (float2)(averageLum, maximumLum); barrier(CLK_LOCAL_MEM_FENCE); for(int s=get_local_size(0)/2; s>32; s>>=1) { if(localID < s) { int index1 = localID + s;

Page 26: Raytracing Distribuit

25

SharedMem[localID].lo = native_exp((native_log(SharedMem[localID].lo) + native_log(SharedMem[index1].lo)) * 0.5f); SharedMem[localID].hi = max(SharedMem[localID].hi, SharedMem[index1].hi); } barrier(CLK_LOCAL_MEM_FENCE); } if(localID < 32) { int8 indexes = (int8)localID + (int8)(32,16,8,4,2,1,0,0); SharedMem[localID].lo = native_exp((native_log(SharedMem[localID].lo) + native_log(SharedMem[indexes.s0].lo)) * 0.5f); SharedMem[localID].hi = max(SharedMem[localID].hi, SharedMem[indexes.s0].hi); SharedMem[localID].lo = native_exp((native_log(SharedMem[localID].lo) + native_log(SharedMem[indexes.s1].lo)) * 0.5f); SharedMem[localID].hi = max(SharedMem[localID].hi, SharedMem[indexes.s1].hi); SharedMem[localID].lo = native_exp((native_log(SharedMem[localID].lo) + native_log(SharedMem[indexes.s2].lo)) * 0.5f); SharedMem[localID].hi = max(SharedMem[localID].hi, SharedMem[indexes.s2].hi); SharedMem[localID].lo = native_exp((native_log(SharedMem[localID].lo) + native_log(SharedMem[indexes.s3].lo)) * 0.5f); SharedMem[localID].hi = max(SharedMem[localID].hi, SharedMem[indexes.s3].hi); SharedMem[localID].lo = native_exp((native_log(SharedMem[localID].lo) + native_log(SharedMem[indexes.s4].lo)) * 0.5f); SharedMem[localID].hi = max(SharedMem[localID].hi, SharedMem[indexes.s4].hi); SharedMem[localID].lo = native_exp((native_log(SharedMem[localID].lo) + native_log(SharedMem[indexes.s5].lo)) * 0.5f); SharedMem[localID].hi = max(SharedMem[localID].hi, SharedMem[indexes.s5].hi); } if(localID==0) LuminancesOUT[groupID] = SharedMem[0]; }

5.6. Adaptarea la lumină

Deoarece măsurăm luminanța pentru fiecare cadru randat, această informație poate

fi refolosită pentru a mima procesul de adaptare a luminii primite de către ochiul uman.

Adaptarea la lumină apare atunci când se petrec modificări de la întuneric la lumină, sau de

la lumină la întuneric. De exemplu, fenomenul se petrece atunci când părăsim o cameră

întunecată și mergem într-o zonă în aer liber foarte luminoasă, și vice-versa.

Retina conține două tipuri de fotoreceptori, bastonașe și conuri, în număr de aprox.

120 de milioane, respectiv 7 milioane. Bastonașele sunt mai sensibile la lumină decât

conurile, dar nu disting culorile.

Vederea de pe timpul zilei (cea datorată conurilor) se adaptează mult mai rapid la

schimbarea nivelului de lumină, ajustându-se la o schimbare de la aer liber la interiorul

Page 27: Raytracing Distribuit

26

întunecat în doar câteva secunde. Bastonașele sunt responsabile de vederea nocturnă sau

scotopică. Ele sunt de mai mult de o mie de ori mai sensibile decât conurile și pot fi

declanșate chiar de fotoni individuali, în condiții optime. Vederea optimă adaptată la noapte

se obține doar după o perioadă îndelungată de întuneric de aproximativ 30 de minute,

deoarece procesul de adaptare al bastonașelor la schimbările de lumină este mult mai încet

decât cel al conurilor.

Într-un scenariu de simulare a acestor efecte de către un program de post-procesare,

nu are sens să adăugăm o secvență de 30 de minute de adaptare a luminii atunci când se

micșorează luminanța medie a imaginilor obținute. Prin urmare, orice aproximare trebuie sa

fie foarte largă, deoarece scopul principal al post-procesării în lucrarea de față este cel de

îmbunătățire a imaginii rezultate, iar abia cel secundar este aproximare a percepției vizuale

umane.

Pentru a simula reacția ochilor la schimbările temporale ale condițiilor de iluminare,

luminanța medie este înlocuită de luminanța adaptată în ecuațiile în care aceasta apare. În

funcție de luminanța medie, luminanța adaptată trebuie să se modifice, urmărind-o pe cea

medie. În cazul în care condițiile de iluminare devin stabile, și luminanța adaptată ar trebui

sa o egaleze pe cea medie, reprezentând momentul când ochii sunt pe deplin ajustați la

condițiile de iluminare. Acest comportament poate fi obținut printr-o funcție de descreștere

exponențială ca argument al funcției de interpolare între luminanța adaptată si cea medie,

actuală:

( ) ( ) ( ( ))( )

Formula îi aparține lui Pattanaik, este timpul măsurat în secunde între cadrele

randate, iar este rata de adaptare. Cum rata de adaptare a conurilor diferă de cea a

bastonașelor, și deoarece n-am vrut să adăugăm o întârziere de chiar 30 de minute, am

considerat ca bastonașele au aceeași rată de adaptare precum a conurilor. O valoare

aproximativă de 0.2-0.4 dă rezultate bune.

În implementare, întârzierea adaptării la lumină am realizat-o din threadul

programului care se ocupă cu post-procesarea, deoarece acest calcul este serial și nu are

sens să fie lansat un kernel OpenCL pentru acesta.

Page 28: Raytracing Distribuit

27

5.7. Tone Mapping

Cunoscând luminanța medie a imaginii, luminanța pixelului curent poate fi scalată în

funcție de intensitatea luminoasă a gri-ului mediu, în felul următor:

Următoarea figură vizualizează ecuația precedentă pentru valori ale luminanței

medii între 0 și 1, iar o luminanță a gri-ului mediu de 0.18:

Aplicând această ecuație, se observă că pixelii ce au aceeași luminanță precum cea

medie, vor fi mapați la luminanța gri-ului mediu. În funcție de momentul zilei și de condițiile

meteorologice, valoarea gri-ului mediu trebuie aleasă diferit. Asta se poate întâmpla prin

intermediul valorilor fixe ce depind de oră și condițiile de iluminare, sau se poate petrece

dinamic. O luminanță medie scăzută presupune o iluminare nocturnă, deci și o valoare mai

mică a gri-ului mediu, simulând culorile închise de pe timpul nopții. Krawczyk folosește

următoarea funcție pentru a genera o valoare de gri mediu în mod dinamic, pentru filme:

( )

Page 29: Raytracing Distribuit

28

Această funcție pur și simplu îmbină un set de valori cheie care au fost asociate

empiric cu diferite valori de luminanță.

Atunci când luminanța medie a imaginii este foarte mică, luminanțele pixelilor afișati

pot foarte ușor să depășească gama de [0,1] pe care un monitor o poate afișa. Dacă ne

uităm la ecuația de mai sus, pentru și , adică pentru o

imagine relativ întunecată ce conține un pixel gri pe jumate aprins, valoarea transformată a

acestuia va depăși valoarea 1.

Pentru a comprima luminanțele de ieșire ale pixelilor în gama [0,1] a monitorului,

această luminanță este introdusă într-o funcție de tipul ( )

, care transformă axa

reală în intervalul [0,1):

Această funcție scalează valorile mici liniar, în timp ce valorile mari ale luminanței

sunt comprimate foarte mult. Funcția are o asimptotă orizontală la 1, ceea ce înseamnă că

orice valoare a luminanței scalate va deveni mai mică decât 1, ceea ce o face favorabilă

afișării pe monitor, precum arată graficul următor:

Page 30: Raytracing Distribuit

29

Totuși, în practică, imaginea de intrare nu conține valori extreme ale luminanței, iar

imaginea de ieșire nu va ajunge niciodată să aibă pixeli complet aprinși. În adăugare, este

artistic dorit să lași pixelii să se aprindă complet intr-o manieră controlată. Reinhard obține

acest efect prin adăugarea unui nou termen ecuației precedente de comprimare a

luminanței de ieșire în intervalul [0,1], producând următorul operator de mapare a tonului

imaginii (tone mapping):

(

)

Această ecuație introduce un nou parametru, , care reprezintă cea mai mică

valoare a unui pixel ce va fi mapată la alb, luminanță 1. În mod implicit, acest parametru

este setat ca fiind maximul luminanței scenei:

( )

Cu alte cuvinte, acest termen face ca intervalul de luminanțe *0, + să fie mapat

în intervalul [0,1]. Figurile următoare afișează ecuația de tone mapping cu o valoare de

, o gamă de luminanțe ale pixelilor individuali de [0,4], și valori pentru

, în prima poză, respectiv în cea de-a doua:

Page 31: Raytracing Distribuit

30

5.8. Filtrul Bright-Pass

Filtrul de Bright-Pass este un filtru trece-sus, iar pentru a fi în concordanță cu

algoritmul de tone mapping, acesta utilizează operatorul de tone mapping al lui Reinhard

pentru a comprima zonele întunecate ale imaginii, astfel încât imaginea rezultată să conțină

numai zonele luminoase:

(

(

) )

Figura următoare prezintă rezultatul acestui operator, cu valorile

și :

Page 32: Raytracing Distribuit

31

Parametrul al operatorului de bright-pass mută întreaga curbă in direcția

–y, verticală, în timp ce parametrul schimbă curbura graficului. Mărind valorea lui

face curba să devină mai abruptă, ceea ce înseamnă că este mai sensibilă la

schimbările de luminanță, în timp ce reducerea valorii face ca filtrul să devină mai puțin

sensibil. Codul OpenCL corespunzător filtrului:

#define fExposure 0.5f #define Threshold 0.5f #define Offset 1.0f __kernel void ComputeBrightPass(__read_only image2d_t Image, __write_only image2d_t Image2, const int width, const int height, const float averageLum, const float maximumLum ) { int2 texCoord = (int2)(get_global_id(0), get_global_id(1)); const sampler_t sampler = CLK_NORMALIZED_COORDS_FALSE | CLK_ADDRESS_CLAMP_TO_EDGE |

CLK_FILTER_NEAREST; float AutoExposure = 1.03f - 2.0f / (2.0f + native_log10(averageLum+1.0f)); float4 color = read_imagef(Image, sampler, texCoord); float Le = dot(color, weight);

float scaleLum = Le * fExposure * AutoExposure / averageLum; float LThresh = max((scaleLum * (1.0f + (scaleLum / (maximumLum * maximumLum)))) /

(1.0f + scaleLum) - Threshold, 0.0f); float LBrightPass = LThresh / (Offset + LThresh); float4 output = color * LBrightPass/Le; write_imagef(Image2, texCoord, output); }

5.9. Gaussian Blur

Filtrul Gaussian Blur este un filtru de mediere bazat pe un algoritm simplificat

dezvoltat de Gauss și este folosit pentru a estompa și a împrăștia pixelii rămași aprinși ca

rezultat al filtrului Bright-Pass. În figura următoare, planul orizontal reprezintă pixelii iar

planul vertical reprezintă ponderile asociate acestora:

Page 33: Raytracing Distribuit

32

Acest filtru estompează imagini 2D prin eșantionarea unei vecinătăți circulare a

fiecărui pixel din imaginea de intrare și calculează media ponderată a acestora:

( )

unde și reprezintă coordonatele relative a pixelului eșantionat din acea vecinătate, față

de pixelul din centrul vecinătății, este deviația standard, iar ( ) reprezintă ponderea

asociată acelui pixel.

Filtrul poate fi rearanjat și făcut separabil, așa cum se arată mai jos:

( ) (

) (

)

Page 34: Raytracing Distribuit

33

Factorizând ecuația in acest mod ne permite să calculăm acest filtru prin două

operații de filtrare succesive ce operează în câte o dimensiune, într-un mod extrem de

eficient. De exemplu, pentru un filtru 11x11, numărul total de citiri din memorie al filtrului

inițial este 121, dar folosind filtrul separabil și aplicândul-l câte o dată pentru fiecare

dimensiune a imaginii, același rezultat se obține făcând doar 22 de citiri, o reducere cu 82%

a muncii procesorului. În imaginea următoare sunt prezentate cele două operații:

Mai jos este prezentat codul OpenCL corespunzător filtrului Gaussian Blur, optimizat

folosind memorie locală. SampleOffsets este un vector de SampleCount elemente ce

reprezintă coordonatele de textură ce vor fi citite și mediate de filtru, iar SampleWeights

reprezintă ponderile asociate acelor eșantioane. Coordonatele de textură sunt date într-o

structură de float2, și descriu coordonate relative față de pixelul curent, în planul 2D. Am

ales această reprezentare pentru a putea să refolosesc același kernel OpenCL atât pentrul

filtrul orizontal, cât și pentru cel vertical.

#define SampleCount 11 __kernel void ComputeGaussianBlur(__read_only image2d_t Image, __write_only image2d_t Image2, const int width, const int height, __global const float2* SampleOffsets, __global const float* SampleWeights ) { int2 texCoord = (int2)(get_global_id(0), get_global_id(1)); float2 NtexCoord = convert_float2(texCoord)/((float2)(width-1, height-1));

Page 35: Raytracing Distribuit

34

int localID = mul24(get_local_id(1), get_local_size(0)) + get_local_id(0); const sampler_t sampler = CLK_NORMALIZED_COORDS_TRUE |

CLK_ADDRESS_CLAMP_TO_EDGE | CLK_FILTER_LINEAR;

__local float2 sampleOffsets[SampleCount]; __local float sampleWeights[SampleCount]; if(localID < SampleCount) { sampleOffsets[localID] = SampleOffsets[localID]; sampleWeights[localID] = SampleWeights[localID]; } barrier(CLK_LOCAL_MEM_FENCE); float4 color = read_imagef(Image, sampler, texCoord) * sampleWeights[0]; #pragma unroll for(int i=0; i<SampleCount; i++) { float2 tc = NtexCoord + sampleOffsets[i]; color += read_imagef(Image, sampler, tc) * sampleWeights[i]; } write_imagef(Image2, texCoord, color); }

5.10. Imaginea finală

Imaginea finală se obține pur și simplu făcând suma dintre culorile pixelilor obținute

după Tone Mapping și după Gaussian Blur. Perceptual, crearea imaginii finale implică

fenomenul de adaptare a ochiului la condițiile de iluminare (în Tone Mapping), ca apoi să i

se adauge halo-urile luminoase, rezultate după filtrul Gaussian Blur.

În implementare, am folosit o extensie a OpenCL care permite partajarea bufferelor

proprii OpenCL cu cele ale DirectX 10. Acest lucru permite optimizarea transferurilor de date

pe magistrala PCI-Express, pe care este conectată placa video. Mai precis, se creează un

buffer imagine de către DirectX, care apoi este preluat de către OpenCL.

Fără această partajare a bufferelor, imaginea obținută după post-procesare, și care

ar fi fost stocată în memoria plăcii video, ar fi trebuit copiată in memoria RAM, ca apoi să fie

copiată într-un buffer imagine al DirectX pentru a putea fi afișată pe ecran, în total două

copieri inutile pe magistrala PCI-Express. Optimizarea constă în randarea post-procesării

direct in bufferul imagine al DirectX, fără a mai implica niciun transfer suplimentar.

Performanța întregii post-procesări, obținută pe o placă video NVidia GeForce 8800GT peste

Page 36: Raytracing Distribuit

35

o magistrală PCI-E2.0, a fost de 40 milisecunde fără optimizarea precedentă. Optimizat,

toată post-procesarea durează doar 7 milisecunde.

O a doua optimizare, mai mică după părerea mea, este ca imaginea finală să fie

calculată în kernelul OpenCL dedicat celui de Tone Mapping, și pe care l-am numit

ComputeHDR. Decât să mai creez un nou kernel care citește din două imagini pentru a scrie

rezultatul sumei lor în a treia, am folosit kernelul de Tone Mapping, căruia i-am mai adăugat

un parametru și linia de cod color += bloom;.

Codul OpenCL:

__kernel void ComputeHDR(__read_only image2d_t OriginalImage, __read_only image2d_t BlurredImage, __write_only image2d_t Output, const int width, const int height, const float averageLum, const float maximumLum ) { int2 texCoord = (int2)(get_global_id(0), get_global_id(1)); float2 NtexCoord = convert_float2(texCoord)/((float2)(width-1, height-1)); const sampler_t samplerBlurred = CLK_NORMALIZED_COORDS_TRUE |

CLK_ADDRESS_CLAMP_TO_EDGE | CLK_FILTER_LINEAR;

const sampler_t samplerOriginal = CLK_NORMALIZED_COORDS_FALSE | CLK_ADDRESS_CLAMP_TO_EDGE |

CLK_FILTER_NEAREST; float4 color = read_imagef(OriginalImage, samplerOriginal, texCoord); float4 bloom = read_imagef(BlurredImage, samplerBlurred, NtexCoord); float AutoExposure = 1.03f - 2.0f / (2.0f + native_log10(averageLum+1.0f)); float PixelLuminance = dot(color, weight);

float Lp = PixelLuminance * (AutoExposure / averageLum) * fExposure; float Lum = Lp / (1.0f + Lp); float toneScalar = Lum * ( 1.0f + ( Lp / ( maximumLum * maximumLum ) ) ); color *= toneScalar/PixelLuminance; color += bloom; write_imagef(Output, texCoord, color); }

Page 37: Raytracing Distribuit

36

6. Evaluarea performanțelor

Per ansamblu, performanța programului este cea așteptată. Într-o conexiune peste

internet, un server plus un client, speedup-ul obținut variază între 1.3 și 1.7 în funcție de

distribuirea obiectelor în cadrul scenei. El nu este mai mare deoarece viteza de transmisie

între cele două calculatoare a fost mică.

Urmează să testăm aplicația în laboratorul de grafică deoarece rețeaua Gigabit la

care sunt conectate calculatoarele este mai promițătoare decât o conexiune internet

realizată peste două ISP-uri diferite.

În cadrul aceluiași calculator, viteza de randare pe o placă grafică NVidia GeForce

8800GT este de în medie de 10 ori mai mare față de procesorul AMD Athlon X2 4200+.

0

100

200

300

400

500

Durata rulării pe uncalc. (ms)

Durata rulării în rețea, 2 calc. (ms)

NVidia 8800GT

NVidia GTX260

Media timpilor de rulare

1

1.1

1.2

1.3

1.4

1.5

1.6

1.7

1.8

1 calculator 2 calculatoare

Test 1

Test 2

Test 3

Test 4

Test 5

Test 6

Test 7

Test 8

Speedups obținute la diferite teste

Page 38: Raytracing Distribuit

37

Referitor la post-procesare, implementarea a fost făcută într-un mod cât mai optim,

iar pe plăcile video NVidia GeForce 8800GT și NVidia GeForce GTX260 timpii de rulare sunt

identici, de 7 milisecunde. Aceste plăci video suportă calcule în virgulă mobilă de precizie

redusă, mai exact, folosind tipul de date half, reprezentat pe 16 biți. El este arhisuficient

pentru reprezentarea gamelor de luminanță posibile. Cu toate acestea, varianta actuală de

OpenCL 1.0 nu suportă calcule cu astfel de tipuri de date. Specificația noului OpenCL 1.1

prevede posibilitatea calculelor cu tipul de date half, ceea ce ar putea introduce o nouă

optimizare. Singura problemă este că la momentul realizării aplicației nu au apărut drivere

video care să suporte OpenCL 1.1.

7. Concluzii

Deoarece algoritmul de RayTracing este foarte ușor paralelizabil, o astfel de

abordare este de dorit. Chiar dacă atribuirea sarcinilor fiecărui calculator în parte nu a fost

făcută foarte eficient, într-o rețea cu latență mică și viteză mare de transfer, distribuirea

muncii pe mai multe calculatoare nu poate aduce decât beneficii. O modificare viitoare a

algoritmului de atribuire a sarcinilor va ține cont și de viteza de transfer pe rețea, pentru a

se putea utiliza eficient la randare și calculatoarele conectate prin internet, nu doar rețea

locală.

Metodele și filtrele prezentate la post-procesare pot fi eficient puse în aplicare pe

plăcile grafice curente, obținând o rulare în timp real. Filtrele au fost implementate folosind

platforma OpenCL și integrate într-un motor grafic distribuit ce folosește RayTracing pentru

randare. Timpii de rulare a post-procesării sunt foarte mici, în jurul valorii de 7 milisecunde

pe o placă video NVidia GeForce 8800GT, insesizabil față de cât durează randarea propriu-

zisă dar permițând un contrast ridicat în imaginile obținute, cu detaliile de imagine

conservate și efecte perceptuale bine simulate.

OpenCL permite folosirea plăcii video pentru rularea eficientă a unui algoritm de

RayTracing cât și a post-procesării imaginilor, ceea ce face această din platformă un bun

candidat pentru integrarea RayTracing-ului și post-procesării intensiv computaționale în

jocurile pe calculator.

Page 39: Raytracing Distribuit

38

Bibliografie Börjesson, Martin. 2006. High Dynamic Range Rendering. s.l. : Research Results, 2006.

Durand, F. and Dorsey, J. 2000. Interactive tone mapping. s.l. : Eurographics Workshop on

Rendering, 2000. pp. 219 - 230.

Erik, Reinhard. 2002. Parameter estimation for photographic tone reproduction. s.l. : Journal of

Graphics Tools, 2002.

Erik, Reinhard, et al. 2005. High Dynamic Range Imaging. s.l. : Morgan Kaufman, 2005.

Erik, Reinhard, et al. 2002. Photographic tone reproduction for digital images. s.l. : Proceedings of

SIGGRAPH, 2002.

F., Drago, et al. 2003. Adaptive Logarithmic Mapping For Displaying High Contrast Scenes. 2003.

Greg, Ward. 1994. A contrast based scalefactor for luminance display. Boston : Graphics Gems IV.

Academic Press., 1994.

J.A., Ferweda, et al. 1996. A model of visual adaptation for realistic image synthesis. s.l. :

Proceedings of SIGGRAPH, 1996. pp. 249-258.

K., Owen. Overcoming Display Limitations in Real Time Systems. 2006 : s.n.

Khronos OpenCL Working Group. OpenCL Specification, version 1.0, revision 48.

Krawczyk, et al. Perceptual Effects in Real-time Tone Mapping.

2009. NVIDIA OpenCL Best Practices Guide, version 1.0. 2009.

Wikipedia. http://en.wikipedia.org/wiki/High_dynamic_range_imaging.

—. http://en.wikipedia.org/wiki/High_dynamic_range_rendering.

—. http://en.wikipedia.org/wiki/OpenCL.

—. http://en.wikipedia.org/wiki/Ray_tracing_(graphics).