Table of Contents

Tema 2 - Bokeh Blur

Descriere generală

Scopul acestei teme va fi implementarea unui efect de blur de profunzime (Depth of Field / Bokeh Blur) aplicat unei imagini 2D (similar cu cel propus de cei de la Electronic Arts), în care zona de focus va fi controlabilă interactiv de către utilizator.

Puteți studia în următorul videoclip o posibilă implementare a cerințelor.

Efectul urmărit este inspirat din tehnicile folosite în motoare grafice moderne (de exemplu, Frostbite), unde zonele din afara planului de focus sunt estompate folosind kernel-uri de blur gaussian (sau combinații de mai multe gaussiene), pentru a simula comportamentul unei lentile reale.

În cadrul temei trebuie:

Efectul final trebuie să arate ca un blur radial: zona din jurul cursorului este clară, iar blurul crește progresiv odată cu distanța față de aceasta.

Arhitectura generală

În cadrul temei, puteți alege dacă doriți să implementați pe CPU sau pe GPU (considerat bonus). În cazul implementării pe GPU, pentru a obține efectul dorit se vor folosi două treceri (2 pass-uri) de procesare pe GPU, pe un quad randat pe tot ecranul, astfel:

  1. Primul pass: blur gaussian orizontal și salvarea valorii de blur;
  2. Al doilea pass: blur gaussian vertical și compunerea rezultatului final.

În plus, se va folosi un shader simplu pentru afișarea texturii procesate și pentru desenarea zonelor de focus (cercurile concentrice).

Pașii principali ai algoritmului sunt următorii:

  1. Se încărcă imaginea inițială (ca textură în cazul implementării pe GPU).
  2. Prima etapă de procesare (primul pass în cazul implementării pe GPU):
    • Se calculează, pentru fiecare pixel, distanța până la punctul de focus;
    • Se determină „cantitatea de blur” (blurAmount) pe baza acestei distanțe;
    • Se aplică un blur gaussian orizontal, cu o rază care depinde de blurAmount;
    • Se procesează 2 imagini (în cazul implementării pe GPU, se scrie în două texturi):
      • textura 0: rezultatul blurului orizontal;
      • textura 1: culoarea originală + blurAmount stocat în canalul alfa (normalizat).
  3. A doua etapă de procesare (al doilea pass în cazul implementării pe GPU):
    • Se citește blurAmount din canalul alfa al imaginii (texturii) cu culoarea originală;
    • Se utilizeazăblurAmount pentru a recalcula dimensiunea măștii de filtrare;
    • Se aplică un blur gaussian vertical peste rezultatul orizontal;
    • Se scrie rezultatul final într-o imagine (textură) de output.
  4. Afișare:
    • Se afișează fie imaginea originală, fie un rezultat intermediar, fie imaginea procesată;
    • Se desenează cercuri concentrice în jurul punctului de focus, pentru a marca vizual zona clară a imaginii.

În cadrul pasului II veți obține imaginea finală.

Aveți deja toate elementele necesare calculate la pasul I.

Trebuie să aplicați filtrul de blur pereche față de cel folosit la pasul I. Spre exemplu se aplică blur vertical în situația în care s-a început cu cel orizontal.

Sursa este imaginea de la pasul anterior, iar intensitatea de blur a fost deja calculată și trebuie doar accesată.

Puteți începe cu oricare dintre cele două tipuri de blur. Varianta de mai sus pornește cu filtrul de blur orizontal.

Calculul cantității de blur

Punctul de focus este dat de poziția cursorului (mousePos) în coordonate ecran. Pentru un pixel cu poziția $(x, y)$, convertită în coordonate ecran, se calculează distanța:

$$ dist = \left\| (x, y) - mousePos \right\| $$

Se definește un parametru focusRadius:

Un model simplu pentru calculul blurAmount ar putea fi:

$$ normalizedDist = \frac{dist - focusRadius}{focusRadius \cdot 2.0} $$

$$ blurAmount = clamp(normalizedDist, 0.0, 1.0) $$

Raza efectivă a blurului este:

$$ dynamicBlurRadius = int(MAX\_BLUR\_RADIUS \cdot blurAmount) $$

unde MAX_BLUR_RADIUS este o constantă (de exemplu, 40).

Filtru de netezire separabil (blur gaussian)

