Gammacorrectie en waarom het ertoe doet

Als je een game-ontwikkelaar bent, heb je waarschijnlijk wel eens van de voorwaarden gehoord gamma en gamma correctie. U kunt wel of niet weten wat zij bedoelen, maar ze mogen niet lichtvaardig worden ontslagen.

Spelontwikkelaars hebben de neiging om gamma te negeren omdat de effecten subtiel genoeg zijn om ongeveer gecorrigeerd te worden door het aanpassen van lichtintensiteiten, speculaire intensiteiten en dergelijke, maar om een ​​echte beeldkwaliteit te bereiken met realistisch uitziende verlichting, is het belangrijk om de gammawaarde en benodigde stappen te begrijpen om zijn aanwezigheid in digitale beeldvorming te omzeilen, om zo de best mogelijke kwaliteit te krijgen. Het toepassen van de juiste gammacorrectie is een van de meest moeiteloze manieren om het uiterlijk van uw real-time 3D-afbeeldingen radicaal te verbeteren.

Introductie: hoe monitoren werken

De CRT-monitoren die oorspronkelijk werden gebruikt voor computerschermen hebben een merkwaardige eigenschap: de kleurreactie op hun schermen is niet-lineaire met betrekking tot onbewerkte waarden doorgegeven vanaf de grafische kaart. 

Niet-lineair, in deze zin, betekent dat verhogingen van een van uw kleurencomponenten met een constante verhouding (bijvoorbeeld als een rode component van een kleur tweemaal zo hoog wordt) niet zal resulteren in een toename van de door de monitor uitgezonden lichtintensiteit met diezelfde verhouding (dat wil zeggen, het rode licht dat door het scherm wordt uitgezonden, zal niet twee keer zo hoog zijn).

De kleurrespons van een CRT-monitor is eigenlijk een exponentiële functie. (Zoals in alle fysica, is dit veel complexer dan we beschrijven, maar omwille van de eenvoud zullen we ons aan deze veronderstelling houden.) Dat wil zeggen, de functie EmittedLight (C), waar C is een waarde van een kleurcomponent (rood, groen of blauw) variërend van 0 (geen licht) aan 1 (volledige lichtintensiteit), is C verhoogd tot enige macht γ

Dit nummer, γ, wordt het gamma-exponent of gewoon gamma. Typische gammawaarden variëren van 2,0 tot 2,4, en wanneer het gaat om gamma in algemene zin, wordt de waarde overeengekomen om 2,2 te zijn als een compromis, en veel nieuwe monitors zijn ontworpen om de gamma-waarde van precies 2.2 te hebben

In een gebruikelijk scenario van gamma = 2,2, is dit hoe de monitor daadwerkelijk de kleurintensiteiten van uw spel weergeeft (groene curve). De gestippelde rode lijn laat zien hoe een lineaire monitor dezelfde intensiteiten zou weergeven.

In de praktijk betekent dit dat zwart en wit niet-vervormd op het scherm worden weergegeven (omdat nul verheven tot elke macht nul is en een verhoogd tot elke macht gelijk is aan één), maar alle waarden daartussen zullen scheef staan ​​zonder een betrouwbare manier om waar te nemen dit gebeurt gewoon door te kijken. 

Als u bijvoorbeeld een kleur weergeeft die zogenaamd twee keer donkerder is dan zwart, dat is, RGB (0,5, 0,5, 0,5)-het wordt feitelijk getoond als minder dan vier keer donkerder, gezien de gemeenschappelijke gammawaarde van 2,2, aangezien 0,5 verhoogd tot 2,2 ongeveer 0,22 is. Het is duidelijk dat dit niet is wat u van plan bent, en dit is niet het geval alleen met CRT-monitoren: LCD's, hoewel niet onopzettelijk met deze eigenschap, zijn ontworpen om compatibel te zijn met hun oudere tegenhangers, en tonen zo uw kleurwaarden zo uitgerekt.

