Gamma korreksjon og hvorfor det gjelder

Hvis du er en spillutvikler, har du sikkert hørt om vilkårene gamma og gamma korreksjon. Du kan eller ikke vet hva de betyr, men de bør ikke bli lett avvist.

Spillutviklere har en tendens til å ignorere gamma fordi effektene er subtile nok til å korrigeres omtrent ved å justere lysintensiteter, spekulære intensiteter og lignende, men for å oppnå ekte bildekvalitet med realistisk utseende, er det viktig å forstå gammaverdien og trinnene som trengs å jobbe rundt sin tilstedeværelse i digital bildebehandling, for å oppnå best mulig kvalitet. Bruk av riktig gamma-korreksjon er en av de enkleste måtene å radikalt forbedre utseendet på 3D-grafikken din i sanntid.

Introduksjon: Hvordan skjermer Arbeider

CRT-skjermene som ble opprinnelig brukt til dataskjerm, har en nysgjerrig egenskap: fargesponset på skjermen er ikke-lineær med hensyn til råverdier som går fra grafikkortet. 

Ikke-lineær betyr i denne forstand at økningen i en av fargekomponentene med et konstant forhold (for eksempel hvis en rød komponent i en farge blir dobbelt så høy) ikke vil føre til en økning av den skjermemitterte lysintensiteten ved det samme forholdet (det vil si, det røde lyset som sendes ut fra skjermen vil ikke være dobbelt så høy).

Fargesponsen til en CRT-skjerm er faktisk en eksponentiell funksjon. (Som i all fysikk er dette langt mer komplekst enn vi beskriver, men for enkelhets skyld vil vi holde fast ved denne antagelsen.) Det vil si funksjonen EmittedLight (C), hvor C er en fargekomponent verdi (rød, grønn eller blå) som strekker seg fra 0 (ingen lys) til 1 (full lysintensitet), er C hevet til litt kraft γ

Dette nummeret, γ, kalles gamma eksponent eller bare gamma. Typiske gammaverdier varierer fra 2,0 til 2,4, og når man arbeider med gamma i generell forstand, er verdien avtalt å være 2,2 som et kompromiss, og mange nyere skjermer er utformet å ha gamma verdien av nøyaktig 2,2

I et vanlig scenario med gamma = 2,2, viser skjermen faktisk fargeintensiteter fra spillet ditt (grønn kurve). Den stiplede røde linjen viser hvordan en lineær skjerm vil vise de samme intensitetene.

I praksis betyr dette at svart og hvitt vil bli vist uforstyrret på skjermen (fordi null hevet til hvilken som helst makt er null, og en hevet til hvilken som helst makt er en), men alle verdier i mellom vil bli skjevt uten pålitelig måte å oppfatte Dette skjer bare ved å se på. 

For eksempel, hvis du viser en farge som angivelig er to ganger mørkere enn svart - det er, RGB (0,5, 0,5, 0,5)-det vil faktisk bli vist som mindre enn fire ganger mørkere, gitt den vanlige gammaverdien på 2,2, siden 0,5 økt til 2,2 er rundt 0,22. Dette er klart ikke dette du har tenkt, og dette er ikke tilfelle bare med CRT-skjermer: LCD-skjermer, mens ikke utilsiktet å ha denne egenskapen, er utformet for å være kompatibel med sine eldre kolleger, og viser dermed fargevennskapene som strekker seg slik.

Dessuten, som røde, grønne og blå komponenter behandles uavhengig, kan de tilsiktede fargetoner av bilder lett mangles ettersom intensiteter av de tre fargekomponentene ikke skal skaleres jevnt. Hva skjer når du viser fargeværdien RGB (1, 0,5, 0,5)? Den røde komponenten vil forbli på 1, men de andre vil falle ned til halvparten av verdiene, og endrer helt fargenes tone.

Den andre fargen ble oppnådd fra den første ved å anvende den ikke-lineære skala som skjermer bruker. Legg merke til hvordan ikke bare lysets lysstyrke, men også dens metning, ble påvirket av denne transformasjonen.

