Laboration 1

Datorgrafik: Mer shaderprogrammering.

Denna laboration tar upp ett par problem som kräver lite mer avancerade shaders än de som ni gjort i TSBK07 eller i labb 0.

Laborationen börjar med en dugga som baseras på kapitel 3-5. Läs på dessa innan labben så du får bra poäng!

Laborationsskalet

Vi startar med ett labbskal som är likt de vi använt i TSBK07 och labb 0. Aktuell version finns här:

lab1-1.tar.gz

lab1-2.tar.gz

Pga filomstuvning så finns följande för ögonblicket på två länkar:

common.tar.gz (lokal kopia för labben, skall tas bort snart)

common.tar.gz (här skall aktuell version finnas)

Makefiler ingår, separat för varje del.

Detta förutsätter följande mappstruktur: att "common" och "lab1-1" ligger i samma överliggade mapp, så att man refererar till "common" som ../common.

Laborationen har två huvuddelar: High Dynamic Range och bump mapping.

Vi arbetar i Linux, mest i labbet Southfork, med GeForce GTX 660Ti-kort. Detta är relativt moderna kort med goda prestanda. Ni kan också använda Olympen, som har acceptabla kort om än lite äldre.

1. Blooming med high dynamic range

Denna laboration är omfattande i antalet steg, men de flesta steg är tämligen enkla. Vi skall försöka skapa en blooming-effekt i en enkel scen. Målet med laborationen är att få en inblick i hur man arbetar med bildfiltreringar, rekursiva shaders och flyttalsbuffrar.

Många av de shaders som skall skrivas har visats på föreläsning 4, så ni kan använda föreläsningsmaterialet som inspirationskälla. Dock kommer dessa inte alltid att fungera precis som givna i föreläsningen. Vissa inparametrar har andra namn, och de täcker inte alla filterfunktioner ni behöver.

När du startar labbskalet ser du en Stanford Bunny med enkel Phongshading. Det är dock inte allt som händer; Labbskalet gör renderingen i två pass, först till en textur, och sedan renderas denna textur på en quad över hela skärmen. Rendering av quads är nämligen en stor del av det du skal göra.

Ett antal funktioner tillhandahålls för att underlätta:

initFBO() skapar en tom textur med tillhörande FBO. (Undvik initFBO2, den är för andra ändamål.)

useFBO() väljer en FBO som utdata och två andra som indata. (Skicka 0L om ingen önskas.)

loadShaders() bygger en shader med ett anrop. Du anger bara källfilerna.

Dessa funktioner ger en viss inpackning av återanvändbar kod. Ni kanske minns att jag på föreläsningen gav rådet att paketera kod? Detta räcker en bit, men du kan gå ännu längre. Som lite extra hjälp ger jag ett exempel, en funktion som applicerar en viss shader på en eller två intexturer och skriver på en given destination (eller skärmen om den saknas). Detta är en ganska enkel ytterligare paketering av useFBO, som inte minst ger dig en vink om några punkter som måste stämma:

void runfilter(GLuint shader, FBOstruct *in1, FBOstruct *in2, FBOstruct *out)
{
    glUseProgram(shader);
    
    // Many of these things would be more efficiently done once and for all
    glDisable(GL_CULL_FACE);
    glDisable(GL_DEPTH_TEST);
    glUniform1i(glGetUniformLocation(shader, "texUnit"), 0);
    glUniform1i(glGetUniformLocation(shader, "texUnit2"), 1);


    useFBO(out, in1, in2);


    DrawModel(squareModel, shader, "in_Position", NULL, "in_TexCoord");
    glFlush();

}

Denna kod är inte inkluderad i labbskalet, det är bara ett exempel på hur man kan snygga till sin kod. Du får använda den om du vill. Förhoppningsvis kan den minska mängden harvande efter egendomliga fel.

I koden kan vi notera följande:

Stäng av back-face culling. Den saknar funktion när du bara ritar en rektangel och är då bara en felkälla.

Stäng av Z-buffern! Betydande felkälla!

Glöm inte att alla shaders måste informeras om texturenheterna du använder (0 och 1).

useFBO aktiverar bara FBOn, varefter du måste rita i den för att det skall hända något.

Anropet har vänt på argumenten mot vad useFBO använder.

Att paketera koden så här gör att många operationer görs i onödan, som kommentaren i koden noterar. Den är därmed inte optimal men duger för labbruk.

1a. Modifiera shadern så du får en tillräckligt stark ljuskälla på scenen.

Du har redan ljus och reflektioner i labbskalet, men hur starka är de? Ljuskällan skall ge ett maxvärde >1, och reflektionen i ytan skall vara spekulär.

Det bör noteras att en högdager som är extremt mycket större än 1 inte går att filtrera på ett tillfedsställande sätt. Varför? Ni måste över 1, säg upp till 2 någonstans, men inte upp till astronomiska värden.

Provkör. Hur ser högdagern ut? Tror du den är stark nog att ge en bra blooming-effekt? Eller är den rentav för stark?

1b. Implementera ett lågpassfilter.

Implementera en shader som utför lågpassfiltrering. Rendera scenen till en textur (FBO), filtrera till utdata (en rektangel).

