HALFTONE SHADER APP Applikation för digital rastrering av bilder i realtid med hjälp av Simplex Noise och OpenGL + GLSL HEMSIDA: HTTP://WWW.JOHNTOLUNAY.COM/TNM084/ Skapad av: John Tolunay (johto970@student.liu.se) Kurs: på Linköpings universitet, 2011 Examinator: Stefan Gustavson (stegu@itn.liu.se)
1. Syfte Att implementera en shader som digitalt färgrastrerar en inläst bildtextur i realtid med hjälp av OpenGL och GLSL. Man ska kunna ändra på punktstorleksskalan och tryckvinklarna och applicera filter direkt i applikationen utan att behöva modifiera det i programkoden. Ett noisemönster ska läggas på varje tryckpunkt och det ska även finnas ett noisemönster i en vit bakgrund som ska animeras i realtid. 2. Inledning Procedurellt genererade bilder används ofta för att generera terrängområden som bergskedjor och havsytor bland annat. Med hjälp av OpenGLs inbyggda shaderspråk GLSL så används det ofta för att rendera shadereffekter som Phong Shading, Cel-Shading, Bump-Mapping och Blooming på polygonobjekt och texturer. Vad som däremot är ovanligt men som är möjligt att göra är att skapa shaders som tar en inläst bildtextur och sedan digitalt rastrerar hela bilden så att den renderade bildtexturen består av runda punkter. De runda punkterna har samma avstånd ifrån varandra men har olika storlekar beroende på hur mörkt det är i bilden. Detta kallas även för AM rastrering och är en konventionell metod för att trycka och skriva ut digitala bilder på papper. Det har tidigare gjorts en liknande shader som gör samma sak fast det var skrivet i Renderman Shading Language, ett shaderspråk som genererar förrenderade bilder och inte renderar i realtid. Dessutom bestod bilden enbart av svarta punkter med olika storlekar baserat på färgtonen i den inlästa bildtexturen [1]. Applikationen som jag har utvecklat som ett procedurellt projekt i kursen, kallad Halftone Shader App, digital rastrerar inlästa bildtexturer i realtid som dessutom utför rastrering i färg med. Detta har gjorts i just OpenGL med dess shaderspråk GLSL och för att applikationen ska klassas som en realtidsapplikation så animeras ett noise-genererat mönster i bakgrunden där det finns vit yta. Dessutom så kan man ändra på punktstorleksskalan, tryckvinklar på färgkanalerna samt applicera olika filter för att jämna ut den shaderrenderade bilden. Men det är shadern skapad i Renderman SL som har varit en grund för hela projektet och har möjliggjort för att kunna skapa applikationen i OpenGL och GLSL [1]. 3. Teori Sammanfattningsvis handlar digital rastrering om att omvandla en digital bild till en binär bild, alltså en bild som enbart består av 1:or och 0:or, när man ska trycka och skriva ut en digital bild på papper. Detta gör man för att tryckmaskiner och skrivare inte kan representera en bild som har kontinuerliga färgtoner [2].
Figur 1: En svartvit bild som har blivit digital rastrerad. Vad som då trycks ut är cirkulära punkter som har konstant avstånd ifrån varandra men som varieras i storlek beroende på hur mörkt eller ljust det är i ett visst bildområde. Ju mörkare desto större punkt och ljusare desto mindre punkt. Denna typ av rastreringsteknik kallas för AM rastrering och det finns även FM rastrering där punkterna har samma storlek men deras avstånd och därmed frekvens i ett visst bildområde varieras. Det är AM rastrering dock som har använts som rastreringsteknik för det här projektet. c) Figur 2: a) Två kontinuerliga gråtoner. b) AM rastrerad representation av a). c) FM rastrerad representation av a). När man kollar på en digital färgbild på skärmen så används den additiva färgrymden RGB (Red, Green, Blue) för att representera färgerna på skärmen. I tryck däremot så används istället den subtraktiva färgrymden CMYK (Cyan, Magenta, Yellow, Key Black) för att representera färgerna på pappret [3].
Figur 3: a) RGB färgrymden. b) CMYK färgrymden. Vid färgrastrering så rastreras varje färgkanal var för sig, vilket innebär att för varje bild som är enfärgad på varje färgkanal omvandlas till en binär bild. Detta lägger man sedan ihop till en färgbild som då är rastrerad. Något som är viktigt för att kunna visa digitalt färgrastrerade bilder på skärmen är att bilderna måste visas i RGB-läge för att färgerna ska återges korrekt på skärmen. Men även att man måste låta bilderna befinna sig i CMYK-läge när de ska färgrastreras. Därför så måste de färgkonverteras i de olika lägen och detta görs då med dessa formler [3]: Detta gäller när man ska konvertera färgrymden från RGB till CMY som också kan göra om till en 4x4 matris som visas nedan: Därefter konverterar man ytterligare från färgrymden CMY till CMYK och då kan man rastrera varje färgkanal: Efter färgrastreringen konverterar man tillbaks från färgrymden CMYK till CMY och sedan från CMY till RGB för att kunna återge den rastrerade färgbilden på skärmen:
Men man kan även konvertera direkt från CMYK till RGB genom den härledda formeln nedan: Noisemönster kommer att läggas till i applikationen, dels som ett noisemönster som kommer att spridas runt omkring tryckpunkterna, dels som ett annat noisemönster som kommer att animeras i en vit pappersbakgrund. Detta för att få till en realtidseffekt på applikationen. 4. Implementation 4.1 AM Färgrastrering När en bildtextur är inläst i applikationen och skickas vidare till fragment shadern, lagras dess RGBAvärden 1 i en vektor: vec4 RGBAcolor = texture2d(imgtex, gl_texcoord[0].st); Det aktuella RGBA-värdet konverteras sedan till CMY-värden: mat4 CMYKmat = mat4(vec4(-1.0,0.0,0.0,0.0), vec4(0.0,-1.0,0.0,0.0), vec4(0.0,0.0,-1.0,0.0), vec4(1.0,1.0,1.0,1.0)); vec4 CMYKcolor = CMYKmat*RGBAcolor; Sedan kan man tilldela K-kanalen enligt formeln i förra kapitlet: CMYKcolor.a = min(min(cmykcolor.r, CMYKcolor.g), CMYKcolor.b); För varje färgkanal utför man sedan AM rastrering där den aktuella färgkanalen och vald tryckvinkel skickas in som parameter. amhalftone(float colorch, float degangle); Texturkoordinaterna s,t skalas upp med punktstorleken som man kan justera från huvudapplikationen. Tryckvinkeln degangle konverteras till radianer och en rotationsmatris skapas med tryckvinkeln som vinkelfaktor. De uppskalade texturkoordinaterna roteras genom att multiplicera rotationsmatrisen med texturkoordinaterna. För att se till att tryckpunkterna har cirkulära former, utför man en modulus-operation, där man skickar in de roterade och uppskalade texturkoordinaterna för den ena parametern och flyttalet 1.0 för andra parametern. float scale = dotsize; float printnoisescale = 1000.0; 1 A representerar Alpha-kanalen vilket styr transparensen i bilden. Den kommer hela tiden ligga på 1.0, vilket innebär att bilden kommer vara ogenomskinlig. Den kanalen kommer alltså inte påverka konverteringen från RGB till CMYK och vice versa.
float sstemp = scale * gl_texcoord[0].s; float tstemp = scale * gl_texcoord[0].t; float radangle = radians(degangle); mat2 rotmat = mat2(vec2(cos(radangle), -sin(radangle)), vec2(sin(radangle), cos(radangle))); vec2 rotvec = rotmat*vec2(sstemp,tstemp); float ss = rotvec.x; float ts = rotvec.y; float slocal = mod(ss, 1.0); float tlocal = mod(ts, 1.0); Därefter skapas noisemönstret som ska spridas runt tryckpunkterna. Simplex Noise används som genereringsfunktion vilket ger bättre men långsammare noise mönster än om man använder sig av Classic Perlin Noise [4]. Det kommer att användas två olika noisemönster med de här två olika vektorerna som inparametrar: vec2 p1 = vec2(printnoisescale*gl_texcoord[0].s, printnoisescale*gl_texcoord[0].t); vec2 p2 = vec2(printnoisescale*2.0*gl_texcoord[0].s, printnoisescale*2.0*gl_texcoord[0].t); float resultnoise1 = snoise(p1); float resultnoise2 = snoise(p2); De resulterande noisevärdena adderas ihop med en viss numerisk subtraktion, som sedan skalas ner: float printnoise = 0.1 * (resultnoise1-0.5 + resultnoise2-0.5); För att tryckpunkterna ska ha rätt och konstant avstånd från varandra utan att de smetar ihop eller att de ligger för långt ifrån varandra, beräknar man det euklidiska avståndet från punkten P(0.5, 0.5) till Q(slocal, tlocal): float dist = sqrt((slocal-0.5)*(slocal-0.5) + (tlocal-0.5)*(tlocal- 0.5)); Sedan läggs det generade noisemönstret på distansen för att noisemönstret ska spridas kring tryckpunkterna: dist = dist + printnoise;
Hur mycket färg och storlek som ska läggas på varje tryckpunkt beräknas genom att ta skillnaden mellan 1.0 och det aktuella flyttalsvärdet på den aktuella färgkanalen, som var inskickad som inparameter. Sedan beräknas radien på tryckpunkten genom den resulterande färgmängden som beräknades tidigare. Till slut får man ut den rätta storleken på tryckpunkten och dess rätta position på bilden genom att använda en step-funktion med radien och distansen som inparametrar och tryckpunktsvärdet returneras: float blackness = 1.0 - colorch; float radius = 0.5 * sqrt(blackness); float dot = step(radius, dist); return dot; Efter att varje kanal har rastrerats, omvandlas CMYK tillbaks till RGB enligt formlerna från förra kapitlet: RGBAcolor.r = (1.0-CMYKcolor.r)*(1.0-CMYKcolor.a); RGBAcolor.g = (1.0-CMYKcolor.g)*(1.0-CMYKcolor.a); RGBAcolor.b = (1.0-CMYKcolor.b)*(1.0-CMYKcolor.a); Rastrering sker när den inlästa bildtexturen inte består av vit färg. Annars skapas ett annat noisemönster, av Simplex Noise typen, som animeras i bakgrunden under tiden som applikationen körs. Den läggs på den befintliga pappersfärgen som alltså är vit: vec4 papercolor = vec4(1.0, 1.0, 1.0, 1.0); papercolor += snoise(vec2(papernoisescale * gl_texcoord[0].s+10.0*cos(time), papernoisescale * gl_texcoord[0].t)); Till slut mixas den färgrastrerade bilden med pappersbakgrunden, fylld med noisemönster, med hjälp av en mixfunktion med en mixfaktor på 0.2: gl_fragcolor = mix(rgbacolor,papercolor, 0.2); Hur resultatet blev för den färgrastrerade bilden beskrivs i avsnittet Resultat. Nedan kommer implementationer som filtrerar och därmed ska förbättra kvaliteten på den resulterande bilden. 4.2 Antialiasing Antialiasing filtrering ser till att den resulterande bilden ger ett sken av att vara högupplöst genom att konturerna på objekt i bilden har blivit utjämnad. I vanliga fall ser man trappsteg runt konturerna om bildtexturen inte är tillräckligt högupplöst.
Figur 4: a) Lågupplöst linje som ger upphov till aliasing. b) Linje som har blivit Antialiasing filtrerad. Istället för att en rastrera färgkanal direkt utför man filtrering först på den genom att skicka in en vektor, som består av distans- och punktvärde vilka amhalftone returnerar istället, och själva färgkanalen som inparametrar: distdotc = amhalftone(cmykcolor.r, anglec); CMYKcolor.r = aafilter(distdotc, CMYKcolor.r); I aafilter beräknas derivatan av distansvärdet genom att beräkna den absoluta derivatan av distansvärdet med hjälp av funktionen fwidth [5]: float dist = distdot.x; float dot = distdot.y; float derivdist = 0.7*fwidth(dist); Denna derivata används sedan för att avgöra hur mycket en kontur hos en tryckpunkt ska bli utsmetat. Detta görs med hjälp av funktionen smoothstep [5]: float smoothing = smoothstep(0.005-derivdist,0.005+derivdist,dot); Den nya tryckpunkten skapas genom att mixa den gamla tryckpunkten med färgkanalen, med smoothing som mixfaktor [5]: float newdot = mix(dot,colorch,smoothing); Den aktuella färgkanalen tilldelas den nya tryckpunkten: return newdot; Samma sak görs för de resterande färgkanalerna som sedan omvandlas till RGBA och den färgrastrerade bilden mixas med pappersbakgrunden.
4.3 Analytiska derivator Vid generering av noisemönster händer det att det skapas för högfrekvent noise i tryckpunkterna vilket leder till att man ser vita prickar på tryckpunkterna. Det kan man inte filtrera bort väl med hjälp av Antialiasing. Det beror på att vid högfrekvent noise felberäknas de automatiska derivatorna som skapas ifrån fwidth-funktionen med distansvärdet som inparameter: float derivdist = 0.7*fwidth(dist); Det kan dock lösas med att man beräknar exakta analytiska derivator i Simplex Noise-funktionen. Nedan visas implementationen av dem (ursprunglig implementation av S. Gustavson) [6]: vec3 snoise(vec2 P) {... float dnoise_dx; float dnoise_dy; float t30 = t0 * t0 * t0; float temp0 = t30 * dot( grad0, Pf0 ); dnoise_dx = temp0 * Pf0.x; dnoise_dy = temp0 * Pf0.y; float t31 = t1 * t1 * t1; float temp1 = t31 * dot( grad1, Pf1 ); dnoise_dx += temp1 * Pf1.x; dnoise_dy += temp1 * Pf1.y; float t32 = t2 * t2 * t2; float temp2 = t32 * dot( grad2, Pf2 ); dnoise_dx += temp2 * Pf2.x; dnoise_dy += temp2 * Pf2.y; float t40 = t30 * t0; float t41 = t30 * t1; float t42 = t30 * t2; dnoise_dx *= -8.0; dnoise_dy *= -8.0; dnoise_dx += t40 * grad0.x + t41 * grad1.x + t42 * grad2.x;
dnoise_dy += t40 * grad0.y + t41 * grad1.y dnoise_dx *= 70.0; dnoise_dy *= 70.0; + t42 * grad2.y; } return vec3(dnoise_dx, dnoise_dy, 70.0 * (n0 + n1 + n2)); Simplex Noise returnerar istället en vec3 som innehåller analytiska derivatorna samt noisevärdet när man skickar in en vec2 som inparameter. I det här fallet när två olika vec2 användas för att skapa två olika noisemönster beräknas sedan medelvärdet av de analytiska derivatorna: vec2 p1 = vec2(printnoisescale*gl_texcoord[0].s, printnoisescale*gl_texcoord[0].t); vec2 p2 = vec2(printnoisescale*2.0*gl_texcoord[0].s, printnoisescale*2.0*gl_texcoord[0].t); float dx = 0.0; float dy = 0.0; vec3 resultnoise1 = snoise(p1); vec3 resultnoise2 = snoise(p2); dx = (resultnoise1.x + resultnoise2.x)/2.0; dy = (resultnoise1.y + resultnoise2.y)/2.0; float printnoise = 0.1*(resultNoise1.z 0.5 + resultnoise2.z 0.5); I amhalftone kommer det returneras en vec4 istället som innehåller de analytiska derivatorna samt den aktuella tryckpunkten och distansen som beräknas på sätt som tidigare: return vec4(dx, dy, dist, dot); I aafilter, där vec4 vektorn som returnerades från amhalftone kommer att användas som inparameter tillsammans med den aktuella färgkanalen, beräknas distansderivatan på det här sättet istället: float derivdist = 0.7*(abs(dx) + abs(dy)); I övrigt sker de övriga beräkningarna på samma sätt i aafilter som tidigare. Resultatet av implementationen visas i Resultat avsnittet.
4.4 Frequency Clamping Ett annat sätt att reducera högfrekvent noise är att filtrera en bild med s.k. Frequency Clamping. Det kan också ske på grund av att man har en för stor filterbredd vid Antialiasing (det går dock att utföra Frequency Clamping utan att ha Antialiasing aktiverat). Figur 5: Det rödmarkerade kurvan representerar översvängningar hos funktionen f(t). Vad man gör då är att man jämnar ut noisefunktionen, som innehåller översvängningar, till ett värde som ligger mellan noisefunktionens aktuella värde och medelvärdet i noisefunktionen. Detta utförs när filterbredden är större än hälften av noisefunktionens översvängningsstorlek [5]. Implementationen har skett enligt N. Tatarchuk [7]: I fadeout-funktionen tar den in värdet x, som representerar noise-funktionens aktuella värde, medelvärdet avg, översvängningsstorleken f och filterbredden w som parameterar. Den returnerar ett flyttalsvärde som innehåller en mix av de inparametrarna för att jämna ut noise-funktionen: float fadeout(float x, float avg, float f, float w) { return mix(x, avg, smoothstep(0.4, 0.6, w/f)); } I signedsimplexnoise-funktionen tar den in en vec2 för att skapa Simplex Noise-mönster som sedan returnerar den s.k. signumfunktionen av snoise. float signedsimplexnoise(vec2 p) { return (2.0 * snoise(p)) - 1.0; } I filterednoise skapas signumfunktionen av snoise som sedan används för att returnera en utjämnad noise-funktion tillsammans med filterbredden w. Medelvärdet är satt till 0.5 medan översvängningsstorleken ligger på 1.0.
float filterednoise(vec2 p, float w) { float ssnoise = signedsimplexnoise(p); return fadeout(ssnoise, 0.5, 1.0, w); } Detta är huvudfunktionen för att utföra Frequency Clamping som kommer anropas ifrån amhalftone. Den kommer att innehållar inparametrarna p, octaves, lacunarity och gain. p används för att skapa noisemönstret, octaves anger antalet gånger man ska utföra Frequency Clamping på den aktuella noise-funktionen och lacunarity anger hur mycket en fraktalkurva täcker en yta. Till slut anger gain hur mycket Frequency Clamping ska förstärkas för varje gång man utför den på en noisefunktion. Filterbredden fw beräknas som summan av absolutbeloppet av p.x och absolutbeloppet av p.y. Noisesumman fnoisesum, som returnas, adderas för varje gång i for-loopen samtidigt som amp multipliceras med gain och freq och fw multipliceras med lacunarity. float filtered_fbm(vec2 p, int octaves, float lacunarity, float gain) { float amp = 1.0; float freq = 1.0; float fw = abs(p.x) + abs(p.y); float fnoisesum = 0.0; for(int i = 0; i < octaves; i++) { fnoisesum += amp * filterednoise(p*freq, fw); } amp *= gain; freq *= lacunarity; fw *= lacunarity; } return fnoisesum; I avsnittet Resultat visas resultaten från alla implementationer.
5. Resultat Följande bild, vilket är tagen ifrån föreläsningsanteckningarna från kursen Grafisk Teknik (TNM059) av S. Gooran [3], kommer att användas för att demonstrera resultaten av rastreringen och filtreringen: Det kommer även att användas följande beteckningar som också används för att demonstrera resultaten: Figur 6: Digital bildfoto av en stuga. FPS Frames Per Second (antalet bildrutor per sekund) (C, M, Y, K ) - Tryckvinklar för varje färgkanal AA Antialiasing AD Analytical Derivatives FC Frequency Clamping DS Dot Scale (punktskala)
Testet gjorde på en DELL Vostro dator med en Intel Core i7 860 processor på 2.80 Ghz, 6.00 GB RAM minne, nvidia Geforce GTS 240 grafikkort och Windows 7 Enterprise 64-bitars operativsystem. Bloodshed Dev-C++ användes som utvecklingsmiljö för hela projektet. 5.1 Standard (utan filter) Figur 7: Standard a) DS: 50.0 (45, 45, 45, 45 ) 210.0 FPS b) DS: 100.0 (45, 45, 45, 45 ) 210.0 FPS Figuren visar färgrastrering utan något filter aktiverat med 45 vinkel på alla färgkanaler. Den ena har ett tryckpunktskala på 50.0 och den andra 100.0 och renderas i 210.0 FPS. Man kan se att bilderna är rätt så vit och blek, de är inte färgrika. Har man dock olika tryckvinklar på varje färgkanal blir bilderna mer färgrik och ju större tryckpunktskala desto mer upplever man att färgtonen har kontinuitet, vilket visas i nästa figur:
Figur 8: Standard a) DS: 50.0 (100, 15, 0, 45 ) 171.0 FPS b) DS: 100.0 (100, 15, 0, 45 ) 171.0 FPS Har man filtret Frequency Clamping aktiverat, får man rundare tryckpunkter vars tryckbrus, som låg runt omkring tryckpunkterna skapat av Simplex Noise, försvinner. Filtret gör dock att bilderna renderas långsammare vid en bildhastighet på 178.0 FPS och när tryckvinklarna är samma igen på varje färgkanal vid 45, får bilderna samma bleka intryck. Detta visas på figurerna nedan: Figur 9: FC a) DS: 50.0 (45, 45, 45, 45 ) 178.0 FPS b) DS: 100.0 (45, 45, 45, 45 ) 178.0 FPS
Har man olika tryckvinklar blir bilderna återigen färgrikt: Figur 10: FC a) DS: 50.0 (100, 15, 0, 45 ) 178.0 FPS b) DS: 100.0 (100, 15, 0, 45 ) 178.0 FPS Att de färgrastrerade bilderna blir bleka som man ser på figur 9 a) och 9 b) beror på att färgvärdena på varje kanal inte beräknas på rätt sätt när man ska visa CMYK på en bildskärm. Det krävs att man blandar in Neugebauers och Demichels ekvationer för att beräkna tryckpunktsfärgerna korrekt [3] [8]. I och med att det är ett komplicerat sätt att implementera, finns det ett annat men ett mycket enklare sätt att lösa det. När man beräknar färgvärdet på färgkanalen K, kan man då skala om dess resulterande värde med en faktor på 0.5: CMYKcolor.a = 0.5*min(min(CMYKcolor.r, CMYKcolor.g), CMYKcolor.b); Detta leder till att de färgrastrerade bilderna som har samma tryckvinkel på 45 på varje färgkanal, kommer att bli mycket färgrikt, vilket visas på figurerna nedan:
John Tolunay johto970@student.liu.se 850513-1956 b) a) Figur 11: Standard a) DS: 50.0 (45, 45, 45, 45 ) 213.0 FPS b) DS: 100.0 (45, 45, 45, 45 ) 213.0 FPS Bilderna med de olika tryckvinklarna är fortfarande färgrikt som innan med en viss ändring på tryckpunkternas position och storlek som dock inte är märkbar: b) a) Figur 12: Standard a) DS: 50.0 (100, 15, 0, 45 ) 213.0 FPS b) DS: 100.0 (100, 15, 0, 45 ) 213.0 FPS
Samma sak händer när man har Frequency Clamping aktiverat. Bilderna blir färgrika vid samma tryckvinkel samtidigt som de har renare utseende: Figur 13: FC a) DS: 50.0 (45, 45, 45, 45 ) 181.0 FPS b) DS: 100.0 (45, 45, 45, 45 ) 181.0 FPS Och bilderna med olika tryckvinklar behåller även sin färgrika karaktär med dess rena utseende när filtret är aktiverat: Figur 14: FC a) DS: 50.0 (100, 15, 0, 45 ) 181.0 FPS b) DS: 100.0 (100, 15, 0, 45 ) 181.0 FPS Bildhastigheterna blir till och med lite snabbare som ett resultat av när man modifierar beräkningen av färgkanalen K på det viset (214.0 FPS utan och 181.0 FPS med Frequency Clamping). Dock så blir
det ingen skillnad på bildresultaten när länge man har Antialiasing filtret aktiverat oavsett om man modifierar beräkningen eller inte. Applikationen i dess slutförda form kommer att ha den modifierade beräkningen i fortsättningen. 5.2 Antialiasing Nästa figur visar bildresultaten när man har Antialiasing filtret aktiverat. Då får bilderna en mer utjämnat färgton då tryckpunkterna smälts mer in i bilderna samtidigt som bildhastigheten sänks aningen något ner till 201.0 FPS: a) b) Figur 15: AA a) DS: 50.0 (45, 45, 45, 45 ) 201.0 FPS b) DS: 100.0 (45, 45, 45, 45 ) 201.0 FPS När man har olika tryckvinklar ser man till en början inte så stor skillnad på figur 16 jämför med ovan tills man tittar närmare på den röda färgen på stugan. Där kan man se det karaktäristiska mönstret som tryckpunkterna har när de är tryckta i olika vinklar:
Figur 16: AA a) DS: 50.0 (100, 15, 0, 45 ) 201.0 FPS b) DS: 100.0 (100, 15, 0, 45 ) 201.0 FPS 5.3 Antialiasing + Frequency Clamping Med Frequency Clamping aktiverat tillsammans med Antialiasing sänks bildhastigheten ner till 171.0 FPS och då får man följande bildresultat enligt figuren nedan: Figur 17: AA +FC a) DS: 50.0 (45, 45, 45, 45 ) 171.0 FPS b) DS: 100.0 (45, 45, 45, 45 ) 171.0 FPS Tryckpunkterna blir mer rundade och noisemönstret som låg omkring tryckpunkterna har försvunnit som ett resultat av att Frequency Clamping har aktiverats. Samma sak blir det när man har olika tryckvinklar som visas på bilderna i figur 18:
Figur 18: AA+FC a) DS: 50.0 (100, 15, 0, 45 ) 171.0 FPS b) DS: 100.0 (100, 15, 0, 45 ) 171.0 FPS Det ger ett rent och professionellt intryck där man får känslan av att bilderna är högupplösta när de i själva verket har samma upplösning som de andra bilderna. 5.3 Antialiasing + Analytical Derivatives När man avaktiverar Frequency Clamping men har analytiska derivator aktiverat tillsammans med Antialiasing istället märker man ingen större skillnad på bilderna. Vad som händer däremot så dras bildhastigheten ganska rejält ner till 156.0 FPS. Figur 19 visar bildresultaten när de har samma tryckvinklar:
Figur 19: AA +AD a) DS: 50.0 (45, 45, 45, 45 ) 156.0 FPS b) DS: 100.0 (45, 45, 45, 45 ) 156.0 FPS Likvärdiga resultat sker även vid olika tryckvinklar: Figur 20: AA+AD a) DS: 50.0 (100, 15, 0, 45 ) 156.0 FPS b) DS: 100.0 (100, 15, 0, 45 ) 156.0 FPS 5.4 Antialiasing + Analytical Derivatives+ Frequency Clamping Hur blir bilderna när alla filter är aktiverade? Något som definitivt kommer att förändras är bildhastigheten som nu hamnar på 148.0 FPS istället, vilket innebär att bildhastigheten blir som mest långsam när alla filter är aktiverade. Med analytiska derivator aktiverade blir det ända ingen större
skillnad på bilderna utan det blir lika identiskt med som bilderna som enbart hade Antialiasing och Frequency Clamping aktiverat. Det visas på figuren nedan där bilderna har samma tryckvinklar: Figur 21: AA +AD +FC a) DS: 50.0 (45, 45, 45, 45 ) 148.0 FPS b) DS: 100.0 (45, 45, 45, 45 ) 148.0 FPS Inte heller blir det någon större förändring när bilderna har olika tryckvinklar: Figur 22: AA+AD+FC a) DS: 50.0 (100, 15, 0, 45 ) 148.0 FPS b) DS: 100.0 (100, 15, 0, 45 ) 148.0 FPS
För att se den tydliga skillnaden när analytiska derivatorna är aktiverat i bilderna måste man förstora tryckpunkterna rejält vilket man kan se i figuren nedan: c) d) Figur 23: Jämförelse mellan tryckpunkterna som har olika filter aktiverade. a) AA b) AA+AD c) AA+FC d) AA+AD+FC Om man kollar på bilderna där enbart AA är aktiverat och AA+AD är aktiverat så ser man den enda skillnaden att de vita prickarna har placerat om sig i tryckpunkterna. Jämför man sedan AA+FC och AA+AD+FC är noisemönstret runt tryckpunkterna helt eliminerat medan det finns vita prickar på AA+AD+FC samt att färgerna har blivit nedtonat. 6. Diskussion Baserat på resultaten man har fått ut från implementationerna så är det möjligt att skapa en färgrastreringsapplikation i realtid. De resulterande bilderna ger en tydlig representation på hur bilder kan se ut när man skriver och trycker ut de på papper. Bildhastigheten är mer än tillräckligt snabbt nog för att rendera de färgrastrerade bilderna för att man ska kunna använda det (byta bilder, applicera filter och ändra tryckpunktskalan) och se det animerade noisemönstret i realtid utan att det hackar. Bildhastigheten låg mellan 145.0-215.0 FPS baserat på vilka filter som var aktiverade under körningen med maximerad fönster. Dock så var det ett mycket kraftfullt grafikkort som användes i testningen (nvidia GeForce GTS 240) och när man testade applikationen på en MacBook med nvidia GeForce 9400M som inbyggt grafikkort låg bildhastigheten mellan 25.0-50.0 FPS med maximerat fönster och när samma bild användes. Vilken bild som ger bäst resultat och därmed mest visuellt imponerande är svårt att säga, då nästan alla bilder fyller sin visuella funktion. Det som var minst imponerande var de resulterande bilderna som hade Antialiasing och analytiska derivator aktiverat i och med att skillnaden var extremt minimala jämfört med bilderna som enbart hade Antialiasing aktiverat. De ser identiska ut med varandra om man kollar på figurerna 15-16 och 19-20 och för att se någon tydlig skillnad kan man se på figur 23 a) och b) att de vita prickarna har placerat om sig. Dessutom drog bildhastigheten ner ganska rejält är de analytiska derivatorna var aktiverade. Däremot kom de lite till nytta när man även hade Frequency Clamping aktiverat tillsammans med de två för det blir lite tydligare skillnad när man nu jämför tryckpunkterna på figur 23 c) och d). Där kan man se att tryckpunkterna på figur 23 d) att de har blivit lite nedtonade och erhållit de vita prickarna medan på 23 c) syns de inte men istället är tryckpunkterna ljusa.
Med enbart Frequency Clamping aktiverat gör att tryckpunkterna blir runda och får den mycket fina färgmättanden och demonstrerar verkligen färgrastreringens ideella utseende när man utför AM rastreringen i färg. Detta visas främst i figur 13 och 14. Däremot så eliminerar den noisemönstret som låg omkring tryckpunkterna helt och hållet, vilket går emot själva grundsyftet av att skapa ett datorprojekt som ska visualisera ett noisemönster även om den animerande noisemönstret fortfarande finns i bakgrunden. Med enbart Antialiasing aktiverat och till och med inga filter aktiverade alls, gör att noisemönstret får sin fulla användning i bilderna, vilket demonstreras i figur 8, 11-12 och 15-16. Det ger också de snabbaste bildhastigheterna i testet och det är något som ens MacBook också klarade av att visa i hygglig hastighet. Ser man de bilderna dock på riktigt nära håll, speciellt vid maximerat fönster, så kan de upplevas aningen gryniga och det är aningen för ljust i bilderna. Bildkvaliteten förbättras dock om man väljer att färgrastrera bilden i olika tryckvinklar istället för att ha samma tryckvinklar. Men om man ska välja ut en favorit av vilken av de färgrastrerade bilderna som ser bäst ut så är det nog den som har enbart Frequency Clamping filtret aktiverat och rastreras med olika tryckvinklar vilket visas på figur 14. Man får en känsla av det är konstverk man ser framför sig som har konstruerat på ett riktigt kosmetiskt och fascinerande sätt. Vad det beträffar figur 7 och 9 när det kommer till deras brist på färg, så har det just med att det är svårt att visa CMYK färger korrekt på en datorskärm, i och med att en datorskärm är gjord för att visa färger med hjälp av RGB färgrymden. Därför så kan man inte omvandla RGB-färgerna direkt till CMYKfärger med hjälp av de matematiska formlerna som visades i Teori avsnittet för att visa färgerna korrekt. Man måste även, som tidigare nämndes, blanda in Neugebauers och Demichels ekvationer [3] [8], vilket kort går ut på i Demichels ekvationer att för varje aktuellt färg som ska visas i CMYK räkna ut 16 st slags delareor på en bråkdels bildyta. På 4 av de delareorna består respektive delarea enbart av cyan, magenta, gul och svart. Om man sedan ska beräkna delarean som enbart är täckt av cyan och magenta, får man då fram ett värde på det här sättet: Variablerna c, m, y och k anges i procent om hur mycket varje primärfärg täcker en viss delarea. När alla 16 delareor har beräknats används de sedan i Neugebauers ekvationer för att därmed beräkna det rätta och riktiga färgen i CMYK för att man ska uppfatta färgen på skärmen som i verkligheten. Man kan då beräkna färgvärdet på K-kanalen korrekt i shadern enligt formlerna: CMYKcolor.a = min(min(cmykcolor.r, CMYKcolor.g), CMYKcolor.b); Något som också hade varit önskvärt att implementera men som inte hanns med på grund av tidsbrist är att implementera FM rastrering i form av felspridningsraster skapat av Floyd & Steinberg, 1976 [3]. Dock så krävs det att man modifierar värden på grannpixlar för varje aktuell pixel man befinner sig i om man ska följa deras algoritm till punkt och pricka och det något som GLSL inte stödjer i dagsläge då den enbart kan modifiera värdet på den aktuella pixeln [9]. Avslutningsvis är hela visualiseringen i applikationen tämligen rätt så ovanligt men rätt så unikt. Där man i vanliga fall använder sig av GLSL för att utföra Cel- och Phong-shading på polygonobjekt och Bump Mapping på texturer så kan man även använda GLSL för att utföra färgrastrering på
bildtexturer. Har man ett bra grafikkort så kan man använda applikationen på ett mycket smidigt sätt i realtid. Det visar på att man kan göra helt unika typer av visualiseringar som man inte annars trodde att det var möjligt att göra i shaderspråk som GLSL. Förhoppningsvis blir kanske applikationen Halftone Shader App en stor inspirationskälla för folk att skapa nya typer av visualiseringar genom GLSL eller något annat shaderspråk. Då är det bara för de att plocka fram Simplex Noise funktionen. John Tolunay, Linköpings universitet 2011 7. Referenser [1] Stefan Gustavson. Halftone Shader in Renderman SL http://webstaff.itn.liu.se/~stegu/tnm084-2010/sl-halftone/, 2011-01-10 [2] Skriftlig och bildkälla: Sasan Gooran. Digital Rastrering http://webstaff.itn.liu.se/~sasgo/tnm059/lectures/lecture_rastrering_3.pdf, 2011-01-10 [3] Skriftlig och bildkälla: Sasan Gooran. Digital Färgrastrering http://webstaff.itn.liu.se/~sasgo/tnm059/lectures/lecture_color_3.pdf, Linköpings universitet, 2011-01-10 [4] Stefan Gustavson. Simplex Noise Demystified http://webstaff.itn.liu.se/~stegu/tnm084-2010/simplexnoise-demystified.pdf, Linköpings universitet, 2005-03-22 [5] Larry Gritz, Tony Apodaca. Advanced Renderman: Beyond the Companion http://www.renderman.org/rmr/publications/sig99.course25.pdf, SIGGRAPH 1999 Course 25, 1999-08-09 [6] Stefan Gustavson. DSOnoises, a set of useful noise functions for SL http://webstaff.itn.liu.se/~stegu/aqsis/dsos/dsonoises.html, Linköpings universitet, 2011-01-24 [7] Natalya Tatarchuk. The Importance of Being Noisy: Fast High Quality Noise http://developer.amd.com/assets/tatarchuk-noise%28gdc07-d3d_day%29.pdf, Game Developers Conference, 2007-03-05 2007-03-09 [8] Jörgen Rydénius. Laboration 3 Grafisk Teknik (TNM059) - Färg http://webstaff.itn.liu.se/~sasgo/tnm059/labs/lab3_2007.pdf, 1997 [9] Forumtråd på gamedev.net. Floyd Steinberg dithering possible in GLSL? http://www.gamedev.net/topic/498774-floyd-steinberg-dithering-possible-in-glsl/, 2011-02-10 [10] Bildkälla: Wikimedia Commons http://commons.wikimedia.org/wiki/file:cmyk-circles.png, 2011-02-03 [11] Bildkälla: PrePress Color Print http://designer-info.com/writing/colour_print.htm, 2011-02-10
[12] Bildkälla: Wikimedia Commons http://commons.wikimedia.org/wiki/file:synthese%2b.svg, 2011-02-04 [13] Bildkälla: Wikimedia Commons http://commons.wikimedia.org/wiki/file:synthese-.svg, 2011-02- 04 [14] Bildkälla: Wikimedia Commons http://commons.wikimedia.org/wiki/file:kantutj%c3%a4mning_2_linjer.png?uselang=sv, 2011-02-06 [15] Bildkälla: Fourier transforming in practice http://users.aber.ac.uk/ruw/teach/340/ft_ft3.php, 2011-02-07