Nå som vi har sett hvilke effekter denne skjermegenskapen har på fargedataene som er gitt til skjermen, kan vi se hvilke skritt det er å bekjempe dem.

Hva er Gamma Correction?

Gamma-korreksjon er en handling som utelukker skjermenes uheldig arbeid. Gamma-korrigering av et bilde øker i hovedsak sin fargeintensitet til 1 / gamma, slik at når skjermen i sin tur øker verdien til gamma, disse avbrytes, og resultatet er fargen vi opprinnelig hadde til hensikt å bli vist. 

(Husk at EN hevet til B, og deretter hevet til C, er det samme som EN hevet til B x C, og dette er grunnen til at disse operasjonene vil avbryte, som gamma × (1 / gamma) er 1.) 

Siden gjennomsnittlig bruker ikke kalibrerer skjermen for å få en lineær respons, blir mange bilder de møter korrigert slik at de aldri føler forskjellen. Som en konvensjon distribueres de fleste bildefiler på Internett i det som kalles sRGB-fargeplass-Dette betyr at originale, tilsiktede fargeverdier er omtrent hevet til makten av 1 / 2.2 før du legger dem inn i filer (selv om mer komplekse ligninger finner sted i realiteten). Dette sikrer at alle brukere med konvensjonelle skjermer ser de ekte farger. Skannere, kameraer og mange digitale bildeinnretninger tar alt dette i betraktning, og korrigerer utdataene sine for deg når du sparer i konvensjonelle bildeformater.

Dette bildet viser kartlegging av fargeintensiteter som sendes til skjermen med grafikkortet, og intensiteter som vises av skjermen.

Ta en titt på bildet ovenfor. Hvis vi ikke regner med gamma, vil kurven være eksponentiell (lavere grønn kurve). Hvis vi utfører gamma korreksjon, vil den faktiske responsen være lineær, slik den burde være. Til sammenligning viser bildet også hvordan grafen ser ut når vi utfører gamma korreksjon, men skjermen har faktisk en lineær respons. I dette tilfellet vil intensiteten bli forvrengt på motsatt måte, og vi kan se at når en ikke-lineær skjerm forvrenger dem i sin tur, avbrytes dette, og vi ender med en rett linje.

Når trenger jeg å bekymre meg?

Hittil har vi forklart teorien bak disse fenomenene, sikkert, skjermer er ikke-lineære og de fleste bilder er korrigert slik at de ser rett ut på disse skjermene, men hva synes å være problemet? Hvorfor skal jeg, en aspirerende 3D-spillutvikler, bekymre meg selv med gamma-korreksjon og gjøre noe annet enn det bare vite om det?

Svaret er enkelt: så lenge bilder er opprettet for å bli vist, eksisterer problemet ikke engang. Men så snart du vil at et program skal gjøre noe for disse bildene (skala dem, bruk dem som teksturer, navnet ditt), må du passe på at programmet vet at verdiene er ikke ekte og er bare rettet slik at de se ekte på en skjerm.

Spesielt skjer dette i en gjengivelse når det tar teksturkart, for eksempel diffuse overflater, som inngang. Det gjør operasjoner på dem, forutsatt at deres fargeværdier representerer nøyaktig lysintensiteter; det vil si å anta a lineær korrespondanse med virkelige fenomener de representerer.

Men dette er en grunnleggende feil: hvis du vil summere fargeværdier, og de er gamma-korrigert (hevet til 1 / gamma) du får feil verdier. Det tar ikke et matematisk geni å innse det EN hevet til 1 / gamma i tillegg til B hevet til 1 / gamma er ikke lik (A + B) hevet til 1 / gamma. Problemet oppstår også når en gjengivelse utfører noen verdier, for eksempel når den utsender lette bidrag: hvis det summer to lys bidrag, men vet ikke resultatet vil bli hevet til gamma når det vises på skjermen, det har produsert feil verdier.

Og dette er nettopp hvor problemet oppstår: når en renderer forutsetter at fargene det blir lineært tilsvarer virkelige fenomener når de ikke gjør det, eller antar at fargene vil det, vil utgangene lineært svare til lysintensiteter på skjermen når de vant ' t, det er gjort en ganske alvorlig feil som kan påvirke utseende og følelse av bilder det produserer.