Pentru blur se folosește un filtru gaussian separabil (ca aici):

Aceasta reduce complexitatea de la $O(N^2)$ la $O(2N)$ pentru un kernel de dimensiune $N$.

Exemplu de kernel gaussian 1D:

$$ G(x) = \frac{exp\left(-0.5 \cdot \frac{x^2}{\sigma^2}\right)}{\sqrt{2\pi} \cdot \sigma} $$

Raza blurului și $\sigma$ se pot raporta la blurAmount:

În implementare se poate folosi un singur kernel gaussian sau o combinație de mai multe gaussiene cu $\sigma$ diferite și ponderi diferite, pentru un efect de bokeh mai interesant (de exemplu: 3 gaussiene cu $\sigma$ mic, mediu și mare).

Începem cu imaginea originală:

Primul pas este efectuarea unei neteziri pe orizontală. Fiecare pixel va fi egal cu media aritmetică a pixelului și a vecinilor pe aceeași linie cu pixelul.

Rezultatul este acesta:

Al doilea pas este efectuarea a unei neteziri pe verticală. Fiecare pixel va fi egal cu media aritmetică a pixelului și a vecinilor pe aceeași coloană cu pixelul.

Rezultatul este acesta:

Model de bokeh / blur radial

Pentru a obține un efect de bokeh / blur radial:

Opțional, se poate implementa combinarea mai multor gaussiene pentru a simula forme de kernel mai complexe, inspirate de tehnici de tip Circular Separable Convolution Depth of Field utilizate în industria jocurilor (de exemplu, Frostbite).

Interacțiune cu utilizatorul

Poziția mouse-ului definește centrul zonei de focus, iar raza zonei de focus poate fi modificată de la tastatură. La apăsarea unui buton al mouse-ului se calculează imaginea blurată în funcție de poziția mouse-ului.

Pentru a vizualiza mai ușor zona de focus, se desenează cercuri concentrice în jurul mouse-ului, în shaderul de afișare:

Prezentare rezultat

Aplicația trebuie să permită vizualizarea rezultatelor în mai multe moduri, folosind tastatura:

În plus:

Implementare și restricții

  • Tema trebuie implementată în limbajul de programare C/C++, în situația în care se realizează o rezolvare ce se execută pe unitatea centrală de procesare, CPU, sau într-un limbaj specializat pentru implementarea pe procesorul grafic, precum GLSL.
  • NU este permisă utilizarea de biblioteci de procesare de imagine pentru implementarea efectului de blur. Efectul trebuie implementat în shaderele voastre (GLSL).
  • Se pot folosi biblioteci pentru:
    • încărcarea imaginilor (texturi);
    • managementul ferestrei și contextului OpenGL;
    • dialoguri de selectare a fișierelor etc.
  • Comportamentul interactiv (citirea input-ului de mouse/tastatură) se va realiza în C++ și se vor trimite datele relevante (mousePos, focusRadius etc.) în GLSL ca variabile uniforme.

Notare (200p)

Bonusuri posibile

Indicații suplimentare

Este indicat să folosiți framework-ul laboratorului și Visual Studio.

Pentru implementarea temei, în folderul src/lab_m2 puteți crea un nou folder, de exemplu Tema2Bokeh, cu fișierele Tema2Bokeh.cpp și Tema2Bokeh.h (pentru implementare POO, este indicat să aveți și alte fișiere).

Pentru a vedea fișierele nou create în Visual Studio în Solution Explorer:

În fișierul lab_list.h trebuie adăugată și calea către header-ul temei, de exemplu:

Arhivarea proiectului

  • În mod normal arhiva trebuie să conțină toate resursele necesare compilării și rulării.
  • Înainte de a face arhiva asigurați-vă că ați curățat proiectul Visual Studio:
    • click dreapta pe proiect în Solution ExplorerClean Solution
    • și ștergeți folderul /build/.vs (dacă nu îl vedeți, este posibil să fie ascuns)
    • SAU ștergeți complet folderul /build.
  • În cazul în care arhiva tot depășește limita de 50MB, puteți să ștergeți și folderele /deps sau /assets, întrucât se pot adăuga la testare. Nu este recomandat să faceți acest lucru întrucât îngreunează mult testarea în cazul în care versiunea curentă a bibliotecilor/resurselor diferă de versiunea utilizată la momentul scrierii temei.