V poslední části úvodu do WebGL prozkoumáme dvě dosud neprobádané krajiny: texturování (tj. pokrývání trojrozměrných objektů obrazovými daty z externího souboru) a míchání barev (průhlednost). Průvodcem nám bude model proslulého Utahského čajníku; jako první krok tento načteme z externích JSON dat a zobrazíme. Ořízneme veškeré osvětlení a začneme tak s kódem, který obsahuje jen transformace a vykreslení objektu žlutou barvou: http://jsfiddle.net/ondras/3JStb/.
Textura
Texturu budeme načítat celkem přímočaře pomocí HTML obrázku. Nejprve se však pojďme podívat, jak je realizováno mapování textury na jednotlivé trojúhelníky modelu: pro každý vrchol potřebujeme dvojici souřadnic, které popisují jeden bod textury. Každému trojúhelníku tělesa tak odpovídá trojúhelník na obrázku s texturou. WebGL pak automaticky provede interpolaci hodnot textury na všechny pixely. Pro nás to znamená nutnost dodat tyto texturové souřadnice; naštěstí je máme k dispozici od autora modelu. Tyto souřadnice jsou vždy dvojice desetinných čísel mezi nulou a jedničkou (textura má tedy v těchto souřadnicích velikost 1×1). Předáme je do vertex shaderu:
attribute vec2 texture;
varying vec2 varyingTexture;
Z pohledu WebGL jde o běžné pole atributů:
var textureLoc = gl.getAttribLocation(program, "texture");
gl.enableVertexAttribArray(textureLoc);
var textureCoordsBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, textureCoordsBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(data.vertexTextureCoords), gl.STATIC_DRAW);
gl.vertexAttribPointer(textureLoc, 2, gl.FLOAT, false, 0, 0);
Do fragment shaderu pak texturu předáme jako speciální datový typ sampler2D
, který předáme do texturovací funkce texture2D
:
varying vec2 varyingTexture;
uniform sampler2D sampler;
void main(void) {
gl_FragColor = texture2D(sampler, varyingTexture);
}
Pojďme načíst obrázek s texturou. Z bezpečnostních důvodů musí být přenos realizován pomocí CORS, pokud je soubor umístěný na jiné doméně. V případě obrázku použijeme HTML5 atribut crossorigin
:
var image = document.createElement("img");
image.crossOrigin = "anonymous";
image.src = "http://bespin.cz/~ondras/webgl/metal.jpg";
Samosebou nemá smysl předávat obrázek do WebGL, dokud nebude plně načten. Jakmile se tak stane (událost load
), vytvoříme z obrázku texturu:
image.onload = function() {
texture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR);
gl.generateMipmap(gl.TEXTURE_2D);
}
Jaká WebGL volání jsme použili?
createTexture
pro vytvoření WebGL textury;bindTexture
pro označení této textury jako aktivní (stejně jako u bufferů);texImage2D
pro nahrání obrazových dat z obrázku do textury. Druhý parametr (nula) říká, že chceme plnou velikost. Další dvě konstanty určují zdrojový a cílový formát obrazových dat;gl.UNSIGNED_BYTE
říká, že obrazová data mají jeden bajt na barevný kanál.texParameteri
používáme k nastavení toho, jak se budou texturová data při interpolaci zvětšovat (TEXTURE_MAG_FILTER
) či zmenšovat (TEXTURE_MIN_FILTER
). Pro zvětšení volíme bilineární interpolaci, pro zmenšení techniku mipmappingu. V rámci pokusů je možné oba dva algoritmy nastavit nagl.NEAREST
– interpolaci nearest neighbor – a všimnout si, jak vznikají nehezké vizuální artefakty.generateMipmap
celkem přímočaře nakonec vygeneruje sadu mipmap.
Naposled musíme ještě texturu předat do fragment shaderu. To pro nás znamená jen spárování uniform hodnoty sampler
s texturovací jednotkou (těch je k dispozici celá řada, my použijeme jen tu první – gl.TEXTURE0
).
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, texture);
var samplerLoc = gl.getUniformLocation(program, "sampler");
gl.uniform1i(samplerLoc, 0); // nula odpovídá gl.TEXTURE0
Vidíme nyní čajník pokrytý texturou z obrázku. Zdrojový kód je k dispozici opět na http://jsfiddle.net/ondras/JY8Sp/:
Míchání barev a průhlednost
Problematiku částečně či plně průhledných barev jsme v tomto seriálu zatím zcela opomíjeli. Je to mimo jiné i proto, že se jedná o překvapivě složitou kapitolu; WebGL (resp. OpenGL) nám v tomto směru vychází vstříc jen lehce.
Při běžném kreslení nový pixel zcela přepíše (překryje) pixel původní, ať už se jedná o barvu pozadí, nebo již vykreslená data. Pokud by ale vykreslovaný pixel nesl nějakou informaci o průhlednosti, bylo by nutné smíchat jeho barvu s barvou, která je již vykreslena. Pojďme to zkusit a celý čajník vykreslit poloprůhledný.
Především to znamená, že v našem případě musíme zahodit testování hloubky (gl.enable(gl.BLEND)
): je třeba vykreslit všechny pixely; i ty, co popisují odvrácenou stranu čajníku. Pojďme ve fragment shaderu nadefinovat výslednou barvu s padesátiprocentní průhledností:
gl_FragColor.a = 0.5;
Stále ještě však nevidíme čajník průhledný, protože WebGL ve výchozím nastavení hodnotu alpha ignoruje a vždy vykreslí pixel plnou barvou.
Je tedy nutné zapnout podporu pro míchání (či mísení?) barev:
gl.enable(gl.BLEND);
A zároveň ještě definovat algoritmus (resp. výpočet), který se bude při míchání aplikovat. To se dělá pomocí dvou konstant; tou první se vynásobí barva kresleného pixelu (source), tou druhou barva již vykresleného (destination) a obě se sečtou. V našem případě použijeme toto nastavení:
gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
Definujeme tedy, že vstupní barvu pixelu vynásobíme hodnotou průhlednosti, zatímco existující jejím doplňkem do jedničky. To víceméně odpovídá běžné představě o poloprůhledné barvě (výsledná hodnota je „mix“ popředí a pozadí s váhou určenou průhledností).
Ještě je dobré zmínit, že tímto se informace o průhlednosti zpropaguje i do výsledného canvasu, což nemusí být nutně vhodné: tento je totiž kombinován s barvou pozadí stránky, takže na bílé stránce by byl čajník zabarven do běla. Vypneme proto zcela používání průhlednosti ve výsledném canvasu:
var gl = document.querySelector("canvas").getContext("experimental-webgl", {alpha:false});
Konečně je hotovo: čajník je poloprůhledný. Zdrojový kód ukázky je na adrese http://jsfiddle.net/ondras/3RwZb/.
Tímto končí krátký úvodní seriál o WebGL. Všechny ukázky z tohoto dílu jsou k dispozici pro stažení v archivu.
Přehled komentářů