Hvis du ikke retter feilen, må du ikke forsikre deg om at inngangsstrukturfarger som er matet inn i rendereren, er lineære og ikke sørger for at rendererens utgangsbilde vil være lineært i forhold til skjermen, vil disse bildene avbryte hver andre ut i en viss grad, mye som hvordan de avbryter hver ut når de viser forhåndsrettede JPEG-filer i en nettleser. Men så snart du inkluderer noen mellomliggende beregninger som antar lineære korrespondanser, vil matematikken din gå galt.


(en) Ikke korrigere teksturer og ikke korrigere det endelige bildet, (B) Korrigerer ikke teksturer, men korrigerer det endelige bildet, (C) korrigere teksturer, men ikke korrigere det endelige bildet, (D) korrigere både teksturer og det endelige bildet.

Husk hva vi sa om å endre fargetoner tidligere - det faktum kan (noen ganger) hjelpe deg med å oppdage ikke-linearitet. En tommelfingerregel er: Hvis du bruker lineære tweaks til parametere (for eksempel dobling lysstyrken på lysene i scenen), endres det resulterende bildet ikke bare i lysstyrke, men også i fargetoner (for eksempel et område som går fra en rødaktig oransje fargetone mot gul), betyr dette at en ikke-lineær mellomprosess trolig skjer.

Dette kan skje med teksturkart som hentes fra ulike kilder-Internett, et digitalkamera som sparer til sRGB JPEG, en skanner, eller hvis tekstur ble malt på en skjerm som ikke var eksplisitt kalibrert for å ha en lineær respons eller ikke eksplisitt korrigert etterpå. Enhver matte gjort på disse teksturkartene vil være feil, og avvike noe fra teoretisk korrekte verdier. Dette er synlig med teksturfiltrering og mipmaps: Siden filtrering antar lineære svar når gjennomsnittlige fargeværdier, vil du se uttalte feil: mindre teksturer (fjerne) vil vises merkbart mørkere enn større (det vil si når de er nærmere deg): Dette skyldes at når de er fjernt, blir filtreringsalgoritmen gjennomsnittligere flere prøver, og deres ikke-linearitet påvirker resultatet mer.

Belysning vil også lider av feil gamma: lysbidrag til overflater sum i ekte verden og følgelig i en renderer, men summering er ikke en trofast operasjon hvis resultatet er ikke-lineært skjevt. Hvis du har komplekse fragment shaders gjør sofistikert belysning, som for eksempel overflate spredning eller HDR, blir feilene mer og mer uttalt, til det punktet du faktisk lurer på hva som er galt med bildet, i motsetning til å ha en urolig følelse av "kanskje ganske feil belysning, men det er nok bare meg" som også kan skje ofte. Mørk teksturene eller lyser de endelige bildene med en konstant eller lineær faktor, ikke drep effekten, fordi de også er lineære operasjoner, og du trenger en ikke-lineær en for å bekjempe den iboende eksponensielle responskurven som skjer i skjermen.

Hvordan løser jeg det?

Nå er du forhåpentligvis helt klar over hva gamma og gamma korreksjon er, og hvorfor dette er en så stor avtale når du gjør 3D-grafikk i sanntid. Men selvfølgelig må det være noen måte å fikse disse problemene på? 

Svaret er ja, og å fikse gamma er en ganske enkel operasjon som ikke krever at du endrer noe annet enn å legge til noen linjer med kode, ikke telle ekstra parameter, intensitet og fargejusteringer du må utføre for å få riktig belysning hvis du har satt scenene dine til å se bra ut på ikke-lineære skjermer uten å korrigere dem. 

Det er tre grunnleggende skritt for å sikre at du forblir lineær så lenge som mulig og gjør korreksjonen på riktig punkt:

1. Pass på at teksturen er riktig

Du bør ikke endre kildebildene slik at de inneholder lineære farger. Å ha farger gamma-korrigert for den typiske skjermen i åtte-biters fargefelt gir deg nødvendig ekstra oppløsning i mørkere områder hvor det menneskelige øye er mer følsomt for intensitetsvariasjoner. Du kan imidlertid forsikre deg om at fargeverdiene er lineære før de når dine shaders. 

