SlideShare a Scribd company logo
1 of 39
Download to read offline
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
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
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ă
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
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ă
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
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
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:
( ) ( )
( ) ( ⁄ ⁄ )
( ) (( )⁄ ( )⁄ )
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
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.
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.
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
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.
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ă.
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.
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.
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
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ă
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
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ă
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
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);
}
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.
{
( ) ⁄
{( )
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.
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;
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
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.
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:
( )
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:
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:
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 :
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:
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:
( ) (
√
) (
√
)
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));
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
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);
}
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 un
calc. (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
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.
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).

More Related Content

Similar to Raytracing Distribuit

Similar to Raytracing Distribuit (20)

Prezentare proiect
Prezentare proiectPrezentare proiect
Prezentare proiect
 
ECDL Curs Formator
ECDL Curs FormatorECDL Curs Formator
ECDL Curs Formator
 
Axiologic quark
Axiologic quarkAxiologic quark
Axiologic quark
 
Microcontrollere (2001).pdf
Microcontrollere (2001).pdfMicrocontrollere (2001).pdf
Microcontrollere (2001).pdf
 
Microcontrollere (2001)
Microcontrollere (2001)Microcontrollere (2001)
Microcontrollere (2001)
 
Graduation projects in Crispico
Graduation projects in CrispicoGraduation projects in Crispico
Graduation projects in Crispico
 
Programarea independenta de platforma in C++. Qt
Programarea independenta de platforma in C++. QtProgramarea independenta de platforma in C++. Qt
Programarea independenta de platforma in C++. Qt
 
HOBBY.21-1-2009.pdf
HOBBY.21-1-2009.pdfHOBBY.21-1-2009.pdf
HOBBY.21-1-2009.pdf
 
Manual QM
Manual QMManual QM
Manual QM
 
Proiect Programare WEB
Proiect Programare WEBProiect Programare WEB
Proiect Programare WEB
 
Ciros robotics instruire
Ciros robotics   instruireCiros robotics   instruire
Ciros robotics instruire
 
Plan Proiect E.Pausan
Plan Proiect E.PausanPlan Proiect E.Pausan
Plan Proiect E.Pausan
 
Sisteme expert mps2
Sisteme expert mps2Sisteme expert mps2
Sisteme expert mps2
 
Simatic step 7 v5 prog
Simatic step 7 v5 progSimatic step 7 v5 prog
Simatic step 7 v5 prog
 
Simatic step 7 v5
Simatic step 7 v5Simatic step 7 v5
Simatic step 7 v5
 
Proiect Programare WEB
Proiect Programare WEBProiect Programare WEB
Proiect Programare WEB
 
Referat_Java_Creanga_An_II_INFO.pdf
Referat_Java_Creanga_An_II_INFO.pdfReferat_Java_Creanga_An_II_INFO.pdf
Referat_Java_Creanga_An_II_INFO.pdf
 
Amelia_proiect
Amelia_proiectAmelia_proiect
Amelia_proiect
 
Irina Cureraru
Irina CureraruIrina Cureraru
Irina Cureraru
 
Sisteme expert mps
Sisteme expert mpsSisteme expert mps
Sisteme expert mps
 

Raytracing Distribuit

  • 1. 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
  • 2. 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
  • 3. 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ă
  • 4. 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
  • 5. 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ă
  • 6. 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
  • 7. 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
  • 8. 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: ( ) ( ) ( ) ( ⁄ ⁄ ) ( ) (( )⁄ ( )⁄ )
  • 9. 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
  • 10. 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.
  • 11. 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.
  • 12. 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
  • 13. 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.
  • 14. 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ă.
  • 15. 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.
  • 16. 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.
  • 17. 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
  • 18. 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ă
  • 19. 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
  • 20. 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ă
  • 21. 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
  • 22. 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); }
  • 23. 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. { ( ) ⁄ {( )
  • 24. 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.
  • 25. 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;
  • 26. 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
  • 27. 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.
  • 28. 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: ( )
  • 29. 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:
  • 30. 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:
  • 31. 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 :
  • 32. 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:
  • 33. 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: ( ) ( √ ) ( √ )
  • 34. 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));
  • 35. 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
  • 36. 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); }
  • 37. 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 un calc. (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
  • 38. 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.
  • 39. 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).