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ší Nejvíce hlasů
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.

AI přepracovala 300 tisíc řádků Street Fighteru III. Hlídal ji hash každého snímku

‐ AI ‐
Komentáře: 0
AI agenti za tři týdny přepracovali dekompilovaný kód Street Fighteru III: 3rd Strike a podle metriky CodeScene dosáhli nejvyššího možného skóre kvality. Shoda s původní verzí se ověřovala přehráváním herních záznamů a porovnáním hashe herního stavu po každém snímku. Taková kontrola je přísnější než běžné testy, zachytí ale jen to, co do hashe vstupuje.

DressCode si vezme váš PHP kód do parády

‐ PHP ‐
Komentáře: 0
Napsat nové pravidlo do PHP CS Fixeru nebo PHP_CodeSnifferu je za trest. David Grudl to zkusil kvůli jedinému řádku `use function` a místo pravidla z toho vznikl nový nástroj DressCode, postavený nad stromem, který ze zdrojáku nic nezahodí. Ukážeme si, jak ho nasadit, nastavit a zapojit do práce AI agenta.

Proč Anthropic ve svém monorepozitáři opustil Git? AI agenti změnili pravidla hry

‐ Různé ‐
Komentáře: 0
Anthropic nahradil Git ve svém hlavním monorepozitáři vlastním systémem Scion. Důvodem je prudký nárůst změn vytvářených s pomocí AI agentů: zatímco v květnu 2026 firma začleňovala přibližně 2 500 změn denně, v září jich bylo přes 30 000. Nový systém přesouvá správu historie na server, omezuje stahování nepotřebných dat a zrychluje přípravu kódu pro testování. Nejde však o univerzální náhradu Gitu.