Bovendien, aangezien rode, groene en blauwe componenten onafhankelijk worden behandeld, kunnen de bedoelde kleurtonen van afbeeldingen gemakkelijk worden gemangeld, omdat de intensiteiten van de drie kleurcomponenten niet uniform worden geschaald. Wat er zal gebeuren wanneer u de kleurwaarde weergeeft RGB (1, 0,5, 0,5)? De rode component blijft op 1, maar de andere komen terug tot de helft van hun waarden, waardoor de kleurtint volledig verandert.

De tweede kleur werd verkregen uit de eerste door toepassing van de niet-lineaire schaal die monitoren gebruiken. Merk op hoe niet alleen de helderheid van de kleur, maar ook de verzadiging ervan, werd beïnvloed door deze transformatie.

Nu we hebben gezien welke effecten deze monitoreigenschap heeft op de kleurgegevens die aan de monitor zijn gegeven, kunnen we zien welke stappen er zijn om deze te bestrijden.

Wat is gammacorrectie?

Gamma-correctie is het ongedaan maken van het ongelukkige werk van de monitor. Gamma-correctie van een afbeelding verhoogt in feite zijn kleurintensiteiten naar 1 / gamma, zodat wanneer de monitor op zijn beurt de waarde verhoogt naar gamma, deze worden geannuleerd en het resultaat is de kleur waarvan we oorspronkelijk wilden dat deze werd getoond. 

(Onthoudt dat EEN gestegen tot B, en vervolgens verhoogd naar C, is hetzelfde als EEN gestegen tot B x C, en dit is waarom deze operaties zullen annuleren, zoals gamma × (1 / gamma) is 1.) 

Omdat de gemiddelde gebruiker zijn monitor niet kalibreert om een ​​lineair antwoord te krijgen, worden veel afbeeldingen die ze tegenkomen gecorrigeerd zodat ze nooit het verschil voelen. Als een conventie worden de meeste afbeeldingsbestanden op het internet gedistribueerd in de zogenaamde sRGB-kleurruimte-dit betekent dat de originele, beoogde kleurwaarden dat zijn ongeveer opgewekt tot de macht van 1 / 2.2 voordat ze in bestanden worden geplaatst (hoewel complexere vergelijkingen in werkelijkheid plaatsvinden). Dit zorgt ervoor dat alle gebruikers met conventionele schermen de echte kleuren zien. Scanners, camera's en veel apparaten voor digitale beeldbewerking houden hier allemaal rekening mee en corrigeren hun uitvoer voor u bij het opslaan in conventionele beeldformaten.

Deze afbeelding toont de toewijzing van kleurintensiteiten zoals verzonden naar de monitor door de grafische kaart en intensiteiten die door de monitor worden weergegeven.

Bekijk de bovenstaande afbeelding. Als we geen rekening houden met gamma, is de curve exponentieel (lagere groene curve). Als we gammacorrectie uitvoeren, zal het feitelijke antwoord lineair zijn, zoals het zou moeten zijn. Ter vergelijking, de afbeelding laat ook zien hoe de grafiek eruitziet wanneer we gammacorrectie uitvoeren, maar de monitor heeft eigenlijk een lineaire respons. In dit geval zullen de intensiteiten in de tegenovergestelde richting worden vervormd, en we kunnen zien dat wanneer een niet-lineaire monitor ze op zijn beurt vervormt, dit wordt geannuleerd en we eindigen met een rechte lijn.

Wanneer moet ik me zorgen maken??

Tot nu toe hebben we de theorie achter deze fenomenen uitgelegd - zeker, monitoren zijn niet-lineair en de meeste afbeeldingen zijn gecorrigeerd zodat ze er recht op kijken op deze monitoren, maar wat lijkt het probleem te zijn? Waarom zou ik, een aspirant ontwikkelaar van 3D-games, mij bezighouden met gammacorrectie en alles doen net wetende over het?

