Hoe om Shader te Gebruik om die Kleur van Sprite Dinamies te Vrsprei
by Daniel Branicki 25 Nov 2015
ShadersGame Art EffectsSpritesheetsHow-To3D Rendering
In hierdie, handleiding sal ons 'n eenvoudige kleurskakelaar skep wat vinnig sprites kan kleur. Shaders maak dit makliker om variasies aan die spel by te voeg, sodat spelers hul karakters kan aanpas, en kan gebruik word om spesiale effekte aan sprites toe te voeg, soos om hulle te laat blink wanneer die karakters skade aanrig.
Alhoewel ons Unity vir demo's en bronkode hier gebruik, sal die basiese beginsel in baie wild enjins en programmeringstale werk.
Jy kan kyk the Unity demo, of the WebGL version (25MB+), om die finale uitslag in aksie te sien. Gebruik die kleurkieser om die boonste karakters te kleur. (Alle ander karakters gebruik dieselfde sprite, maar het ook kleur verander.) Klik op Hit Effect om alle karakters kortliks kort te maak.
Hier is die voorbeeld tekstuur wat ons gaan gebruik om die skaduwee te demonstreer:
Ek het hierdie tekstuur afgelaai http://opengameart.org/content/classic-hero,en redigeer 'n bietjie.
Daar is nogal 'n paar kleure op hierdie tekstuur. Hier is hoe die palet lyk:
Nou, kom ons dink oor hoe ons hierdie kleure in shaders kan uitruil.
Elke kleur het 'n unieke RGB waarde wat daarmee gepaard gaan. Dit is dus interessant om 'n skaderkode te skryf wat sê: "As die tekstuurkleur gelyk is aan hierdie RGB waarde, vervang dit met die RGB waarde". Dit is egter, nie goed genoeg vir baie kleure nie, en dit is redelik duur. Ons wil beslis voorwaardelike stellings heeltemal vermy, die feite.
In plaas daarvan gebruik ons ​​'n addisionele tekstuur wat die vervangende kleure bevat. Kom ons noem hierdie tekstuur 'n ruil tekstuur.
Die groot vraag is hoe skakel ons die kleur van die sprite tekstuur na die kleur van die ruil tekstuur? Die antwoord is, ons sal die rooi komponent (R) van die RGB-kleur gebruik om die ruiltekstuur te indekseer. Dit beteken dat die ruil tekstuur 256 pixels wyd moet wees, want dit is hoeveel verskillende waardes die rooi komponent kan neem.
Kom ons gaan hieroor in 'n voorbeeld. Hier is die rooi kleurwaardes van die sprite-palet se kleure:
Kom ons sê ons wil die omtrek/oogkleur (swart) op die sprite vervang met die kleur blou. Die lynkleur is die laaste een op die palet - wat 'n rooi waarde van 25 het. As ons hierdie kleur wil ruil, moet ons in die ruiltekstuur die pixel by indeks 25 instel na die kleur wat ons wil hê die omtrek moet wees: blou.
Die ruil tekstuur, met die kleur by indeks 25 tot blou.
Nou, wanneer die skaker die kleur met 'n rooi waarde van 25 meet, sal dit dit vervang met die blou kleur van die ruiltekstuur:
Let daarop dat dit dalk nie soos verwag sal werk as twee of meer kleure op die sprite tekstuur dieselfde rooi waarde deel nie! By die gebruik van hierdie metode is dit belangrik om die rooi waardes van die kleure in die sprite-tekstuur anders te hou.
Let ook op, soos u in die demo kan sien, plaas 'n deursigtige pixel op enige indeks in die ruiltekstuur, sodat daar geen kleuruitruilings vir die kleure wat ooreenstem met die indeks.
Implementeer die Shader
Ons sal hierdie idee toepas deur die bestaande sprite shader te verander. Aangesien die demo-projek in Unity gemaak word, sal ek Sprite Shrite Eenheidstandaard.
Al die verstek shader (wat relevant is vir hierdie handleiding) is die kleur van die hoof tekstuur atlas en vermenigvuldig daardie kleur met 'n vertex kleur om die tint te verander. Die gevolglike kleur word dan vermenigvuldig met alfa, om die sprite donkerder te maak by laer deursigtighede.
Die eerste ding wat ons moet doen is om 'n addisionele tekstuur by die skaduwee te voeg:
Soos u kan sien, het ons nou twee teksture hier. Die eerste een, _MainTex, is die sprite-tekstuur; Die tweede een, _SwapTex, is die ruiltekstuur.
Ons moet ook 'n sampler spesifiseer vir die tweede tekstuur, sodat ons dit eintlik kan verkry. Ons sal 'n 2D tekstuurmonster gebruik, aangesien Unity nie 1D monsternemers ondersteun nie:
Nou kan ons die fragment shader uiteindelik verander:
Hier is die toepaslike kode vir die standaard fragment shader. Soos u kan sien, is c die kleur wat uit die hooftekstuur bemonster word; Dit word vermenigvuldig met die verteks kleur om dit 'n tint te gee. Ook, die skadu donker sterretjie ontmoet laer troebelheid.
Nadat ons die hoofkleurmonster geneem het, laat ons die ruilkleur ook proe-maar voordat ons dit doen, laat ons die deel verwyder wat dit vermenigvuldig met die kleur van die kleur. Ons neem die monster met die regte rooi tekstuurwaarde, nie die kleure nie.
Soos u kan sien, is die monster-kleurindeks gelyk aan die rooi waarde van die hoofkleur.
Nou, laat ons ons laaste kleur bereken:
Om dit te doen, moet ons interpolasie tussen die hoofkleur en die kleur van die uitruil met alfa van die kleurruil as 'n stap. Op hierdie manier, as die ruil kleur deursigtig is, sal die finale kleur gelyk wees aan die hoofkleur; maar as die ruil kleur heeltemal ondeursigtig is, sal die finale kleur gelyk wees aan die ruil kleur.
Moenie vergeet dat die laaste kleur met kleur vermenigvuldig moet word nie:
Nou moet ons oorweeg wat moet gebeur as ons 'n kleur op die hooftekstuur wil verander wat nie heeltemal ondeursigtig is nie. Byvoorbeeld, as ons 'n blou, semi-deursigtige spooksprite het en die kleur na pers wil verander, wil ons nie sprake hê nie, met kleure om te vervaag, ons wil die oorspronklike deursigtigheid handhaaf. Dus, kom ons doen dit
Die finale kleur deursigtigheid moet gelyk wees aan die deursigtigheid van die hoof tekstuur kleur.
Ten slotte, aangesien die oorspronklike shader die RGB-waarde van kleur met alfa kleur vermenigvuldig, moet ons dit ook doen om die skaduwee dieselfde te hou:
Die skaduwee is nou afgehandel; ons kan 'n ruil kleur tekstuur skep, vul dit met verskillende kleur pixels, en kyk of die sprite kleur verander korrek.
Natuurlik, sou hierdie metode nie baie handig wees as ons die hele tyd deur die hand gesit moet word nie! Ons sal die prosedure wil genereer en verander ...
Sample Demo Opstel
Ons weet ons benodig 'n ruil tekstuur om ons shaders te kan gebruik. Verder, as ons wil hê dat sommige karakters gelyktydig verskillende karakters gebruik, sal elkeen van hierdie karakters 'n eie ruiltekstuur hê.
Dit sal dan die beste wees as ons hierdie ruil teksture eenvoudig skep, soos ons die voorwerpe skep.
Eerstens, laat ons 'n ruil tekstuur en 'n skikking definieer waarin ons al die gekruiste kleure sal byhou:
Kom ons skep dan 'n funksie waar ons die tekstuur sal initialiseer. Ons gebruik RGBA32 formaat en stel die filter af na Punt:
public void InitColorSwapTex()
Kom ons maak seker dat alle deursigtige tekstuurpixels deur al die pixels te verwyder en die veranderinge toe te pas:
for (int i = 0; i < colorSwapTex.width; ++i)
Ons moet ook die materiaal se ruiltekstuur aan die nuutgeskepte een stel:
Ten slotte, slaan ons die verwysing na die tekstuur op en skep die skikking vir die kleure:
Die volledige funksie is soos volg:
Let daarop dat dit nie elke voorwerp nodig het om afsonderlike 256x1px teksture te gebruik nie; Ons kan 'n groter tekstuur skep wat alle voorwerpe dek. As ons 32 karakters benodig, kan ons 'n tekstuur van grootte 256x32px maak, en maak seker dat elke karakter slegs 'n spesifieke ry in daardie tekstuur gebruik. Wanneer ons egter, hierdie groter tekstuur moet verander, moet ons meer data na die GPU stuur, wat dit waarskynlik minder doeltreffend sal maak.
Dit is ook nie nodig om 'n aparte ruil tekstuur vir elke sprite te gebruik nie. Byvoorbeeld, as 'n karakter met 'n wapen toegerus is en die wapen 'n afsonderlike sprite is, kan dit maklik 'n ruil tekstuur met 'n karakter deel (solank die sprite tekstuur van die wapen nie 'n rooi waarde wat identies is aan 'n karaktersprite gebruik nie).
Dit is baie nuttig om te weet wat die rooi waardes van spesifieke sprite dele is, dus kom ons skep 'n enum wat hierdie data sal bevat:
public enum SwapIndex
Dit is al die kleure wat deur die voorbeeldkarakter gebruik word.
Nou het ons alles wat ons nodig het om 'n funksie te skep om die kleure heeltemal te verander:
public void SwapColor(SwapIndex index, Color color)
Soos jou kan sien, is daar niks hierin fancy nie; ons het net die kleur in ons voorwerp se kleur skikking gestel en ook die tekstuur se pixel op 'n toepaslike indeks gestel.
Let daarop dat ons nie werklik veranderinge aan die tekstuur wil aanbring wanneer ons hierdie funksie eintlik noem nie; Ons verkies om dit toe te pas nadat ons al die pixels wat ons wil los.
Kom ons kyk na 'n voorbeeld van die gebruik van hierdie funksie:
Soos u kan sien, is dit redelik maklik om te verstaan ​​wat hierdie funksie oproepe doen, net deur hulle te lees. In hierdie geval verander hulle beide velkleure, beide hemp kleure en die broek kleur.
Bygevoeg Hit Effects Demo
Kom ons kyk nou hoe ons die skadu kan gebruik om 'n treffende effek vir ons sprite te skep. Hierdie effek sal al die kleure van die sprite na wit ruil, hou dit so vir 'n kort tydperk en dan terug na die oorspronklike kleur. Die hele effek is 'n wit flitsende sprite.
Eerste van alles, laat ons 'n funksie skep wat alle kleure uitruil, maar eintlik ignoreer die kleur van die objek skikking nie. Ons sal hierdie kleure nodig hê as ons die treffer effek wil afskakel.
public void SwapAllSpritesColorsTemporarily(Color color)
for (int i = 0; i < mColorSwapTex.width; ++i)
Ons kan slegs deur die enum herhaal, maar herhaal dat die hele tekstuur sal verseker dat die kleur uitgeruil word, selfs al word 'n sekere kleur nie in SwapIndex gedefinieer nie.
Nou wissel die kleur, ons moet 'n rukkie wag en terugkeer na die vorige kleur.
Eerstens, laat ons 'n funksie skep wat kleure sal herstel:
public void ResetAllSpritesColors()
Nou, kom ons definieer die timers en konstantes:
float mHitEffectTimer = 0.0f;
const float cHitEffectTime = 0.1f;
Kom ons skep 'n funksie wat begin met klik effek:
public void StartHitEffect()
En in die opdateringsfunksie, laat ons kyk hoeveel tyd in die timer oorgebly het, minimaliseer enige bosluise en versoek reset wanneer die tyd op is.
if (mHitEffectTimer > 0.0f)
if (mHitEffectTimer <= 0.0f)
Dit is dit - nou, wanneer StartHitEffect genoem word, blink die sprite 'n oomblik vir 'n oomblik en dan terug na sy vorige kleur.
Dit is die einde van die tutoriaal! Ek hoop jy vind 'n aanvaarbare metode en skaduwee nuttig. Dit is regtig eenvoudig, maar werk goed vir pixel kuns sprites wat nie baie kleure gebruik nie.
Hierdie metode moet 'n bietjie verander word as ons die hele kleurgroep gelyktydig wil ruil, wat beslis 'n meer ingewikkelde en duur skaduwee vereis. In my eie spel gebruik ek 'n bietjie kleur, so hierdie tegniek is perfek.