1c. Applicera lågpassfiltret rekursivt med ping-ponging för att få en kraftig lågpassfiltrering.

Rendera till polygon över hela framebuffern. Observera att quaden renderas med en enklare shader utan projectionMatrix och viewMatrix.

Du kan använda mer än en shader för filtrering. Varför vill man det?

Observera att alla data som du laddar upp går till den aktiva shadern, så data som skall gå till flera shaders måste laddas upp till alla.

Spara resultatet för att kunna visa för assistenten!

1d. Blooming-filter

Skriv en tröskelsättningsshader för att föra över värden över 1 till separat textur. Filtrera denna.

Spara resultatet för att kunna visa för assistenten!

1e. Blooming-effekt

Lägg ihop originalbild (ej filtrerad) med den filtrerade trösklade bilden för att få ett resultat med bloomingeffekter.

Spara resultatet för att kunna visa för assistenten!

Frågor (skissa svar innan redovisning):

Hur ser högdagrarna ut i 1a? Varför?

Hur allokerar man en tom textur?

Hur många pass körde du lågpassfiltret i 1c och 1d?

Hur löste du 1f (om du gjorde det)? Vilket/vilka filter? (Extrauppgift)

Bör trunkeringen göras i egen shader eller som del av en shader som gör något mer? Varför?

1f. Variant EXTRAUPPGIFT

Skapa med lämpliga filter en effekt som ger en kraftig spridning längs diagonalerna kombinerat med en viss lågpassfiltrering. Addera detta som en ytterligare effekt.

Visa för assistenten (speciellt 1d, 1e, 1f).


2. Bump mapping

Målet med detta laborationsmoment är att få praktisk erfarenhet av att arbeta med bump maps, i både vykoordinater och texturkoordinater (tangent space), därmed få ökad förståelse för hur de olika koordinatsystemen fungerar och hur man transformerar mellan dem.

För enkelhetens skull arbetar vi på enkla plana ytor, så beräkningen av tangentvektorn blir trivial.

Vi använder ett enklare labbskal som är del 1-2 i det du laddade ner ovan. Bumpmaps ingår i paketet.

Scenen (en kub) är redan klar. Notera att den genereras med tangentvektor. Även bitangent, vilket dock inte är nödvändigt då den kan kryssas fram.

BUGRISK: Under experiment med koden har jag stött på en anomali som kan drabba er (gäller enbart i Linux). Kod som fungerade bra på en maskin fungerade inte på en annan. Det som hände mig var att mina tangent- och bitangenter blev felaktiga så man inte kunde räkna med dem. Resultatet var att fragmenten blev kolsvarta. Om ni stöter på detta och känner att det är oförklarligt, prova då med att hårdkoda tangent och bitangent i vertex shadern. (Deras värden i modellkoordinater är konstanta.)

2a. Bumpmappning i vykoordinater

Din vertexshader transformerar normalvektor och tangentvektor till vykoordinater. Texturkoordinater skickas vidare på vanligt sätt.

Skriv en fragmentshader som beräknar partiella derivatorna över texturen och modifierar normalvektorn efter dessa. Beräkna ljussättning från en ljusriktning (som får vara hårdkodad för enkelhetens skull). Diffus ljussättning räcker.

Detta skall resultera i en trevlig bumpmappningseffekt. Observera att bumpmappen inte längre bör användas som textur utan enbart för ljussättning! (Använd gärna en annan textur som komplement, för färg/mönster.)

Spara resultatet (även källkod) för att kunna visa för assistenten!

2b. Bumpmappning i texturkoordinater

Skapa matrisen Mvt av vektorerna Ps, Pt, n (i vykoordinater). Ledning: En 3x3-matris (mat3) kan tilldelas med tre vektorer genom att skriva mat3(v1, v2, v3).

Transformera ljusvektorn med dessa. Beräkna ljussättning i texturrymden.

Resultatet skall vara likvärdigt med 2b. Spara resultatet inkl källkod för att kunna visa.

Frågor:

Vilken bumpmappning tycker du är att föredra, vykoordinater eller texturkoordinater? Varför? Vad är skillnaden mellan att arbeta i vy- och texturkoordinater? Vilken bumpmappning (2a eller 2b) är lämplig för normalmapping?

Definierar du bumpmappen som avstånd in i eller ut ur objektet? Var spelar det in?

Blev Mvt rätt med mat3 i 2b? Om inte, vad gjorde du åt det?


2c. Parallaxmappning EXTRAUPPGIFT

Vi skall nu göra det första steget mot avancerad bumpmappning, parallaxmappning.

Hur finner vi betraktningsriktningen i vykoordinater? Skicka denna till fragmentshader via varying-parametrar.

Transformera betraktningsriktningen till texturkoordinater.

Hur kan man nu utföra ett steg i texturrymden? Utför detta.

Undersök skillnaden med och utan "offset limiting".

Visa resultatet för assistenten!

Frågor:

Hur finner vi betraktningsriktningen i vykoordinater? (Extrauppgift)

Vad blev skillnaden med och utan offset limiting? (Extrauppgift)


Det var allt för den här labben!





Denna sida underhålls av Ingemar Ragnemalm.