Normalt, i OpenGL, kan du gjøre dette ved å passere GL_SRGB8 i stedet for GL_RGB8, og GL_SRGB8_ALPHA8 i stedet for GL_RGBA8, til glTexImage2D (), når du spesifiserer en tekstur. Dette vil sikre at alle verdier som leses fra denne teksten gjennom en shader-sampler, blir korrigert tilbake fra sRGB-fargeplass til en lineær, som er nettopp det vi trenger! Hvis du bruker en gjengivelses- eller spillmotor som laster inn tekstur for deg, kan det ta hensyn til dette, eller du må kanskje spesifisere det manuelt. Rådfør deg med dokumentasjonen til biblioteket eller spør noen om hjelp hvis du er usikker.

Vær imidlertid sikker på at du ikke feilaktig gjør dette på bilder som per definisjon ikke representerer fargeinformasjon, og ble eksplisitt malt med dette i tankene. Eksempler er normale kart, bump kart eller høydekart, som alle koder for noe annet enn farge i fargekanaler i en tekstur og dermed ikke sannsynligvis trenger denne typen forkorreksjon.

Fra demoen som er inkludert i denne artikkelen (noen parametre byttes med deres faktiske verdier for klarhet):

glTexImage2D (GL_TEXTURE_2D, 0, GL_RGB8, bredde, høyde, 0, GL_BGR, GL_UNSIGNED_BYTE, data); 

Dette vil laste teksten i et ikke-korrigert fargeplass. Men hvis dataene i teksturfilen er i sRGB-fargeplassen, bør vi endre den tredje parameteren til GL_SRGB8, givende:

glTexImage2D (GL_TEXTURE_2D, 0, GL_SRGB8, bredde, høyde, 0, GL_BGR, GL_UNSIGNED_BYTE, data);

Dette sikrer at OpenGL korrigerer teksturdataene når vi ser dem opp.

2. Pass på at utgangsbildene dine er riktige

Nå må du bruke fargekorrigering til de endelige utgangsbildene til rendereren. Pass på at du ikke bruker korrigering til annet enn endelig Framebuffer som skal vises på skjermen. (Ikke rør mellomliggende buffere som inngår i andre etterbehandlings shaders, da disse fortsatt vil forvente å fungere med lineære verdier.) 

Dette kan gjøres i OpenGL ved å spesifisere renderbufferen (den endelige, ikke-prøvbare framebufferen) for å få en sRGB fargekoding ved å passere GL_SRGB i stedet for GL_RGB som en parameter til glRenderbufferStorage (). Etter det må du heve GL_FRAMEBUFFER_SRGB flagg ved å ringe glenable. På denne måten skriver shader til sRGB buffere blir korrigert slik at de vises rett på en vanlig skjerm. 

Hvis du bruker en motor eller et rammeverk, inneholder det sannsynligvis en slags mulighet til å lage en sRGB framebuffer for deg og sette den opp riktig. Igjen kan du konsultere dokumentasjonen til biblioteket eller be noen om å klargjøre dette for deg.

I demo bruker vi GLFW-biblioteket, som gir oss en smertefri måte å be om en sRGB framebuffer på. Spesielt stiller vi et vindu hint og deretter, senere, fortell OpenGL for å aktivere framebuffer operasjonene å være i sRGB plass:

glfwWindowHint (GLFW_SRGB_CAPABLE, TRUE); ... glEnable (GL_FRAMEBUFFER_SRGB);

3. Fest dine Tweaked Light Intensities og Color Parameters

Hvis dette ikke er en start på et nytt prosjekt, er det sjansen for at gamma-feil belysning og filtrering har tatt sine toll på deg. Kanskje du har tweaked dine diffuse reflekterende farger, lysintensiteter og whatnot i et forsøk på å gjøre opp for subtile gener som forsømmer gamma, har brakt deg. 