Het antwoord is simpel: zolang afbeeldingen worden gemaakt om te worden weergegeven, bestaat het probleem zelfs niet. Zodra u echter wilt dat een programma iets aan deze afbeeldingen doet (ze schalen, ze als texturen gebruiken, noem maar op), moet u ervoor zorgen dat het programma weet dat de waarden niet echt en zijn gewoon gecorrigeerd zodat ze er echt uitzien op een monitor.

In het bijzonder gebeurt dit in een renderer wanneer textuurkaarten, zoals diffuse oppervlakken, als invoer worden gebruikt. Er worden bewerkingen op uitgevoerd, ervan uitgaande dat hun kleurwaarden nauwkeurig de lichtintensiteiten weergeven; dat wil zeggen, ervan uitgaande dat een lineaire correspondentie met real-life verschijnselen die ze vertegenwoordigen.

Maar dit is een fundamentele fout: als u de kleurwaarden wilt optellen en deze zijn gecorrigeerd met het gamma (verhoogd naar 1 / gamma) krijg je de verkeerde waarden. Er is geen wiskundig genie voor nodig om dat te beseffen EEN gestegen tot 1 / gamma plus B gestegen tot 1 / gamma is niet gelijk aan (A + B) gestegen tot 1 / gamma. Het probleem doet zich ook voor als een renderer een aantal waarden uitvoert, zoals wanneer deze lichtbijdragen uitvoert: als dat het geval is sommen twee lichtbijdragen maar weet niet dat het resultaat wordt verhoogd naar gamma wanneer het op het scherm wordt weergegeven, het heeft geproduceerd fout waarden.

En dit is precies waar het probleem zich voordoet: wanneer een renderer aanneemt dat de kleuren lineair corresponderen met werkelijke fenomenen wanneer dat niet het geval is, of ervan uitgaat dat kleuren die worden uitgevoerd lineair overeenkomen met de lichtintensiteiten op het scherm wanneer ze winnen ' Het heeft een behoorlijk ernstige fout gemaakt die het uiterlijk en het gevoel van de gemaakte afbeeldingen kan beïnvloeden.

Als u geen van de fouten corrigeert, zorg er dan niet voor dat de kleuren van de ingangstextuur die in de renderer worden ingevoerd lineair zijn en zorg er niet voor dat het uitvoerbeeld van de renderer lineair is ten opzichte van het scherm; deze afbeeldingen annuleren elke afbeelding andere tot op zekere hoogte, net als hoe ze elkaar annuleren wanneer ze onjuiste JPEG-bestanden in een webbrowser vertonen. Zodra u echter tussentijdse berekeningen opneemt die lineaire overeenkomsten veronderstellen, is uw wiskunde verkeerd.


(een) Niet corrigeren van texturen en niet corrigeren van de uiteindelijke afbeelding, (B) niet corrigeren van texturen maar corrigeren van de uiteindelijke afbeelding, (C) texturen corrigeren maar de uiteindelijke afbeelding niet corrigeren, (D) het corrigeren van zowel de texturen als de uiteindelijke afbeelding.

Denk aan wat we eerder zeiden over het veranderen van de kleurtinten, dat feit kan (soms) u helpen om niet-lineariteit te herkennen. Als vuistregel geldt: als u lineaire tweaks toepast op parameters (zoals het verdubbelen van de helderheid van lichten in de scène), verandert het resulterende beeld niet alleen in helderheid, maar ook in kleurtinten (bijvoorbeeld een gebied dat van een roodachtig oranje tint naar geel), dit betekent dat er waarschijnlijk een niet-lineair intermediair proces plaatsvindt.

