Negli articoli precedenti, abbiamo imparato come scrivere semplici vertex e frammenti di shader, creare una semplice pagina Web e preparare una tela per disegnare. In questo articolo, inizieremo a lavorare sul nostro codice boilerplate WebGL.
Otterremo un contesto WebGL e lo useremo per cancellare la tela con il colore della nostra scelta. Woohoo! Questo può essere un minimo di tre righe di codice, ma ti prometto che non lo renderò così facile! Come al solito, cercherò di spiegare gli ingannevoli concetti di JavaScript mentre li incontriamo e di fornirti tutti i dettagli necessari per capire e prevedere il comportamento WebGL corrispondente.
Questo articolo fa parte della serie "Guida introduttiva in WebGL". Se non hai letto le parti precedenti, ti consiglio di leggerle prima:
Nel primo articolo di questa serie abbiamo scritto un semplice shader che disegna un gradiente colorato e lo sbiadisce leggermente all'interno e all'esterno. Ecco lo shader che abbiamo scritto:
Nel secondo articolo di questa serie, abbiamo iniziato a lavorare per utilizzare questo shader in una pagina web. Facendo piccoli passi, abbiamo spiegato lo sfondo necessario dell'elemento canvas. Noi:
Ecco cosa abbiamo fatto finora:
In questo articolo prendiamo in prestito alcuni pezzi di codice dall'articolo precedente e adattiamo la nostra esperienza a WebGL anziché a disegni 2D. Nel prossimo articolo, se Allah vorrà, tratterò la gestione della visualizzazione e il ritaglio delle primitive. Ci vorrà un po ', ma spero che troverai l'intera serie molto utile!
Costruiamo la nostra pagina basata su WebGL. Useremo lo stesso codice HTML che abbiamo utilizzato per l'esempio di disegno 2D:
... con una modifica molto piccola. Qui chiamiamo la tela glCanvas
invece di solo tela
(MEH!).
Useremo anche lo stesso CSS:
html, body height: 100%; body margin: 0; canvas display: block; larghezza: 100%; altezza: 100%; sfondo: # 000;
Tranne il colore di sfondo, che ora è nero.
Non useremo lo stesso codice JavaScript. Iniziamo senza codice JavaScript e aggiungiamo funzionalità bit per bit per evitare confusione. Ecco la nostra configurazione finora:
Adesso scriviamo un po 'di codice!
La prima cosa che dovremmo fare è ottenere un contesto WebGL per il canvas. Proprio come abbiamo fatto quando abbiamo ottenuto un contesto di disegno 2D, usiamo la funzione membro getContext
:
glContext = glCanvas.getContext ("webgl") || glCanvas.getContext ( "sperimentale-WebGL");
Questa riga contiene due getContext
chiamate. Normalmente, non dovremmo aver bisogno della seconda chiamata. Ma nel caso in cui l'utente stia utilizzando un vecchio browser in cui l'implementazione WebGL è ancora sperimentale (o Microsoft Edge), abbiamo aggiunto il secondo.
La cosa bella del ||
operatore (o operatore) È che non deve valutare l'intera espressione se è stato trovato il primo operando vero
. In altre parole, in un'espressione a || B
, Se un
valuta a vero
, allora se B
è vero
o falso
non ha alcun effetto sul risultato. Quindi, non abbiamo bisogno di valutare B
ed è saltato interamente. Questo è chiamato Valutazione del cortocircuito.
Nel nostro caso, getContext ( "sperimentale-WebGL")
sarà eseguito solo se getContext ( "WebGL")
fallisce (ritorna nullo
, che valuta falso
in un'espressione logica).
Abbiamo anche usato un'altra funzionalità di o
operatore. Il risultato di oring non è né l'uno né l'altro vero
né falso
. Invece, lo è il primo oggetto che valuta vero
. Se nessuno degli oggetti è valutato vero
, o restituzioni l'oggetto più a destra nell'espressione. Ciò significa, dopo aver eseguito la riga sopra, glContext
conterrà o un oggetto di contesto o nullo
, ma no vero
o falso
.
Nota: se il browser supporta entrambe le modalità (WebGL
e sperimentale WebGL
) quindi vengono trattati come alias. Non ci sarebbe assolutamente alcuna differenza tra loro.
Mettendo la linea sopra a cui appartiene:
var glContext; function initialize () // Ottieni contesto WebGL, var glCanvas = document.getElementById ("glCanvas"); glContext = glCanvas.getContext ("webgl") || glCanvas.getContext ( "sperimentale-WebGL"); if (! glContext) alert ("Impossibile acquisire un contesto WebGL. Ci scusiamo!"); restituisce falso; return true;
Ecco! Abbiamo il nostro inizializzare
funzione (sì, continua a sognare!).
Si noti che non abbiamo usato provare
e catturare
Rilevare getContext
problemi come abbiamo fatto nel precedente articolo. È perché WebGL ha i suoi meccanismi di segnalazione degli errori. Non genera un'eccezione quando la creazione del contesto fallisce. Invece, spara a webglcontextcreationerror
evento. Se siamo interessati al messaggio di errore, dovremmo probabilmente farlo:
// Listener di errori di creazione del contesto, var errorMessage = "Impossibile creare un contesto WebGL"; function onContextCreationError (event) if (event.statusMessage) errorMessage = event.statusMessage; glCanvas.addEventListener ("webglcontextcreationerror", onContextCreationError, false);
Prendendo queste linee a parte:
glCanvas.addEventListener ("webglcontextcreationerror", onContextCreationError, false);
Proprio come quando abbiamo aggiunto un listener all'evento di caricamento della finestra nell'articolo precedente, abbiamo aggiunto un listener al canvas webglcontextcreationerror
evento. Il falso
l'argomento è facoltativo; Lo sto solo includendo per completezza (dal momento che l'esempio delle specifiche WebGL ce l'ha). Di solito è incluso per la retrocompatibilità. Sta per useCapture
. quando vero
, significa che l'ascoltatore verrà chiamato nel fase di acquisizione della propagazione dell'evento. Se falso
, sarà chiamato nel fase di gorgogliamento anziché. Controlla questo articolo per ulteriori dettagli sulla propagazione degli eventi.
Ora per l'ascoltatore stesso:
var errorMessage = "Impossibile creare un contesto WebGL"; function onContextCreationError (event) if (event.statusMessage) errorMessage = event.statusMessage;
In questo listener, manteniamo una copia del messaggio di errore, se presente. Sì, avere un messaggio di errore è totalmente facoltativo:
if (event.statusMessage) errorMessage = event.statusMessage;
Quello che abbiamo fatto qui è piuttosto interessante. messaggio di errore
è stato dichiarato al di fuori della funzione, eppure lo abbiamo usato all'interno. Questo è possibile in JavaScript e viene chiamato chiusure. Ciò che è interessante delle chiusure è la loro vita. Mentre messaggio di errore
è locale al inizializzare
funzione, dal momento che è stato utilizzato all'interno onContextCreationError
, non sarà distrutto a meno che onContextCreationError
stesso non è più referenziato.
In altre parole, finché un identificatore è ancora accessibile, non può essere raccolto. Nella nostra situazione:
messaggio di errore
vive perché onContextCreationError
lo fa riferimento.onContextCreationError
vive perché è referenziato da qualche parte tra gli ascoltatori di eventi su tela. Quindi, anche se inizializzare
termina, onContextCreationError
è ancora referenziato da qualche parte nell'oggetto canvas. Solo quando è liberato può messaggio di errore
essere raccolto dai rifiuti. Inoltre, le successive chiamate di inizializzare
non influenzerà il precedente messaggio di errore
. Ogni inizializzare
la chiamata alla funzione avrà il suo messaggio di errore
e onContextCreationError
.
Ma non vogliamo davvero onContextCreationError
vivere oltre inizializzare
terminazione. Non vogliamo ascoltare altri tentativi di ottenere contesti WebGL in qualsiasi altro punto del codice. Così:
glCanvas.removeEventListener ("webglcontextcreationerror", onContextCreationError, false);
Mettere tutto insieme:
Per verificare che abbiamo creato con successo il contesto, ho aggiunto un semplice mettere in guardia
:
alert ("Contesto WebGL creato con successo!");
Ora passa al Risultato
scheda per eseguire il codice.
E non funziona! Ovviamente, perché inizializzare
non è mai stato chiamato. Dobbiamo chiamarlo subito dopo aver caricato la pagina. Per questo, aggiungeremo queste righe sopra di esso:
window.addEventListener ('load', function () initialize ();, false);
Proviamo di nuovo:
Funziona! Voglio dire, dovrebbe a meno che non si possa creare un contesto! In caso contrario, assicurati di visualizzare questo articolo da un dispositivo / browser compatibile con WebGL.
Nota che abbiamo fatto un'altra cosa interessante qui. Abbiamo usato inizializzare
nel nostro caricare
ascoltatore prima che fosse addirittura dichiarato. Questo è possibile in JavaScript a causa di sollevamento. Sollevare significa che tutte le dichiarazioni vengono spostate nella parte superiore del loro ambito, mentre le loro inizializzazioni rimangono al loro posto.
Ora, non sarebbe bello provare se il nostro meccanismo di segnalazione degli errori funziona davvero? Abbiamo bisogno getContext
fallire. Un modo semplice per farlo è ottenere un diverso tipo di contesto per la tela prima di tentare di creare il contesto WebGL (ricorda quando abbiamo detto che il primo ha avuto successo getContext
cambia la modalità canvas in modo permanente?). Aggiungeremo questa riga appena prima di ottenere il contesto WebGL:
glCanvas.getContext ( "2d");
E:
Grande! Ora se il messaggio che hai visto è stato "Impossibile creare un contesto WebGL
"o qualcosa di simile"La tela ha un contesto esistente di un tipo diverso
"dipende dal fatto che il tuo browser supporti webglcontextcreationerror
o no. Al momento di scrivere questo articolo, Edge e Firefox non lo supportano (era programmato per Firefox 49, ma non funziona ancora su Firefox 50.1). In tal caso, il listener di eventi non verrà chiamato e messaggio di errore
rimarrà impostato su "Impossibile creare un contesto WebGL
". Per fortuna, getContext
ancora ritorna nullo
, quindi sappiamo che non siamo riusciti a creare il contesto. Semplicemente non abbiamo il messaggio di errore dettagliato.
Il problema dei messaggi di errore WebGL è che ... non ci sono messaggi di errore WebGL! WebGL restituisce numeri che indicano stati di errore, non messaggi di errore. E quando capita di consentire i messaggi di errore, sono dipendenti dal driver. La formulazione esatta dei messaggi di errore non è fornita nelle specifiche: spetta agli sviluppatori di driver decidere come debbano essere messi. Quindi aspettati di vedere lo stesso errore formulato in modo diverso su dispositivi diversi.
Va bene allora. Dal momento che abbiamo fatto in modo che il nostro meccanismo di segnalazione degli errori funzioni, il "creato con successo
"avviso e getContext ( "2d")
non sono più necessari Li ometteremo.
Torna ai nostri riveriti getContext
funzione:
glContext = glCanvas.getContext ("webgl");
C'è dell'altro più di quanto sembri. getContext
può opzionalmente assumere un altro argomento: un dizionario che contiene un insieme di attributi di contesto e i loro valori. Se nessuno è fornito, vengono utilizzate le impostazioni predefinite:
dizionario WebGLContextAttributes GLboolean alpha = true; GLboolean depth = true; Stencil goboleano = falso; Antialias glicolico = vero; Premloboverso GLbooleanoAlpha = true; GLboolean preserveDrawingBuffer = false; GLboolean preferLowPowerToHighPerformance = false; GLboolean failIfMajorPerformanceCaveat = false; ;
Spiegherò alcuni di questi attributi mentre li usiamo. È possibile trovare ulteriori informazioni su di essi nella sezione Attributi di contesto WebGL delle specifiche WebGL. Per ora, non abbiamo bisogno di a buffer di profondità per il nostro semplice shader (ne parleremo più avanti). E per evitare di doverlo spiegare, disabiliteremo anche premoltiplicato-alfa! Ci vuole un articolo a parte per spiegare correttamente la logica dietro di esso. Quindi, il nostro getContext
la linea diventa:
var contextAttributes = depth: false, premultipliedAlpha: false; glContext = glCanvas.getContext ("webgl", contextAttributes) || glCanvas.getContext ("experimental-webgl", contextAttributes);
Nota: profondità
, stampino
e antialias
attributi, se impostato su vero
, sono richieste, non requisiti. Il browser dovrebbe cercare il meglio per soddisfarli, ma non è garantito. Tuttavia, quando sono impostati su falso
, il browser deve rispettare.
D'altra parte, il alfa
, premultipliedAlpha
e preserveDrawingBuffer
gli attributi sono requisiti che devono essere soddisfatti dal browser.
Ora che abbiamo il nostro contesto WebGL, è ora di usarlo! Una delle operazioni di base nel disegno WebGL sta cancellando il buffer dei colori (o semplicemente la tela nella nostra situazione). La pulizia della tela viene eseguita in due passaggi:
Le chiamate OpenGL / WebGL sono costose e i driver dei dispositivi non sono garantiti per essere terribilmente intelligenti ed evitare lavori inutili. Pertanto, come regola generale, se possiamo evitare di utilizzare l'API, dovremmo evitare di utilizzarlo.
Quindi, a meno che non sia necessario cambiare il colore chiaro di ogni fotogramma o disegno a metà, dovremmo scrivere il codice impostandolo in una funzione di inizializzazione anziché in uno di disegno. In questo modo, viene chiamato solo una volta all'inizio e non con ogni frame. Dal momento che il clear-color non è l'unico variabile di stato che inizializzeremo, creeremo una funzione separata per l'inizializzazione dello stato:
function initializeState () ...
E chiameremo questa funzione dal inizializzare
funzione:
function initialize () ... // Se fallito, if (! glContext) alert (errorMessage); restituisce falso; initializeState (); ritorna vero;
Bellissimo! La modularità manterrà il nostro codice non così breve più pulito e più leggibile. Ora per popolare il initializeState
funzione:
function initializeState () // Imposta clear-color su red, glContext.clearColor (1.0, 0.0, 0.0, 1.0);
clearColor
accetta quattro parametri: rosso, verde, blu e alfa. Quattro galleggianti, i cui valori sono serrato all'intervallo [0, 1]. In altre parole, qualsiasi valore inferiore a 0 diventa 0, qualsiasi valore maggiore di 1 diventa 1 e qualsiasi valore compreso tra questi rimane invariato. Inizialmente, il clear-color è impostato su tutti gli zeri. Quindi, se il nero trasparente fosse ok con noi, avremmo potuto ometterlo del tutto.
Dopo aver impostato il colore chiaro, ciò che rimane è in realtà cancellare la tela. Ma non si può fare a meno di fare una domanda, dobbiamo assolutamente cancellare la tela?
Ai vecchi tempi, i giochi che eseguivano il rendering a schermo intero non avevano bisogno di cancellare lo schermo ogni fotogramma (prova a digitare idclip
in DOOM 2 e vai da qualche parte che non dovresti essere!). I nuovi contenuti sovrascrivono solo quelli vecchi e salveremmo l'operazione non banale.
Su hardware moderno, la cancellazione dei buffer è estremamente veloce. Inoltre, la cancellazione dei buffer può effettivamente migliorare le prestazioni! Per dirla semplicemente, se il contenuto del buffer non è stato cancellato, la GPU potrebbe dover recuperare i contenuti precedenti prima di sovrascriverli. Se sono stati cancellati, non è necessario recuperarli dalla memoria relativamente più lenta.
Ma cosa succede se non vuoi sovrascrivere l'intero schermo, ma aggiungerlo in modo incrementale? Come quando si realizza un programma di pittura. Si desidera disegnare solo i nuovi tratti, mantenendo quelli precedenti. Non ha senso l'atto di lasciare la tela senza schiarirsi?
La risposta è ancora no. Sulla maggior parte delle piattaforme che useresti doppio buffer. Ciò significa che tutto il disegno che eseguiamo è fatto su a back buffer mentre il monitor recupera i suoi contenuti da a buffer frontale. Durante la ritraccia verticale, questi buffer vengono scambiati. La parte posteriore diventa anteriore e quella anteriore diventa la parte posteriore. In questo modo evitiamo di scrivere sulla stessa memoria che viene attualmente letta dal monitor e visualizzata, evitando così artefatti dovuti a disegno incompleto o disegno troppo veloce (avendo disegnato diversi frame scritti mentre il monitor ne sta ancora tracciando uno solo).
Pertanto, il frame successivo non sovrascrive il frame corrente, perché non è scritto nello stesso buffer. Invece, sovrascrive quello che era nel buffer frontale prima di scambiare. Questo è l'ultimo frame. E qualunque cosa abbiamo disegnato in questa cornice non apparirà nel prossimo. Apparirà nel prossimo. Questa incoerenza tra i buffer causa uno sfarfallio normalmente non desiderato.
Ma questo avrebbe funzionato se stessimo usando una singola configurazione bufferizzata. In OpenGL su molte piattaforme, abbiamo il controllo esplicito sul buffering e lo swapping dei buffer, ma non su WebGL. Spetta al browser gestirlo da solo.
Umm ... Forse non è il momento migliore, ma c'è una cosa che riguarda la pulizia del buffer di disegno che non ho menzionato prima. Se non lo chiariamo esplicitamente, ciò sarebbe implicitamente chiarito per noi!
Ci sono solo tre funzioni di disegno in WebGL 1.0: chiaro
, drawArrays
, e drawElements
. Solo se chiamiamo uno di questi nel buffer di disegno attivo (o se abbiamo appena creato il contesto o ridimensionato l'area di disegno), deve essere presentato al compositore di pagine HTML all'inizio della prossima operazione di compositing.
Dopo il compositing, i buffer di disegno sono automaticamente cancellato. Il browser può essere intelligente ed evitare di cancellare automaticamente i buffer se li cancelliamo da soli. Ma il risultato finale è lo stesso; i respingenti saranno comunque cancellati.
La buona notizia è che c'è ancora un modo per far funzionare il tuo programma di disegno. Se insisti a fare disegni incrementali, possiamo impostare il preserveDrawingBuffer
attributo contesto quando si acquisisce il contesto:
glContext = glCanvas.getContext ("webgl", preserveDrawingBuffer: true);
Ciò impedisce che il canvas venga automaticamente cancellato dopo il compositing e simula una singola configurazione bufferizzata. Un modo è fatto copiando il contenuto del buffer anteriore nel buffer posteriore dopo lo scambio. Il disegno su un back buffer è ancora necessario per evitare di disegnare artefatti, quindi non può essere aiutato. Questo, ovviamente, ha un prezzo. Potrebbe influire sulle prestazioni. Quindi, se possibile, utilizzare altri approcci per conservare il contenuto del buffer di disegno, come il disegno su a frame buffer object (che va oltre lo scopo di questo tutorial).
Preparati, libereremo la tela da un momento all'altro! Ancora una volta, per modularità, scriviamo il codice che disegna la scena ogni fotogramma in una funzione separata:
function drawScene () // Cancella il buffer dei colori, glContext.clear (glContext.COLOR_BUFFER_BIT);
Ora ce l'abbiamo fatta! chiaro
accetta un parametro, un campo di bit che indica quali buffer devono essere cancellati. Risulta che di solito abbiamo bisogno di più di un semplice buffer di colori per disegnare oggetti 3D. Ad esempio, un buffer di profondità viene utilizzato per tenere traccia delle profondità di ogni pixel disegnato. Usando questo buffer, quando la GPU sta per disegnare un nuovo pixel, può facilmente decidere se questo pixel occlude o è occluso dal pixel precedente che risiede al suo posto.
Va così:
Ho usato "più vicino" anziché "più piccolo" perché abbiamo il controllo esplicito su funzione di profondità (quale operatore usare in confronto). Decidiamo se un valore maggiore significa un pixel più vicino (sistema di coordinate destrorso) o viceversa (mancino).
La nozione di destrimano o mancino si riferisce alla direzione del pollice (asse z) mentre si piegano le dita dall'asse x all'asse y. Non sono bravo a disegnare, quindi, guarda questo articolo in Windows Dev Center. Per impostazione predefinita, WebGL è mancino, ma è possibile farlo destrimani modificando la funzione di profondità, purché si tenga conto dell'intervallo di profondità e delle trasformazioni necessarie.
Dato che abbiamo scelto di non avere un buffer di profondità quando abbiamo creato il nostro contesto, l'unico buffer che deve essere cancellato è il buffer dei colori. Quindi, impostiamo il COLOR_BUFFER_BIT
. Se avessimo un buffer di profondità, avremmo invece fatto questo:
glContext.clear (glContext.COLOR_BUFFER_BIT | glContext.GL_DEPTH_BUFFER_BIT);
L'unica cosa rimasta è chiamare drawScene
. Facciamolo subito dopo l'inizializzazione:
window.addEventListener ('load', function () // Inizializza tutto, initialize (); // Inizia a disegnare, drawScene ();, false);
Passare al Risultato
scheda per vedere il nostro bel rosso chiaro!
Uno dei fatti importanti su chiaro
è che non applica alcun alfa-compositing. Anche se usiamo esplicitamente un valore per alpha che lo rende trasparente, il clear-color verrebbe semplicemente scritto nel buffer senza alcun compositing, sostituendo tutto ciò che è stato disegnato prima. Pertanto, se hai una scena disegnata sulla tela e poi la si cancella con un colore trasparente, la scena verrà completamente cancellata.
Tuttavia, il browser esegue ancora il compositing alfa per l'intero canvas e utilizza il valore alfa presente nel buffer dei colori, che avrebbe potuto essere impostato durante la cancellazione. Aggiungiamo un testo sotto la tela e poi cancelliamo con un colore rosso semitrasparente per vederlo in azione. Il nostro HTML sarebbe:
Shhh, mi sto nascondendo dietro la tela in modo che tu non possa vedermi.
e il chiaro
la linea diventa:
// Imposta il colore da trasparente a rosso trasparente, glContext.clearColor (1.0, 0.0, 0.0, 0.5);
E ora, rivelano:
Guarda da vicino. Lassù nell'angolo in alto a sinistra ... non c'è assolutamente nulla! Certo che non puoi vedere il testo! È perché nel nostro CSS, abbiamo specificato # 000
come lo sfondo della tela. Lo sfondo agisce come un livello aggiuntivo sotto l'area di disegno, quindi il browser esegue il compositing alfa del buffer dei colori su di esso mentre nasconde completamente il testo. Per renderlo più chiaro, cambieremo lo sfondo in verde e vediamo cosa succede:
sfondo: # 0f0;
E il risultato:
Sembra ragionevole. Questo colore sembra essere rgb (128, 127, 0)
, che può essere considerato come il risultato della fusione di rosso e verde con alfa uguale a 0.5 (eccetto se si utilizza Microsoft Edge, in cui il colore deve essere rgb (255, 127, 0)
perché al momento non supporta l'alpha premoltiplicato). Non possiamo ancora vedere il testo, ma almeno sappiamo come il colore di sfondo influisce sul nostro disegno.
Il risultato invita alla curiosità, però. Perché il rosso è stato dimezzato 128
, mentre il verde era dimezzato 127
? Non dovrebbero entrambi essere entrambi 128
o 127
, in base all'arrotondamento in virgola mobile? L'unica differenza tra loro è che il colore rosso è stato impostato come il colore chiaro nel codice WebGL, mentre il colore verde è stato impostato nel CSS. Onestamente non so perché succede, ma ho una teoria. Probabilmente è a causa del funzione di fusione usato per unire i due colori.
Quando si disegna qualcosa di trasparente sopra qualcos'altro, si attiva la funzione di fusione. Definisce il colore finale del pixel (SU
) deve essere calcolato dal livello in alto (livello sorgente, SRC
) e il livello sottostante (livello di destinazione, DST
). Quando si disegna con WebGL, abbiamo molte funzioni di fusione tra cui scegliere. Ma quando il browser compone alfa la tela con gli altri livelli, abbiamo solo due modalità (per ora): premoltiplicato-alfa e non premoltiplicato-alfa (chiamiamolo normale modalità).
La normale modalità alfa è simile a:
OUTᴀ = SRCᴀ + DSTᴀ (1 - SRCᴀ) OUTʀɢʙ = SRCʀɢʙ.SRCᴀ + DSTʀɢʙ.DSTᴀ (1 - SRCᴀ)
Nella modalità alfa premoltiplicata, si presume che i valori RGB siano già moltiplicati con i corrispondenti valori alfa (da cui il nome pre-moltiplicato). In tal caso, le equazioni sono ridotte a:
OUTᴀ = SRCᴀ + DSTᴀ (1 - SRCᴀ) OUTʀɢʙ = SRCʀɢʙ + DSTʀɢʙ (1 - SRCᴀ)
Dal momento che non usiamo premoltiplied-alpha, ci basiamo sul primo gruppo di equazioni. Queste equazioni presuppongono che le componenti del colore siano valori in virgola mobile che vanno da 0
a 1
. Ma questo non è il modo in cui sono effettivamente memorizzati nella memoria. Invece, sono valori interi che vanno da 0
a 255
. Così srcAlpha
(0.5
) diventa 127
(o 128
, basato su come lo arrotondate), e 1 - srcAlpha
(1 - 0,5
) diventa 128
(o 127
). È per metà 255
(che è 127.5
) non è un numero intero, quindi finiamo con uno dei livelli che perdono a 0.5
e l'altro guadagnando a 0.5
nei loro valori alfa. Caso chiuso!
Nota: l'alpha-compositing non deve essere confuso con le modalità di fusione CSS. Prima viene eseguito il compositing alfa e quindi il colore calcolato viene unito al livello di destinazione utilizzando le modalità di fusione.
Torna al nostro testo nascosto. Proviamo a rendere lo sfondo trasparente-verde:
background: rgba (0, 255, 0, 0.5);
Finalmente:
Dovresti essere in grado di vedere il testo ora! È a causa di come questi strati sono dipinti l'uno sopra l'altro:
Doloroso, giusto? Fortunatamente, non dobbiamo occuparci di tutto questo se non vogliamo che la nostra tela sia trasparente!
var contextAttributes = depth: false, alpha: false; glContext = glCanvas.getContext ("webgl", contextAttributes) || glCanvas.getContext ("experimental-webgl", contextAttributes);
Ora il nostro buffer di colori non avrà un canale alfa per iniziare! Ma questo non ci impedirebbe di disegnare cose trasparenti?
La risposta è no. In precedenza, ho menzionato qualcosa su WebGL con funzioni di fusione flessibili indipendenti da come il browser unisce la tela con altri elementi di pagina. Se usiamo una funzione di fusione che si traduce in una miscelazione alfa premoltiplicata, allora non abbiamo assolutamente bisogno del canale alfa del buffer di disegno:
OUTᴀ = SRCᴀ + DSTᴀ (1 - SRCᴀ) OUTʀɢʙ = SRCʀɢʙ + DSTʀɢʙ (1 - SRCᴀ)
Se ci limitiamo a ignorare outAlpha
nel complesso, non perdiamo davvero nulla. Tuttavia, qualunque cosa disegniamo ha ancora bisogno di un canale alfa per essere trasparente. È solo il buffer di disegno che ne manca uno.
Premultiplied-alpha funziona bene con il filtro della trama e altre cose, ma non con la maggior parte degli strumenti di manipolazione delle immagini (non abbiamo ancora discusso le trame - supponiamo che siano immagini che dobbiamo disegnare). La modifica di un'immagine che è memorizzata in modalità alfa premuscolata non è conveniente perché accumula errori di arrotondamento. Ciò significa che vogliamo mantenere le nostre trame non premolteplicate finché continuiamo a lavorarci. Quando è il momento di testare o rilasciare, dobbiamo: