Přejít k navigační liště

Zdroják » JavaScript » WebGL: Texturovat, nemíchat

WebGL: Texturovat, nemíchat

Články JavaScript

Když jsem se před časem poprvé ponořil do světa WebGL, začínal jsem na zelené louce. Kdybych chtěl mít rychle nějaký výstup, jistě bych sáhl po hotovém řešení, poskytujícím přímo graf scény (například vynikající three.js). Já chtěl ale vědět, jak a proč ty věci fungují; každou funkci si vyzkoušet a pochopit její účel. Své poznatky budu sepisovat, kdyby se náhodou někomu hodily…

Nálepky:

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/.

teapot-yellow

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 na gl.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/:

teapot-textured

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/.

teapot-alpha

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.

Komentáře

Odebírat
Upozornit na
guest
4 Komentářů
Nejstarší
Nejnovější Most Voted
Inline Feedbacks
Zobrazit všechny komentáře
Jan Prachař

S tím mícháním barev vidím zasadní (a nezmíněný) problém v tom, jak zajistit, aby spodní pixely byly vykreslené dřív než ty horní (lze vidět, když konvičce nastavím průhlednost 0.9).

Petr Bečka

Chtel bych podekovat za cas a usili vynalozene pri tvorbe tutorialu, je to strucne, vystizne a vecne! Jeste bych si dovolil otazku, mate nejake doporucene tutorialy, nadstavbu, kde znalosti s webGL prohloubit? Vim, ze materialu je v anglickem jazyce spousta, nicmene nepochybuji, ze budete mit lepsi prehled nez ja, ktery tutorial zvolit a ktery ne.

Přístupnost není jen o splnění norem: nový pohled na inkluzivní design

Přístupnost a inkluze možná nepatří mezi nejžhavější témata digitálního světa – dokud o nich nezačne mluvit Vitaly Friedman. Na WebExpo 2024 předvedl, že inkluzivní design není jen o splněných checkboxech, ale hlavně o lidech. S energií sobě vlastní obrátil zažité přístupy naruby a ukázal, že skutečně přístupný web je nejen možný, ale i nezbytný.

Efektivnější vývoj UI nebo API: Co si odnést z WebExpo 2025?

Různé
Komentáře: 0
Jak snadno implementovat moderní uživatelské rozhraní? Které funkce brzdí rychlost vašeho webu? A kdy raději sami přibrzdit, abychom využitím AI nepřekročili etické principy? Debatu aktuálních dev témat rozdmýchá sedmnáctý ročník technologické konference WebExpo, která proběhne v Praze od 28. do 30. května. Který talk či workshop si rozhodně nenechat ujít? Toto je náš redakční výběr z vývojářských hroznů.

Zapřáhněte AI jako nikdy předtím. Květnová konference WebExpo přivítá hvězdy technologického světa

Od 28. do 30. května 2025 promění pražský Palác Lucerna na tři dny technologická konference WebExpo. Na programu je více než 80 přednášek a workshopů od expertů z celého světa. WebExpo tradičně propojuje vývojáře, designéry, marketéry i byznysové lídry a nabízí praktické dovednosti, strategické myšlení a přináší nejnovější trendy nejen v oblasti AI.