Dit kan gebeuren met structuurmappen die zijn opgehaald uit verschillende bronnen: het internet, een digitale camera die sRGB JPEG opslaat, een scanner of als de textuur is geverfd op een monitor die niet expliciet is gekalibreerd voor een lineaire respons of niet expliciet achteraf gecorrigeerd. Elke wiskunde die op deze textuurkaarten wordt uitgevoerd, is onjuist en wijkt enigszins af van theoretisch juiste waarden. Dit is zichtbaar met textuurfiltering en mipmaps: aangezien bij het berekenen van het gemiddelde van kleurwaarden rekening wordt gehouden met filteren, zie je duidelijke fouten: kleinere texturen (verre) worden zichtbaar donkerder dan grotere (dat wil zeggen wanneer ze dichter bij je in de buurt zijn): Dit komt omdat wanneer ze zich op grote afstand bevinden, het filteralgoritme meer monsters maakt en hun niet-lineariteit het resultaat meer beïnvloedt.

Verlichting zal ook last hebben van onjuist gamma: lichte bijdragen aan oppervlakken som in de echte wereld, en bijgevolg in een renderer, maar optellen is geen getrouwe bewerking als het resultaat niet-lineair scheef is. Als je complexe fragmentschaduwen hebt die geavanceerde verlichting gebruiken, zoals suburface-verstrooiing of HDR, worden de fouten steeds duidelijker, tot het punt dat je werkelijk vraag me af wat er mis is met het beeld, in tegenstelling tot een ongemakkelijk gevoel van "misschien een verkeerde verlichting, maar het is waarschijnlijk alleen mij", wat ook vaak kan gebeuren. Het verdonkeren van de texturen of het helderder maken van de uiteindelijke beelden met een constante of lineaire factor doodt het effect niet, omdat het ook lineaire bewerkingen zijn en u een niet-lineaire bewerking nodig hebt om de inherente exponentiële responscurve te bestrijden die in de monitor gebeurt.

Hoe los ik het op?

Hopelijk bent u zich er nu volledig van bewust wat gamma- en gammacorrectie zijn en waarom dit zo belangrijk is bij realtime 3D-afbeeldingen. Maar er moet natuurlijk een manier zijn om deze problemen op te lossen? 

Het antwoord is ja en het corrigeren van gamma is een vrij eenvoudige handeling waarbij je niets hoeft te veranderen naast het toevoegen van een paar regels code, zonder extra parameters, intensiteit en kleuraanpassingen die je moet uitvoeren om de juiste belichting te krijgen als je hebt je scènes ingesteld om er goed uit te zien op niet-lineaire monitoren zonder ze te corrigeren. 

Er zijn drie basisstappen om ervoor te zorgen dat je zo lang mogelijk lineair blijft en de correctie op het juiste moment doet:

1. Zorg ervoor dat uw textuurkleuren juist zijn

U moet normaal gesproken de bronafbeeldingen niet wijzigen zodat ze lineaire kleuren bevatten; kleuren in gamma-gecorrigeerd voor de standaardmonitor in 8-bits kleurvelden bieden u de benodigde toegevoegde resolutie in donkere gebieden waar het menselijk oog gevoeliger is voor intensiteitsvariaties. U kunt er echter voor zorgen dat de kleurwaarden lineair zijn voordat ze uw shaders bereiken. 

Normaal gesproken, in OpenGL, kunt u dit doen door te passeren GL_SRGB8 in plaats van GL_RGB8, en GL_SRGB8_ALPHA8 in plaats van GL_RGBA8, naar glTexImage2D (), bij het specificeren van een textuur. Dit zal ervoor zorgen dat alle waarden die van deze textuur worden uitgelezen via een shader-sampler, worden gecorrigeerd terug van de sRGB-kleurruimte naar een lineaire, dat is precies wat we nodig hebben! Als u een rendering- of game-engine gebruikt die texture-loading voor u laadt, kan hier mogelijk rekening mee worden gehouden of moet u deze mogelijk handmatig opgeven. raadpleeg de documentatie van de bibliotheek of vraag iemand om hulp als u niet zeker bent.