Du må gå gjennom disse verdiene igjen og justere dem slik at de ser rett ut igjen - men denne gangen vil scenene dine se naturlig ut på grunn av belysning som representerer ekte verdenssituasjoner nøyaktig. Hjørner vil ikke se for mørke, slik at du ikke trenger å legge til mer intensitet for lys (derved ødelegger belysning av lysere gjenstander som da vil se kunstig lyse for den mengden lys i scenen). 

Dette vil lønne seg: å revidere parametrene dine for å skape en naturlig omgivelse med gamma-korreksjon vil gå langt for å gi brukerne en opplevelse og lysstyrkefordeling som bare ser riktig ut i deres øyne, så vant og følsom for hvor lett det fungerer i det virkelige liv.

Demo

Inkludert med denne artikkelen er en liten OpenGL 3.3 demo som viser en enkel scene med noen teksturer tændt av to bevegelige lyskilder. Den lar deg bytte mellom flere scenarier: ikke korrigere teksturer, men korrigere det endelige bildet; korrigere teksturer, men forsømmer å korrigere det endelige bildet; korrigere begge (det vil si alt riktig); og unnlater å korrigere enten (effektivt å gjøre en dobbel feil). 

Demoen er skrevet i C ++ (med to GLSL-shaders) og bruker bærbare GLFW- og GLEW-biblioteker, slik at den skal kjøre på et bredt utvalg av plattformer. Kildekoden er moden med kommentarer, slik at du kan gå og utforske alle aspekter av denne korte applikasjonen.

Demoen i aksjon.

Bruke 1 tast på tastaturet for å sykle mellom korrigering av teksturer og ikke korrigere teksturer, og 2 nøkkel til å sykle mellom korrigering av framebuffer og ikke korrigering av framebuffer. Hvis du vil sykle begge disse samtidig, trykker du på 3-nyttig å se forskjellen mellom å forsømme gamma helt (to feil som avbryter hverandre for det meste) og gjør alt riktig. Når demoen starter, blir ingen av disse rettelsene utført, så hit 3 for å se fordelene ved riktig gamma korreksjon.

Jeg har tatt med et Microsoft Visual C ++ 2013-prosjekt, kompatible 64-biters versjoner av GLFW- og GLEW-bibliotekene, og en 64-biters Windows-kjørbar. Du kan imidlertid kompilere dette ganske enkelt på en hvilken som helst plattform med GLFW og GLEW-støtte: bare kompilere main.cpp og loader.cpp sammen og knytte dem til de to bibliotekene. På Linux, installerer disse bibliotekene via pakkebehandling og passering -lglew-lglfw til g++ bør gjøre trikset. (Vær oppmerksom på at dette ikke ble testet på andre operativsystemer enn Windows, men det skal fungere. Hvis du støter på problemer, vennligst gi beskjed om det i kommentarene, og jeg løser dem så snart som mulig.)

Som du kan se når du kjører demoen, er effektene ganske merkbare selv med en enkel modell og enkel scene som dette. Selvfølgelig, i dette enkle tilfellet kan du kanskje komme unna med å justere skyggeparametrene slik at bildet ser bra ut når det ikke er korrigert. Men så snart du begynner å bygge opp kompleksitet i scenene dine, vil forskjellen bare være for synlig for å kompensere på denne måten.

Konklusjon

I denne artikkelen har vi dekket betingelser som gamma, gamma korreksjon, ikke-lineære innganger og utdata, og ikke-lineær matte. Forhåpentligvis har jeg klart å overbevise deg om at du bør begynne å bekymre deg for gamma korreksjon akkurat nå hvis du hadde forsømt det så langt, og hvis du har vært forsiktig med gamma før du støter på denne artikkelen, håper jeg bare at det har gitt deg noe nytt lite stykke informasjon for å takle problemet med. 

Vi har viktigst lært hvordan du løser problemer som oppstår når du gjør feil manipulasjon på fargeverdier, forutsatt at de er lineære, og vi har gjennomgått vanlige fallgruver og symptomer som oppstår når du forsømmer dette viktige aspektet av datagrafikk.

Jeg håper du har hatt det gøy og lært noe nytt mens du leser denne artikkelen. Til neste gang!