Zorg er echter voor dat u dit niet ten onrechte doet aan afbeeldingen die per definitie geen kleurinformatie vertegenwoordigen en expliciet zijn geschilderd met dit in gedachten. Voorbeelden hiervan zijn normale kaarten, bump maps of hoogtekaarten, die allemaal coderen voor sommige andere gegevens dan kleur in de kleurkanalen van een textuur en daarom waarschijnlijk niet dit type voorbewerking nodig hebben.

Van de demo opgenomen in dit artikel (sommige parameters geruild met hun werkelijke waarden voor de duidelijkheid):

glTexImage2D (GL_TEXTURE_2D, 0, GL_RGB8, width, height, 0, GL_BGR, GL_UNSIGNED_BYTE, data); 

Hierdoor wordt de textuur geladen in een niet-gecorrigeerde kleurenruimte. Als de gegevens in het textuurbestand zich echter in de sRGB-kleurruimte bevinden, moeten we de derde parameter wijzigen in GL_SRGB8, opbrengst:

glTexImage2D (GL_TEXTURE_2D, 0, GL_SRGB8, width, height, 0, GL_BGR, GL_UNSIGNED_BYTE, data);

Dit zorgt ervoor dat OpenGL de textuurgegevens corrigeert wanneer we ze opzoeken.

2. Zorg ervoor dat de kleuren van uw uitvoer goed zijn

Nu moet je kleurcorrectie toepassen op de uiteindelijke uitvoerafbeeldingen van je renderer. Zorg ervoor dat je de correctie op niets anders toepast dan de laatste framebuffer die op het scherm moet worden weergegeven. (Raak de tussentijdse buffers die worden ingevoerd voor andere nabewerkingsarilla's niet aan, omdat deze nog steeds verwachten te werken met lineaire waarden.) 

Dit kan gedaan worden in OpenGL door de renderbuffer (de laatste, niet-sampleable framebuffer) op te geven om een ​​sRGB-kleurcodering te hebben door te slagen GL_SRGB in plaats van GL_RGB als een parameter voor glRenderbufferStorage (). Daarna moet je de. Verhogen GL_FRAMEBUFFER_SRGB vlag door te bellen glEnable. Op deze manier worden shader-schrijven naar sRGB-buffers gecorrigeerd zodat ze direct op een standaardmonitor worden weergegeven. 

Als je een engine of een framework gebruikt, bevat deze waarschijnlijk een of andere optie om een ​​sRGB-framebuffer voor je te maken en deze goed in te stellen. Nogmaals, u kunt de documentatie van de bibliotheek raadplegen of iemand vragen om dit voor u te verduidelijken.

In de demo gebruiken we de GLFW-bibliotheek, die ons een pijnloze manier biedt om een ​​sRGB-framebuffer aan te vragen. We hebben in het bijzonder een vensterhint ingesteld en laten we vervolgens OpenGL vertellen dat de framebufferbewerkingen zich in de sRGB-ruimte bevinden:

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

3. Repareer uw geknikte lichtintensiteiten en kleurparameters

Als dit geen begin is van een nieuw project, is de kans groot dat u door het ontbreken van gamma-incorrecte verlichting en filtering zijn tol heeft geëist. Misschien heb je je diffuse reflectiekleuren, lichtintensiteiten en wat niet aangepast in een poging om de subtiele overlast te compenseren die het verwaarlozen van gamma je heeft opgeleverd. 

Je moet deze waarden nog een keer doornemen en aanpassen zodat ze er weer goed uitzien, maar deze keer zullen je scènes er natuurlijker uitzien omdat de belichting nauwkeuriger de realiteit weergeeft. Hoeken zien er niet te donker uit, dus je hoeft niet meer intensiteit aan de lichten toe te voegen (waardoor de verlichting van helderdere objecten kapot gaat, die er dan kunstmatig helder uitzien voor die hoeveelheid licht in de scène). 

Dit loont: het opnieuw bezoeken van uw parameters om een ​​natuurlijke omgeving met gammacorrectie te creëren, zal uw gebruikers een ervaring en een helderheidsverdeling bieden die precies bij hun ogen past, zo gewend en gevoelig voor hoe licht in het echte leven werkt.

demonstratie

In dit artikel is een kleine OpenGL 3.3-demo opgenomen met een eenvoudige scène met enkele texturen die verlicht worden door twee bewegende lichtbronnen. Hiermee kunt u schakelen tussen verschillende scenario's: niet corrigeren van structuren maar corrigeren van de uiteindelijke afbeelding; het corrigeren van structuren maar het negeren van het uiteindelijke beeld; beide corrigeren (dat wil zeggen, alles goed doen); en ook niet corrigeren (effectief een dubbele fout maken). 

De demo is geschreven in C ++ (met twee GLSL-shaders) en maakt gebruik van draagbare GLFW- en GLEW-bibliotheken, zodat deze op een groot aantal verschillende platforms moet worden uitgevoerd. De broncode is rijp met opmerkingen, zodat u alles kunt doen en elk aspect van deze korte toepassing kunt verkennen.

De demo in actie.

Gebruik de 1 toets op uw toetsenbord om te schakelen tussen het corrigeren van structuren en het niet corrigeren van structuren, en de 2 toets om te schakelen tussen het corrigeren van de framebuffer en het niet corrigeren van de framebuffer. Als u beide tegelijk wilt doorlopen, drukt u op 3-handig om het verschil te zien tussen het volledig verwaarlozen van gamma (twee fouten die elkaar grotendeels opheffen) en alles goed doen. Wanneer de demo start, worden geen van deze correcties uitgevoerd, dus druk op 3 om de voordelen van een juiste gammacorrectie te zien.

Ik heb een Microsoft Visual C ++ 2013-project, compatibele 64-bits versies van de GLFW- en GLEW-bibliotheken en een 64-bits Windows-uitvoerbaar bestand opgenomen. U kunt dit echter vrij gemakkelijk compileren op elk platform met GLFW- en GLEW-ondersteuning: alleen compileren main.cpp en loader.cpp samen en link ze tegen die twee bibliotheken. Onder Linux installeert u deze bibliotheken via uw pakketbeheerder en doorgeeft -lglew -lglfw naar g++ zou het moeten doen. (Houd er rekening mee dat dit niet is getest op andere besturingssystemen dan Windows, maar het zou moeten werken. Als u problemen ondervindt, kunt u me dit laten weten in de opmerkingen en ik zal ze zo snel mogelijk oplossen.)

Zoals je kunt zien tijdens het uitvoeren van de demo, zijn de effecten zelfs merkbaar met een eenvoudig model en een eenvoudige scène als deze. Natuurlijk, in dit eenvoudige geval, zou je misschien weg kunnen komen met het aanpassen van de arceringsparameters, zodat het beeld er goed uitziet als het niet wordt gecorrigeerd. Zodra je echter begint met het opbouwen van complexiteit in je scènes, zal het verschil gewoon te goed zichtbaar zijn om ooit op deze manier te compenseren.

Conclusie

In dit artikel hebben we termen behandeld als gamma, gammacorrectie, niet-lineaire invoer en uitvoer en niet-lineaire wiskunde. Hopelijk heb ik je kunnen overtuigen dat je je nu al zorgen moest gaan maken over gammacorrectie als je dit tot nu toe had verwaarloosd, en als je voorzichtig bent geweest met gamma voordat je dit artikel tegenkwam, ik hoop alleen dat je wat nieuwe hebt gekregen klein stukje informatie om het probleem aan te pakken. 

We hebben vooral geleerd hoe we problemen kunnen oplossen die optreden wanneer u onjuiste bewerkingen uitvoert op kleurwaarden, ervan uitgaande dat ze lineair zijn en we hebben veelvoorkomende valkuilen en symptomen besproken die optreden wanneer u dit belangrijke aspect van computergraphics verwaarloost.

Ik hoop dat je plezier hebt gehad en iets nieuws hebt geleerd tijdens het lezen van dit artikel. Tot de volgende keer!