Využití textur ve WebGL a OpenGL ES

3D konvička wireframe
Autor: Hay Kranen, Wikimedia, podle licence: CC-BY
Na několika příkladech si ukážeme, jakým způsobem je možné na stěnu těles ve 3D scéně nanést textury. Popíšeme si jak nanesení procedurálních textur, tak i textur načítaných z externích souborů (fotografie atd.).

Podpora textur a texturování v OpenGL

Co se dozvíte v článku
  1. Podpora textur a texturování v OpenGL
  2. Omezení texturování v OpenGL
  3. První demonstrační příklad: zjištění maximální velikosti textury prakticky
  4. Postup při texturování
  5. Nastavení způsobu nanášení textury na vykreslované povrchy
  6. Další parametry ovlivňující způsob nanášení textur
  7. Vykreslení scény se zadanými texturovacími souřadnicemi pro každý vrchol
  8. Druhý demonstrační příklad: vykreslení trojúhelníku s nanesenou texturou
  9. Vykreslení čtvercové plochy s nanesenou texturou
  10. Třetí demonstrační příklad: vykreslení čtvercové plochy s nanesenou texturou
  11. Vygenerování procedurální textury
  12. Využití procedurální textury
  13. Čtvrtý demonstrační příklad: nanesení procedurální textury na plochu
  14. Modifikace uv-souřadnic v prostoru textury
  15. Pátý demonstrační příklad: zobrazení otexturovaného čtverce s modifikovanými uv-souřadnicemi
  16. Textury uložené v externích souborech (PNG, JPEG)
  17. Realizace načtení rastrového obrázku
  18. Šestý demonstrační příklad: čtverec s texturou načtenou z externího obrázku
  19. Repositář s demonstračními příklady
  20. Odkazy na Internetu

Jak v klasickém OpenGL, tak i v OpenGL ES jsou podporovány pouze rastrové textury, které mohou být buď jednodimenzionální, dvoudimenzionální nebo třídimenzionální. V případě jednodimenzionálních textur se prakticky jedná o pruh texelů, pomocí něhož se dají realizovat různě komplikované barvové přechody. Dvoudimenzionální textury (tedy vlastně bitmapy a pixmapy) jsou v současnosti zdaleka nejpoužívanější a jsou podporovány na naprosté většině grafických akcelerátorů, včetně historických kousků.

Třídimenzionální textury (tj. objemové či voxelové textury) se používají především ve specializovaných aplikacích (medicína apod.), v nichž je zapotřebí zobrazovat objemová data. Starší GPU tyto textury nepodporují. A pokud se vrátíme k technologiím WebGL a OpenGL ES: třídimenzionální textury jsou podporovány až v OpenGL ES 3.0, nikoli v 2.0 (i když existují volitelná rozšíření i pro tyto typy textur).

Největší podpora ze strany jak historických, tak i moderních grafických akcelerátorů je tedy u dvoudimenzionálních textur, které také splňují značnou část nároků programátora počítačové grafiky. Především tímto typem textur se budeme zabývat v dalším textu. V OpenGL (i OpenGL ES) lze také zvolit různé filtrace textur, režimy mapování textur na plošky, multitexturování a další grafické efekty, které si probereme jak v dnešním článku, tak i v článku navazujícím.

Musíme si však uvědomit, že pokud použijeme některý grafický efekt, který není grafickým akcelerátorem přímo podporován, dojde k výpočtům pomocí hlavního procesoru počítače, což značně zpomaluje celý systém. Proto je nejprve vhodné zjistit, které efekty jsou na grafickém akcelerátoru podporovány a případně dát uživateli naší aplikace na výběr mezi kvalitou a rychlostí zobrazení. Všechny operace probírané dnes jsou podporovány všemi dnes používanými GPU. Příště upozorním na některé pokročilejší operace, které už podporovány být nemusí (ovšem lze je zařadit například do fragment shaderu – to je však téma na samostatný článek).

Omezení texturování v OpenGL

V OpenGL (ES) jsme při použití rastrových (2D) textur omezeni poměrně minimálně. První omezení spočívá ve faktu, že texturování je podporováno pouze pro plnobarevné (true color) režimy. V indexových režimech (s barevnou paletou) není povoleno. Toto omezení však dnes již nemá prakticky žádný význam, protože paletové režimy se používají minimálně (otázkou je, zde ještě vůbec) a velká část moderních grafických akcelerátorů je ani nemá implementované. Další omezení spočívá v maximální povolené velikosti textury a rozlišeních textury.

Všechny textury by měly mít rozlišení, které je mocninou čísla 2, například 256×256 texelů. Lze se ale setkat i s texturami, které tento požadavek nesplňují. Takové textury jsou použitelné jen v případě, že GPU podporuje rozšíření OES_texture_npot (bez tohoto rozšíření nebude možné realizovat mipmapy apod.). Naprosto všechny implementace OpenGL ES by měly podporovat textury alespoň do velikosti 64×64 pixelů (což je pochopitelně dosti omezující), ovšem většinou jsou podporovány i mnohem větší textury). Maximální podporovanou velikost textur lze zjistit pomocí příkazů OpenGL.

První demonstrační příklad: zjištění maximální velikosti textury prakticky

Maximální rozlišení textury je možné zjistit přímo z prostředí webového prohlížeče, což je ukázáno v dnešním prvním demonstračním příkladu. Skript nejprve zjistí velikost textury a následně ji zapíše zpět do webové stránky:

function print(message) {
    console.log(message);
}
 
// reference na objekt představující kreslicí plátno umístěné na webové stránce
const canvas = document.getElementById("web_gl_canvas");
if (!canvas) {
    throw new Error("Canvas with id 'web_gl_canvas' not found");
}
print("Canvas object retrieved");
 
// vytvoření a získání kontextu WebGL (3D grafika)
const gl = canvas.getContext("webgl");
if (!gl) {
    throw new Error("WebGL is not supported or context creation failed");
}
print("WebGL context created");
 
// přečtení maximálního rozlišení textury
const maxTextureSize = gl.getParameter(gl.MAX_TEXTURE_SIZE);
 
// zápis zpět na webovou stránku
document.getElementById("tsize").innerHTML = maxTextureSize + "×" + maxTextureSize + " texels";

Do webové stránky, která tento skript spouští, je nutné pouze doplnit element s ID tsize:

<!doctype html>
<html>
    <head>
        <title>WebGL: maximum supported texture size</title>
    </head>
    <body>
        <p>Maximum texture size: <span id="tsize" style="color:red"></span></p>
 
        <!-- kreslicí plátno -->
        <canvas id="web_gl_canvas" width="256" height="256"></canvas>
 
        <!-- skript, který kreslicí plátno využije -->
        <script type="application/javascript" src="texsize.js"></script>
    </body>
</html>

Maximální velikost textury lze ověřit na adrese https://tisnik.github.io/tem­p/texsize.html.

V mém případě se vypíše:

Maximum texture size: 8192×8192 texels

Postup při texturování

Vykreslování 3D objektů, na jejichž povrch je nanesena 2D textura, je poměrně složité na počet a vzájemnou provázanost kroků, které je potřeba v programu provést:

  1. Vytvoření rastrové předlohy textury (procedurální textury) nebo její načtení ze souboru.
  2. Vytvoření nového texturovacího objektu, přiřazení textury tomuto objektu a nastavení formátu textury.
  3. Nastavení způsobu nanášení textury na vykreslované povrchy.
  4. Zapnutí (povolení) nanášení textur.
  5. Vykreslení scény se zadanými texturovacími souřadnicemi (uv-souřadnice) pro každý vrchol trojúhelníka.

Jednotlivé kroky si podrobněji popíšeme v následujících odstavcích.

Vytvoření popř. načtení rastrové předlohy textury

Tento krok musí implementovat sám autor programu, protože grafická knihovna OpenGL ES nenabízí prakticky téměř žádnou podporu ani pro vytvoření (výpočet) rastrových textur ani pro jejich načtení ze souboru. V závěru článku si ukážeme načtení textury z externího obrázku s využitím dalších funkcí nabízených webovým prohlížečem. Další možností je provedení výpočtu procedurální textury přímo v aplikaci. Tento způsob sice poněkud zdrží start aplikace, ale na druhou stranu se textury nemusí ukládat na disk, takže se může zjednodušit distribuce programu či omezit množství požadavků provedených webovým prohlížečem. Na internetu lze nalézt velké množství algoritmů pro vytváření procedurálních textur (my dnes použijeme jednoduchý výpočet šachovnice). Texturu lze také vytvořit přečtením vykresleného obrázku (celé scény či její části) přímo z framebufferu, což je umožněno příkazem gl.CopyTexImage2D().

// textura (prozatím primitivní)
const texture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);
 
// primitivní textura 2x2 texely
const texels = new Uint8Array([
      255, 0,   0,   255,  // red
      0,   255, 0,   255,  // green
      0,   0,   255, 255,  // blue
      255, 255, 0,   255,  // yellow
]);

Složitější problém nastane při použití mipmappingu, kdy je zapotřebí specifikovat textury v několika rozlišeních. Tento problém lze opět řešit buď programově, nebo lze výpočet ponechat specializovaným funkcím, což je však téma pro navazující článek.

Vytvoření nového texturovacího objektu, přiřazení textury tomuto objektu, nastavení formátu textury

Popišme si ten nejjednodušší případ, ve kterém pro všechna vykreslovaná tělesa používáme pouze jednu texturu. V tomto případě musíme pouze nastavit základní parametry textury. Pokud by bylo nutné používat více textur (což je v reálných scénách pochopitelně více než pravděpodobné) musela by se aktivní textura vybírat podle čísla svého texturovacího objektu (ten se musí pro každou texturu nejprve vytvořit a teprve poté nastavovat formát textury). OpenGL ES podporuje celou řadu formátů textur, které se podle dimenze textury dají specifikovat příkazy:

gl.TexImage1D(
    GLenum  target,
    GLint   level,
    GLint   components,
    GLsizei width,
    GLint   border,
    GLenum  format,
    GLenum  type,
    const GLvoid *pixels
);
 
gl.TexImage2D(
    GLenum  target,
    GLint   level,
    GLint   components,
    GLsizei width,
    GLsizei height,
    GLint   border,
    GLenum  format,
    GLenum  type,
    const GLvoid *pixels
);

Význam jednotlivých parametrů je následující:

  • Parametrem target určujeme dimenzi textury. Pro jednodimenzionální texturu bude v tomto parametru předána hodnota GL_TEXTURE_1D, pro dvoudimenzionální GL_TEXTURE_2D atd. Pokud současně nastavíme parametry 1D a 2D textury, mají přednost parametry pro 2D texturu.
  • Parametr level má svůj význam zejména při používání mipmappingu, tj. textur s více úrovněmi detailu (viz další pokračování tohoto seriálu). Pro běžnou texturu s jedním rozlišením tento parametr nastavujeme na nulovou hodnotu.
  • Pomocí parametru components se specifikuje počet barevných složek použitých pro reprezentaci jednoho pixelu. Hodnota tohoto parametru může být 1, 2, 3 nebo 4. Hodnota 1 značí, že se používá pouze barevná složka R (red). Hodnotou dvě specifikujeme barevnou složku R a alfa složku A. Hodnota 3 představuje klasickou barevnou reprezentaci pixelu pomocí barevných složek R, G, B. Hodnotou 4 specifikujeme použití tří barevných složek R, G, B společně s alfa složkou A.
  • Velikost (resp. rozlišení) textury se zadává v parametrech width a height. Pro 1D texturu se parametr height nezadává, protože je vždy jednotkový. Oba parametry musí mít hodnotu rovnu 2n+2(border), kde n je celé kladné číslo z rozsahu daného konkrétní implementací OpenGL ES a border je šířka okraje textury. Minimální rozlišení textury, kterou musí podporovat všechny implementace, je 64×64 pixelů bez okraje popř. 66×66 pixelů s okrajem. Pokud je jeden z parametrů width nebo height nastaven na nulu, je texturování zakázáno.
  • Parametrem border se zadává šířka okraje textury v pixelech. Okraj může být použit například pro hladké navazování textur. Jeho použití si ukážeme v některém z následujících dílů. Pro běžně mapované textury se okraj nastavuje na nulovou hodnotu.
  • V parametru format se předává formát dat použitý pro jednotlivé pixely textury. Tento parametr má stejný význam jako u již dříve probraných funkcí glDrawPixels() a glReadPixels(). Každá implementace by měla podporovat minimálně tyto formáty: GL_COLOR_INDEX, GL_RED, GL_GREEN, GL_BLUE, GL_ALPHA, GL_RGB, GL_RGBA, GL_LUMINANCE a GL_LUMINANCE_ALPHA, ale můžeme se setkat i s grafickými akcelerátory, které poslední dva formáty špatně interpretují (nenastaví například korektně všechny barevné složky).
  • Parametr type, stejně jako parametr předchozí, má stejný význam jako u funkcí glDrawPixels() a glReadPixels(), tj. udává typ dat každé barevné složky pixelu. Mezi běžně podporované typy patří GL_BITMAP, GL_BYTE, GL_UNSIGNED_BYTE, GL_SHORT, GL_UNSIGNED_SHORT, GL_INT, GL_UNSIGNED_INT a GL_FLOAT. Jak je vidět, máme díky parametrům format a type značnou volnost při tvorbě a/nebo načítání textur ze souboru.
  • V posledním parametru pixels je předáván ukazatel na rastrová data textury.

V příkladu použijeme:

gl.texImage2D(gl.TEXTURE_2D,      // typ textury: zde 2D textura
              0,                  // úroveň detailu
              gl.RGBA,            // počet barvových komponent (zde 4)
              2,                  // horizontální rozlišení textury
              2,                  // vertikální rozlišení textury
              0,                  // šířka okraje textury
              gl.RGBA,            // formát uložení texelů
              gl.UNSIGNED_BYTE,   // typ každé barvové složky texelu
              texels              // pole texelů
);

Nastavení způsobu nanášení textury na vykreslované povrchy

Texturu lze na objekt nanášet více způsoby. Nejjednodušší a nejpoužívanější způsob spočívá v nanášení barev jednotlivých texelů přímo na zobrazovanou plochu – přímo se tím mění barvy pixelů zobrazované plochy. Druhý způsob moduluje barvu texelu barvou fragmentu, čehož lze použít například při kombinaci světelných efektů s texturou. Třetím způsobem je využití blendingu, tj. kombinace původní barvy pixelu s barvou texelu.

Texturování musíme nejprve zapnout, jinak se nebude provádět (v tomto případě by se při vykreslování plošek použily buď barvy jednotlivých vertexů, nebo by se spočítala globální barva). Současně se zapnutím texturování specifikujeme, kolik dimenzí budou mít vykreslované textury.

Texturování lze zapnout příkazem gl.Enable(), kterému předáme jako jediný parametr konstantu určující dimenzi textury, tedy GL_TEXTURE_1D, GL_TEXTURE_2D nebo GL_TEXTURE_3D. Opětovné vypnutí texturování lze provést příkazem glDisable() se stejnými parametry jako u předchozího příkazu.

Další parametry ovlivňující způsob nanášení textur

V demonstračních příkladech nastavujeme ještě čtyři další parametry, které ovlivňují způsob vykreslení jednotlivých fragmentů. Především lze specifikovat filtr (resp. algoritmus) použitý při přiblížení či oddálení od textury. Jedná se o stav, kdy je texel větší než pixel na obrazovce či naopak, kdy je texel menší. A taktéž lze určit, jak se změní způsob nanesení textury v případě, že uv-souřadnice nebudou spadat do intervalu 0 až 1. Prozatím ponecháme následující nastavení, ovšem v navazujícím článku si ukážeme vliv dalších voleb:

// způsob vykreslení textury
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);

Vykreslení scény se zadanými texturovacími souřadnicemi pro každý vrchol

Vykreslení scény s texturovanými ploškami probíhá podobně jako u barevných plošek (trojúhelníků). Největší změnou je, že pro každý vrchol musíme k jeho 2D/3D/4D souřadnicím navíc specifikovat souřadnice v textuře. Každá textura má nezávisle na své velikosti souřadnice v rozsahu od 0.0 do 1.0, tj. 2D textura je chápána jako čtverec o straně délky 1. Souřadnice do textury však můžeme zadávat libovolně, protože díky opakování (pokud je zapnuto) se obrázek textury může na zobrazované ploše šachovnicově skládat, podobně jako například obrázky na pozadí HTML stránek:

// souřadnice vrcholů trojúhelníku + souřadnice v prostoru textury
const vertexes = [
  // x,     y,      u,   v
   0.0,   height,  0.5, 0.0,
   min_x, min_y,   0.0, 1.0,
   max_x, min_y,   1.0, 1.0,
];

Vertexy a uv-souřadnice se předávají zvlášť (i když jsou uloženy v jediném poli). Povšimněte si roli parametru offset:

// seznam předávaný do vertex shaderu
gl.vertexAttribPointer(p,
                       2,                                // počet prvků v každém atributu
                       gl.FLOAT,                         // typ prvků
                       gl.FALSE,                         // data nejsou normalizována
                       4*Float32Array.BYTES_PER_ELEMENT, // velikost vertexu+texturovacích souřadnic v bajtech
                       0                                 // offset
);
gl.enableVertexAttribArray(p);

a:

// seznam předávaný do vertex shaderu
gl.vertexAttribPointer(texLoc,
                       2,                                // počet prvků v každém atributu
                       gl.FLOAT,                         // typ prvků
                       gl.FALSE,                         // data nejsou normalizována
                       4*Float32Array.BYTES_PER_ELEMENT, // velikost vertexu+texturovacích souřadnic v bajtech
                       2*Float32Array.BYTES_PER_ELEMENT  // offset
);
gl.enableVertexAttribArray(texLoc);

Navíc je ovšem nutné pozměnit i shadery. Ty nyní nebudou pracovat s předávanou barvou, ale s uv-souřadnicemi v prostoru textury.

Vertex shader pouze předá uv-souřadnice v textuře pro další zpracování:

attribute vec2 position;
attribute vec2 texcoord;
varying vec2 vTexcoord;
 
void main() {
    gl_Position = vec4(position, 0, 1);
    vTexcoord = texcoord;
}

Fragment shader namísto nastavení konstantní barvy fragmentu tuto barvu získá z textury:

precision mediump float;
varying vec2 vTexcoord;
uniform sampler2D texture;
 
void main() {
    gl_FragColor = texture2D(texture, vTexcoord);
}
Poznámka: souřadnice v textuře se označují písmeny u a v, nikoli x a y. Často se proto setkáme s termínem uv-space.

Druhý demonstrační příklad: vykreslení trojúhelníku s nanesenou texturou

Úplný zdrojový kód dnešního druhého demonstračního příkladu, který je možné spustit z této adresy, vypadá následovně:

const vertexShaderSource = `
    attribute vec2 position;
    attribute vec2 texcoord;
    varying vec2 vTexcoord;
 
    void main() {
        gl_Position = vec4(position, 0, 1);
        vTexcoord = texcoord;
    }
`
 
const fragmentShaderSource = `
    precision mediump float;
    varying vec2 vTexcoord;
    uniform sampler2D texture;
 
    void main() {
        gl_FragColor = texture2D(texture, vTexcoord);
    }
`
 
function print(message) {
    console.log(message);
}
 
// reference na objekt představující kreslicí plátno umístěné na webové stránce
const canvas = document.getElementById("web_gl_canvas");
if (!canvas) {
    throw new Error("Canvas with id 'web_gl_canvas' not found");
}
print("Canvas object retrieved");
 
// vytvoření a získání kontextu WebGL (3D grafika)
const gl = canvas.getContext("webgl");
if (!gl) {
    throw new Error("WebGL is not supported or context creation failed");
}
print("WebGL context created");
 
// vytvoření vertex shaderu
const vertexShader = gl.createShader(gl.VERTEX_SHADER);
if (!vertexShader) {
    throw new Error("Failed to create vertex shader.");
}
print("Vertex shader created");
 
// překlad vertex shaderu
gl.shaderSource(vertexShader, vertexShaderSource);
gl.compileShader(vertexShader);
if (!gl.getShaderParameter(vertexShader, gl.COMPILE_STATUS)) {
    const log = gl.getShaderInfoLog(vertexShader);
    gl.deleteShader(vertexShader);
    throw new Error(`Vertex shader compile failed: ${log}`);
}
print("Vertex shader compiled");
 
// vytvoření fragment shaderu
const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
if (!fragmentShader) {
    throw new Error("Failed to create fragment shader.");
}
print("Fragment shader created");
 
// překlad fragment shaderu
gl.shaderSource(fragmentShader, fragmentShaderSource);
gl.compileShader(fragmentShader);
if (!gl.getShaderParameter(fragmentShader, gl.COMPILE_STATUS)) {
    const log = gl.getShaderInfoLog(fragmentShader);
    gl.deleteShader(fragmentShader);
    throw new Error(`fragment shader compile failed: ${log}`);
}
print("Fragment shader compiled");
 
// registrace obou shaderů
const program = gl.createProgram();
if (!program) {
    throw new Error("Failed to create program.");
}
print("Program created");
 
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
print("Shaders attached to the program");
 
gl.linkProgram(program);
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
    const log = gl.getProgramInfoLog(program);
    gl.deleteProgram(program);
    throw new Error(`Program link failed: ${log}`);
}
print("Program linked");
 
const min_x = -0.9
const max_x = 0.9
const min_y = min_x
const max_y = max_x
const height = Math.sqrt(3) / 2
 
// souřadnice vrcholů trojúhelníku + souřadnice v prostoru textury
const vertexes = [
  // x,     y,      u,   v
   0.0,   height,  0.5, 0.0,
   min_x, min_y,   0.0, 1.0,
   max_x, min_y,   1.0, 1.0,
];
 
// buffer, do kterého se překopírují souřadnice bodu
const vertexBuffer = gl.createBuffer();
if (!vertexBuffer) {
    throw new Error("Failed to create buffer.");
}
print("Vertex buffer created");
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
 
const data = new Float32Array(vertexes);
gl.bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW);
print("Vertex buffer filled-in by vertexes");
 
// navázání na parametr předávaný do vertex shaderu
const p = gl.getAttribLocation(program, "position");
if (p === -1) {
    throw new Error("Attribute 'position' not found in shader program");
}
print("Attribute 'position' found in shader program")
 
// navázání na parametr předávaný do vertex shaderu
const texLoc = gl.getAttribLocation(program, "texcoord");
if (p === -1) {
    throw new Error("Attribute 'texcoord' not found in shader program");
}
print("Attribute 'texcoord' found in shader program")
 
// seznam předávaný do vertex shaderu
gl.vertexAttribPointer(p,
                       2,                                // počet prvků v každém atributu
                       gl.FLOAT,                         // typ prvků
                       gl.FALSE,                         // data nejsou normalizována
                       4*Float32Array.BYTES_PER_ELEMENT, // velikost vertexu+texturovacích souřadnic v bajtech
                       0                                 // offset
);
gl.enableVertexAttribArray(p);
 
// seznam předávaný do vertex shaderu
gl.vertexAttribPointer(texLoc,
                       2,                                // počet prvků v každém atributu
                       gl.FLOAT,                         // typ prvků
                       gl.FALSE,                         // data nejsou normalizována
                       4*Float32Array.BYTES_PER_ELEMENT, // velikost vertexu+texturovacích souřadnic v bajtech
                       2*Float32Array.BYTES_PER_ELEMENT  // offset
);
gl.enableVertexAttribArray(texLoc);
 
// textura (prozatím primitivní)
const texture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);
 
// primitivní textura 2x2 texely
const texels = new Uint8Array([
      255, 0,   0,   255,  // red
      0,   255, 0,   255,  // green
      0,   0,   255, 255,  // blue
      255, 255, 0,   255,  // yellow
]);
 
gl.texImage2D(gl.TEXTURE_2D,      // typ textury: zde 2D textura
              0,                  // úroveň detailu
              gl.RGBA,            // počet barvových komponent (zde 4)
              2,                  // horizontální rozlišení textury
              2,                  // vertikální rozlišení textury
              0,                  // šířka okraje textury
              gl.RGBA,            // formát uložení texelů
              gl.UNSIGNED_BYTE,   // typ každé barvové složky texelu
              texels              // pole texelů
);
 
// způsob vykreslení textury
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
 
// navázání na atribut ve shaderech
const samplerLoc = gl.getUniformLocation(program, "texture");
gl.uniform1i(samplerLoc, 0);
 
// transformace souřadnic: z normalizovaných souřadnic na souřadnice na plátnu
gl.viewport(0, 0, canvas.width, canvas.height);
 
// nastavení barvy pro vymazání barvového bufferu
gl.clearColor(0, 0, 0, 1);
 
// vymazání barvového bufferu
gl.clear(gl.COLOR_BUFFER_BIT);
 
// spuštění programu
gl.useProgram(program);
 
// vykreslení trojúhelníku
gl.drawArrays(gl.TRIANGLES, 0, 3);

Vykreslení čtvercové plochy s nanesenou texturou

Pro úplnost se ještě pokusme vykreslit čtvercovou plochu s nanesenou texturou. Čtvercové plochy jsou z pohledu texturování ideální, protože i samotná 2D textura je reprezentována čtvercovým rastrovým obrázkem. Čtverec vznikne složením dvou trojúhelníků, takže si jen musíme dát pozor na pořadí souřadnic těchto trojúhelníků a na odpovídající souřadnice v prostoru textury (uv-souřadnice). Jedno z možných uspořádání dvojice trojúhelníků může vypadat následovně:

// souřadnice vrcholů trojúhelníku + souřadnice v prostoru textury
const vertexes = [
  // x, y,   u, v
   min_x, min_y, 0.0, 0.0,
   min_x, max_y, 0.0, 1.0,
   max_x, max_y, 1.0, 1.0,
 
   min_x, min_y, 0.0, 0.0,
   max_x, max_y, 1.0, 1.0,
   max_x, min_y, 1.0, 0.0,
];

Textura bude pořád obsahovat pouze 2×2=4 texely:

// primitivní textura 2x2 texely
const texels = new Uint8Array([
      255, 0,   0,   255,  // red
      0,   255, 0,   255,  // green
      0,   0,   255, 255,  // blue
      255, 255, 0,   255,  // yellow
]);

Poslední úprava spočívá v tom, že do grafické pipeline musíme přenést šestici vrcholů (tvořících dva samostatné trojúhelníky):

// vykreslení dvojice trojúhelníků
gl.drawArrays(gl.TRIANGLES, 0, 6);
Poznámka: úpravu příkladu pro zobrazení čtverce s využitím pásu trojúhelníků ponechávám na čtenáři.

Třetí demonstrační příklad: vykreslení čtvercové plochy s nanesenou texturou

Dnešní třetí demonstrační příklad vykreslí čtverec složený ze dvou trojúhelníků. Jeho chování si lze ověřit na adrese https://tisnik.github.io/tem­p/textures2.html:

const vertexShaderSource = `
    attribute vec2 position;
    attribute vec2 texcoord;
    varying vec2 vTexcoord;
 
    void main() {
        gl_Position = vec4(position, 0, 1);
        vTexcoord = texcoord;
    }
`
 
const fragmentShaderSource = `
    precision mediump float;
    varying vec2 vTexcoord;
    uniform sampler2D texture;
 
    void main() {
        gl_FragColor = texture2D(texture, vTexcoord);
    }
`
 
function print(message) {
    console.log(message);
}
 
// reference na objekt představující kreslicí plátno umístěné na webové stránce
const canvas = document.getElementById("web_gl_canvas");
if (!canvas) {
    throw new Error("Canvas with id 'web_gl_canvas' not found");
}
print("Canvas object retrieved");
 
// vytvoření a získání kontextu WebGL (3D grafika)
const gl = canvas.getContext("webgl");
if (!gl) {
    throw new Error("WebGL is not supported or context creation failed");
}
print("WebGL context created");
 
// vytvoření vertex shaderu
const vertexShader = gl.createShader(gl.VERTEX_SHADER);
if (!vertexShader) {
    throw new Error("Failed to create vertex shader.");
}
print("Vertex shader created");
 
// překlad vertex shaderu
gl.shaderSource(vertexShader, vertexShaderSource);
gl.compileShader(vertexShader);
if (!gl.getShaderParameter(vertexShader, gl.COMPILE_STATUS)) {
    const log = gl.getShaderInfoLog(vertexShader);
    gl.deleteShader(vertexShader);
    throw new Error(`Vertex shader compile failed: ${log}`);
}
print("Vertex shader compiled");
 
// vytvoření fragment shaderu
const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
if (!fragmentShader) {
    throw new Error("Failed to create fragment shader.");
}
print("Fragment shader created");
 
// překlad fragment shaderu
gl.shaderSource(fragmentShader, fragmentShaderSource);
gl.compileShader(fragmentShader);
if (!gl.getShaderParameter(fragmentShader, gl.COMPILE_STATUS)) {
    const log = gl.getShaderInfoLog(fragmentShader);
    gl.deleteShader(fragmentShader);
    throw new Error(`fragment shader compile failed: ${log}`);
}
print("Fragment shader compiled");
 
// registrace obou shaderů
const program = gl.createProgram();
if (!program) {
    throw new Error("Failed to create program.");
}
print("Program created");
 
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
print("Shaders attached to the program");
 
gl.linkProgram(program);
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
    const log = gl.getProgramInfoLog(program);
    gl.deleteProgram(program);
    throw new Error(`Program link failed: ${log}`);
}
print("Program linked");
 
const min_x = -0.9
const max_x = 0.9
const min_y = min_x
const max_y = max_x
 
// souřadnice vrcholů trojúhelníku + souřadnice v prostoru textury
const vertexes = [
  // x, y,   u, v
   min_x, min_y, 0.0, 0.0,
   min_x, max_y, 0.0, 1.0,
   max_x, max_y, 1.0, 1.0,
 
   min_x, min_y, 0.0, 0.0,
   max_x, max_y, 1.0, 1.0,
   max_x, min_y, 1.0, 0.0,
];
 
// buffer, do kterého se překopírují souřadnice bodu
const vertexBuffer = gl.createBuffer();
if (!vertexBuffer) {
    throw new Error("Failed to create buffer.");
}
print("Vertex buffer created");
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
 
const data = new Float32Array(vertexes);
gl.bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW);
print("Vertex buffer filled-in by vertexes");
 
// navázání na parametr předávaný do vertex shaderu
const p = gl.getAttribLocation(program, "position");
if (p === -1) {
    throw new Error("Attribute 'position' not found in shader program");
}
print("Attribute 'position' found in shader program")
 
// navázání na parametr předávaný do vertex shaderu
const texLoc = gl.getAttribLocation(program, "texcoord");
if (p === -1) {
    throw new Error("Attribute 'texcoord' not found in shader program");
}
print("Attribute 'texcoord' found in shader program")
 
// seznam předávaný do vertex shaderu
gl.vertexAttribPointer(p,
                       2,                                // počet prvků v každém atributu
                       gl.FLOAT,                         // typ prvků
                       gl.FALSE,                         // data nejsou normalizována
                       4*Float32Array.BYTES_PER_ELEMENT, // velikost vertexu+texturovacích souřadnic v bajtech
                       0                                 // offset
);
gl.enableVertexAttribArray(p);
 
// seznam předávaný do vertex shaderu
gl.vertexAttribPointer(texLoc,
                       2,                                // počet prvků v každém atributu
                       gl.FLOAT,                         // typ prvků
                       gl.FALSE,                         // data nejsou normalizována
                       4*Float32Array.BYTES_PER_ELEMENT, // velikost vertexu+texturovacích souřadnic v bajtech
                       2*Float32Array.BYTES_PER_ELEMENT  // offset
);
gl.enableVertexAttribArray(texLoc);
 
// textura (prozatím primitivní)
const texture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);
 
// primitivní textura 2x2 texely
const texels = new Uint8Array([
      255, 0,   0,   255,  // red
      0,   255, 0,   255,  // green
      0,   0,   255, 255,  // blue
      255, 255, 0,   255,  // yellow
]);
 
gl.texImage2D(gl.TEXTURE_2D,      // typ textury: zde 2D textura
              0,                  // úroveň detailu
              gl.RGBA,            // počet barvových komponent (zde 4)
              2,                  // horizontální rozlišení textury
              2,                  // vertikální rozlišení textury
              0,                  // šířka okraje textury
              gl.RGBA,            // formát uložení texelů
              gl.UNSIGNED_BYTE,   // typ každé barvové složky texelu
              texels              // pole texelů
);
 
// způsob vykreslení textury
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
 
// navázání na atribut ve shaderech
const samplerLoc = gl.getUniformLocation(program, "texture");
gl.uniform1i(samplerLoc, 0);
 
// transformace souřadnic: z normalizovaných souřadnic na souřadnice na plátnu
gl.viewport(0, 0, canvas.width, canvas.height);
 
// nastavení barvy pro vymazání barvového bufferu
gl.clearColor(0, 0, 0, 1);
 
// vymazání barvového bufferu
gl.clear(gl.COLOR_BUFFER_BIT);
 
// spuštění programu
gl.useProgram(program);
 
// vykreslení dvojice trojúhelníků
gl.drawArrays(gl.TRIANGLES, 0, 6);

Vygenerování procedurální textury

Textura, kterou jsme až doposud používali, měla rozlišení pouze 2×2 texely. Reálné textury mají pochopitelně mnohem větší rozlišení. V dalším demonstračním příkladu z tohoto důvodu vytvoříme procedurální texturu, která bude obsahovat šachovnici zkombinovanou s barvovým přechodem (gradientem). Texely budou mít formát RGBA (tedy včetně alfa kanálu), i když prozatím nastavíme hodnotu alfa na konstantu 255 pro všechny texely. Funkce, která vytvoří jednorozměrné (!) pole se všemi texely, bude vypadat následovně:

function makeTexture(width, height) {
    const array = new Array(width*height*4);
 
    let index = 0;
    for (j=0; j<height; j++) {
        for (i=0; i<width; i++) {
            const c = ((i&0x20)==0^(j&0x20)==0) * 255;
            array[index++] = c;
            array[index++] = i;
            array[index++] = j;
            array[index++] = 255;            // alfa složka
        }
    }
    return array;
}

Využití procedurální textury

Výše uvedenou funkci využijeme pro konstrukci pole s barvami texelů:

// textura
textureArray = makeTexture(textureWidth, textureHeight);

Toto obecně heterogenní pole převedeme na pole hodnot typu byte (bez znaménka):

const texels = new Uint8Array(textureArray);

Výsledné pole typu Uint8Array lze přímo použít pro vytvoření textury, což se provede následovně:

gl.texImage2D(gl.TEXTURE_2D,      // typ textury: zde 2D textura
              0,                  // úroveň detailu
              gl.RGBA,            // počet barvových komponent (zde 4)
              textureWidth,       // horizontální rozlišení textury
              textureHeight,      // vertikální rozlišení textury
              0,                  // šířka okraje textury
              gl.RGBA,            // formát uložení texelů
              gl.UNSIGNED_BYTE,   // typ každé barvové složky texelu
              texels              // pole texelů
);
Poznámka: nehledě na rozlišení textury budou souřadnice texelů stále ležet v uv-prostoru, přičemž souřadnice u a v budou v rozsahu 0,0 až 1,0. To tedy znamená, že pole vrcholů a uv-souřadnic našich dvou trojúhelníků je zcela nezávislé na rozlišení textury:
// souřadnice vrcholů trojúhelníku + souřadnice v prostoru textury
const vertexes = [
  // x, y,   u, v
   min_x, min_y, 0.0, 0.0,
   min_x, max_y, 0.0, 1.0,
   max_x, max_y, 1.0, 1.0,
 
   min_x, min_y, 0.0, 0.0,
   max_x, max_y, 1.0, 1.0,
   max_x, min_y, 1.0, 0.0,
];

Čtvrtý demonstrační příklad: nanesení procedurální textury na plochu

Procedurální textura s obarvenou šachovnicí je použita v dnešním čtvrtém demonstračním příkladu, který si můžete odzkoušet na stránce https://tisnik.github.io/tem­p/textures3.html. Následuje výpis úplného zdrojového kódu tohoto příkladu:

const vertexShaderSource = `
    attribute vec2 position;
    attribute vec2 texcoord;
    varying vec2 vTexcoord;
 
    void main() {
        gl_Position = vec4(position, 0, 1);
        vTexcoord = texcoord;
    }
`
 
const fragmentShaderSource = `
    precision mediump float;
    varying vec2 vTexcoord;
    uniform sampler2D texture;
 
    void main() {
        gl_FragColor = texture2D(texture, vTexcoord);
    }
`
 
function print(message) {
    console.log(message);
}
 
function makeTexture(width, height) {
    const array = new Array(width*height*4);
 
    let index = 0;
    for (j=0; j<height; j++) {
        for (i=0; i<width; i++) {
            const c = ((i&0x20)==0^(j&0x20)==0) * 255;
            array[index++] = c;
            array[index++] = i;
            array[index++] = j;
            array[index++] = 255;            // alfa slozka
        }
    }
    return array;
}
 
// reference na objekt představující kreslicí plátno umístěné na webové stránce
const canvas = document.getElementById("web_gl_canvas");
if (!canvas) {
    throw new Error("Canvas with id 'web_gl_canvas' not found");
}
print("Canvas object retrieved");
 
// vytvoření a získání kontextu WebGL (3D grafika)
const gl = canvas.getContext("webgl");
if (!gl) {
    throw new Error("WebGL is not supported or context creation failed");
}
print("WebGL context created");
 
// vytvoření vertex shaderu
const vertexShader = gl.createShader(gl.VERTEX_SHADER);
if (!vertexShader) {
    throw new Error("Failed to create vertex shader.");
}
print("Vertex shader created");
 
// překlad vertex shaderu
gl.shaderSource(vertexShader, vertexShaderSource);
gl.compileShader(vertexShader);
if (!gl.getShaderParameter(vertexShader, gl.COMPILE_STATUS)) {
    const log = gl.getShaderInfoLog(vertexShader);
    gl.deleteShader(vertexShader);
    throw new Error(`Vertex shader compile failed: ${log}`);
}
print("Vertex shader compiled");
 
// vytvoření fragment shaderu
const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
if (!fragmentShader) {
    throw new Error("Failed to create fragment shader.");
}
print("Fragment shader created");
 
// překlad fragment shaderu
gl.shaderSource(fragmentShader, fragmentShaderSource);
gl.compileShader(fragmentShader);
if (!gl.getShaderParameter(fragmentShader, gl.COMPILE_STATUS)) {
    const log = gl.getShaderInfoLog(fragmentShader);
    gl.deleteShader(fragmentShader);
    throw new Error(`fragment shader compile failed: ${log}`);
}
print("Fragment shader compiled");
 
// registrace obou shaderů
const program = gl.createProgram();
if (!program) {
    throw new Error("Failed to create program.");
}
print("Program created");
 
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
print("Shaders attached to the program");
 
gl.linkProgram(program);
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
    const log = gl.getProgramInfoLog(program);
    gl.deleteProgram(program);
    throw new Error(`Program link failed: ${log}`);
}
print("Program linked");
 
const min_x = -0.9
const max_x = 0.9
const min_y = min_x
const max_y = max_x
 
// souřadnice vrcholů trojúhelníku + souřadnice v prostoru textury
const vertexes = [
  // x, y,   u, v
   min_x, min_y, 0.0, 0.0,
   min_x, max_y, 0.0, 1.0,
   max_x, max_y, 1.0, 1.0,
 
   min_x, min_y, 0.0, 0.0,
   max_x, max_y, 1.0, 1.0,
   max_x, min_y, 1.0, 0.0,
];
 
// buffer, do kterého se překopírují souřadnice bodu
const vertexBuffer = gl.createBuffer();
if (!vertexBuffer) {
    throw new Error("Failed to create buffer.");
}
print("Vertex buffer created");
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
 
const data = new Float32Array(vertexes);
gl.bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW);
print("Vertex buffer filled-in by vertexes");
 
// navázání na parametr předávaný do vertex shaderu
const p = gl.getAttribLocation(program, "position");
if (p === -1) {
    throw new Error("Attribute 'position' not found in shader program");
}
print("Attribute 'position' found in shader program")
 
// navázání na parametr předávaný do vertex shaderu
const texLoc = gl.getAttribLocation(program, "texcoord");
if (p === -1) {
    throw new Error("Attribute 'texcoord' not found in shader program");
}
print("Attribute 'texcoord' found in shader program")
 
// seznam předávaný do vertex shaderu
gl.vertexAttribPointer(p,
                       2,                                // počet prvků v každém atributu
                       gl.FLOAT,                         // typ prvků
                       gl.FALSE,                         // data nejsou normalizována
                       4*Float32Array.BYTES_PER_ELEMENT, // velikost vertexu+texturovacích souřadnic v bajtech
                       0                                 // offset
);
gl.enableVertexAttribArray(p);
 
// seznam předávaný do vertex shaderu
gl.vertexAttribPointer(texLoc,
                       2,                                // počet prvků v každém atributu
                       gl.FLOAT,                         // typ prvků
                       gl.FALSE,                         // data nejsou normalizována
                       4*Float32Array.BYTES_PER_ELEMENT, // velikost vertexu+texturovacích souřadnic v bajtech
                       2*Float32Array.BYTES_PER_ELEMENT  // offset
);
gl.enableVertexAttribArray(texLoc);
 
// textura (prozatím primitivní)
const texture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);
 
const textureWidth = 256
const textureHeight = 256
 
// textura
textureArray = makeTexture(textureWidth, textureHeight);
const texels = new Uint8Array(textureArray);
 
gl.texImage2D(gl.TEXTURE_2D,      // typ textury: zde 2D textura
              0,                  // úroveň detailu
              gl.RGBA,            // počet barvových komponent (zde 4)
              textureWidth,       // horizontální rozlišení textury
              textureHeight,      // vertikální rozlišení textury
              0,                  // šířka okraje textury
              gl.RGBA,            // formát uložení texelů
              gl.UNSIGNED_BYTE,   // typ každé barvové složky texelu
              texels              // pole texelů
);
 
// způsob vykreslení textury
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
 
// navázání na atribut ve shaderech
const samplerLoc = gl.getUniformLocation(program, "texture");
gl.uniform1i(samplerLoc, 0);
 
// transformace souřadnic: z normalizovaných souřadnic na souřadnice na plátnu
gl.viewport(0, 0, canvas.width, canvas.height);
 
// nastavení barvy pro vymazání barvového bufferu
gl.clearColor(0, 0, 0, 1);
 
// vymazání barvového bufferu
gl.clear(gl.COLOR_BUFFER_BIT);
 
// spuštění programu
gl.useProgram(program);
 
// vykreslení dvojice trojúhelníků
gl.drawArrays(gl.TRIANGLES, 0, 6);

Modifikace uv-souřadnic v prostoru textury

V předchozím textu jsme si řekli, že souřadnice texelů v textuře se značí písmeny u a v a že tyto souřadnice leží v rozsahu 0,0 až 1,1 (včetně obou mezních hodnot). To konkrétně znamená, že texel ležící v levém horním rohu bude mít souřadnice [0, 0], texel v pravém horním rohu souřadnice [1, 0] a texel v pravém dolním rohu pak souřadnice [1, 1]. Co se však stane v případě, že při definici vrcholů trojúhelníků uvedeme větší hodnotu těchto souřadnic? To si ostatně můžeme velmi snadno otestovat:

// souřadnice vrcholů trojúhelníku + souřadnice v prostoru textury
const vertexes = [
  // x, y,   u, v
   min_x, min_y, 0.0, 0.0,
   min_x, max_y, 0.0, 1.0,
   max_x, max_y, 1.0, 1.0,
 
   min_x, min_y, 0.0, 0.0,
   max_x, max_y, 2.0, 2.0,
   max_x, min_y, 2.0, 0.0,
];

V takovém případě se projeví vliv nastavení TEXTURE_WRAP_S a TEXTURE_WRAP_T (označení S a T může být matoucí, to je však v OpenGL použito z historických důvodů, kdy se používal čtyřrozměrný prostor STRQ). Prozatím máme „wrapping“ texelů de facto zakázaný – namísto toho se použijí texely, které se nachází na hranicích textury. V našem konkrétním případě texely z pravé hrany, z dolní hrany a speciálně pak texel umístěný zcela vpravo dole:

gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);

Výsledek můžete vidět zde. Povšimněte si, že se skutečně texely z pravé hrany textury roztáhnou až na hranici obrázku. A pravý dolní texel vyplní celou horní část jednoho z trojúhelníků. Další možnosti budou ukázány v navazujícím článku.

Pátý demonstrační příklad: zobrazení otexturovaného čtverce s modifikovanými uv-souřadnicemi

Dnešní pátý demonstrační příklad si můžete otestovat po zobrazení stránky dostupné na adrese https://tisnik.github.io/tem­p/textures4.html. Úplný zdrojový kód tohoto demonstračního příkladu vypadá následovně:

const vertexShaderSource = `
    attribute vec2 position;
    attribute vec2 texcoord;
    varying vec2 vTexcoord;
 
    void main() {
        gl_Position = vec4(position, 0, 1);
        vTexcoord = texcoord;
    }
`
 
const fragmentShaderSource = `
    precision mediump float;
    varying vec2 vTexcoord;
    uniform sampler2D texture;
 
    void main() {
        gl_FragColor = texture2D(texture, vTexcoord);
    }
`
 
function print(message) {
    console.log(message);
}
 
function makeTexture(width, height) {
    const array = new Array(width*height*4);
 
    let index = 0;
    for (j=0; j<height; j++) {
        for (i=0; i<width; i++) {
            const c = ((i&0x20)==0^(j&0x20)==0) * 255;
            array[index++] = c;
            array[index++] = i;
            array[index++] = j;
            array[index++] = 255;            // alfa slozka
        }
    }
    return array;
}
 
// reference na objekt představující kreslicí plátno umístěné na webové stránce
const canvas = document.getElementById("web_gl_canvas");
if (!canvas) {
    throw new Error("Canvas with id 'web_gl_canvas' not found");
}
print("Canvas object retrieved");
 
// vytvoření a získání kontextu WebGL (3D grafika)
const gl = canvas.getContext("webgl");
if (!gl) {
    throw new Error("WebGL is not supported or context creation failed");
}
print("WebGL context created");
 
// vytvoření vertex shaderu
const vertexShader = gl.createShader(gl.VERTEX_SHADER);
if (!vertexShader) {
    throw new Error("Failed to create vertex shader.");
}
print("Vertex shader created");
 
// překlad vertex shaderu
gl.shaderSource(vertexShader, vertexShaderSource);
gl.compileShader(vertexShader);
if (!gl.getShaderParameter(vertexShader, gl.COMPILE_STATUS)) {
    const log = gl.getShaderInfoLog(vertexShader);
    gl.deleteShader(vertexShader);
    throw new Error(`Vertex shader compile failed: ${log}`);
}
print("Vertex shader compiled");
 
// vytvoření fragment shaderu
const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
if (!fragmentShader) {
    throw new Error("Failed to create fragment shader.");
}
print("Fragment shader created");
 
// překlad fragment shaderu
gl.shaderSource(fragmentShader, fragmentShaderSource);
gl.compileShader(fragmentShader);
if (!gl.getShaderParameter(fragmentShader, gl.COMPILE_STATUS)) {
    const log = gl.getShaderInfoLog(fragmentShader);
    gl.deleteShader(fragmentShader);
    throw new Error(`fragment shader compile failed: ${log}`);
}
print("Fragment shader compiled");
 
// registrace obou shaderů
const program = gl.createProgram();
if (!program) {
    throw new Error("Failed to create program.");
}
print("Program created");
 
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
print("Shaders attached to the program");
 
gl.linkProgram(program);
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
    const log = gl.getProgramInfoLog(program);
    gl.deleteProgram(program);
    throw new Error(`Program link failed: ${log}`);
}
print("Program linked");
 
const min_x = -0.9
const max_x = 0.9
const min_y = min_x
const max_y = max_x
 
// souřadnice vrcholů trojúhelníku + souřadnice v prostoru textury
const vertexes = [
  // x, y,   u, v
   min_x, min_y, 0.0, 0.0,
   min_x, max_y, 0.0, 1.0,
   max_x, max_y, 1.0, 1.0,
 
   min_x, min_y, 0.0, 0.0,
   max_x, max_y, 2.0, 2.0,
   max_x, min_y, 2.0, 0.0,
];
 
// buffer, do kterého se překopírují souřadnice bodu
const vertexBuffer = gl.createBuffer();
if (!vertexBuffer) {
    throw new Error("Failed to create buffer.");
}
print("Vertex buffer created");
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
 
const data = new Float32Array(vertexes);
gl.bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW);
print("Vertex buffer filled-in by vertexes");
 
// navázání na parametr předávaný do vertex shaderu
const p = gl.getAttribLocation(program, "position");
if (p === -1) {
    throw new Error("Attribute 'position' not found in shader program");
}
print("Attribute 'position' found in shader program")
 
// navázání na parametr předávaný do vertex shaderu
const texLoc = gl.getAttribLocation(program, "texcoord");
if (p === -1) {
    throw new Error("Attribute 'texcoord' not found in shader program");
}
print("Attribute 'texcoord' found in shader program")
 
// seznam předávaný do vertex shaderu
gl.vertexAttribPointer(p,
                       2,                                // počet prvků v každém atributu
                       gl.FLOAT,                         // typ prvků
                       gl.FALSE,                         // data nejsou normalizována
                       4*Float32Array.BYTES_PER_ELEMENT, // velikost vertexu+texturovacích souřadnic v bajtech
                       0                                 // offset
);
gl.enableVertexAttribArray(p);
 
// seznam předávaný do vertex shaderu
gl.vertexAttribPointer(texLoc,
                       2,                                // počet prvků v každém atributu
                       gl.FLOAT,                         // typ prvků
                       gl.FALSE,                         // data nejsou normalizována
                       4*Float32Array.BYTES_PER_ELEMENT, // velikost vertexu+texturovacích souřadnic v bajtech
                       2*Float32Array.BYTES_PER_ELEMENT  // offset
);
gl.enableVertexAttribArray(texLoc);
 
// textura (prozatím primitivní)
const texture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);
 
const textureWidth = 256
const textureHeight = 256
 
// textura
textureArray = makeTexture(textureWidth, textureHeight);
const texels = new Uint8Array(textureArray);
 
gl.texImage2D(gl.TEXTURE_2D,      // typ textury: zde 2D textura
              0,                  // úroveň detailu
              gl.RGBA,            // počet barvových komponent (zde 4)
              textureWidth,       // horizontální rozlišení textury
              textureHeight,      // vertikální rozlišení textury
              0,                  // šířka okraje textury
              gl.RGBA,            // formát uložení texelů
              gl.UNSIGNED_BYTE,   // typ každé barvové složky texelu
              texels              // pole texelů
);
 
// způsob vykreslení textury
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
 
// navázání na atribut ve shaderech
const samplerLoc = gl.getUniformLocation(program, "texture");
gl.uniform1i(samplerLoc, 0);
 
// transformace souřadnic: z normalizovaných souřadnic na souřadnice na plátnu
gl.viewport(0, 0, canvas.width, canvas.height);
 
// nastavení barvy pro vymazání barvového bufferu
gl.clearColor(0, 0, 0, 1);
 
// vymazání barvového bufferu
gl.clear(gl.COLOR_BUFFER_BIT);
 
// spuštění programu
gl.useProgram(program);
 
// vykreslení dvojice trojúhelníků
gl.drawArrays(gl.TRIANGLES, 0, 6);

Textury uložené v externích souborech (PNG, JPEG)

V praxi se kromě různých procedurálních textur setkáme i s požadavkem na použití textur, které vzniknou například z fotografií, v grafických editorech atd. Příkladem může být tato textura, která reprezentuje část zdi (a jak uvidíme dále, lze tuto texturu opakovat). Jak se však rastrový obrázek s touto texturou načte do skriptu, který běží v prostředí webového prohlížeče? Ve skutečnosti to není příliš složité, ovšem za předpokladu, že je obrázek dostupný přes HTTP či HTTPS (a navíc je dostupný ze stejné domény jako skript – zde ovšem již záleží na konkrétním nastavení webového prohlížeče). Obecný postup při načtení obrázku s texturou vypadá následovně:

  1. Zkonstruuje se objekt typu Image
  2. Do tohoto objektu se načte rastrový obrázek přes HTTP či HTTPS
  3. Zkonstruuje se textura zavoláním metody gl.texImage2D s mírně upravenými parametry
  4. Poté je již možné nastavit další parametry textury a dokončit vykreslení scény

Realizace načtení rastrového obrázku

Konstrukce prázdného objektu typu Image je triviální:

// obrázek s texturou
const img = new Image();

Načtení rastrového obrázku následované konstrukcí textury lze realizovat například přes tuto anonymní funkci, která se provede po načtení dat s obrázkem. Povšimněte si, že do metody gl.texImage2D se nepředává rozlišení textury, ale přímo objekt img (to klasické OpenGL ES nepodporuje):

img.onload = () => {
    print("Loading texture");
    print(img);
    const texture = gl.createTexture();
    gl.bindTexture(gl.TEXTURE_2D, texture);
 
    gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);
    gl.texImage2D(
        gl.TEXTURE_2D,      // typ textury: zde 2D textura
        0,                  // úroveň detailu
        gl.RGBA,            // interní formát uložení texelů
        gl.RGBA,            // formát uložení texelů
        gl.UNSIGNED_BYTE,   // typ každé barvové složky texelu
        img                 // objekt s obrázkem
    );
 
    // způsob vykreslení textury
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
 
    print("Texture loaded");
 
    draw();
};

Výše uvedená anonymní funkce zavolala funkci draw, která provede dokončení vykreslení celé scény:

Wazuh školení

function draw() {
    // navázání na atribut ve shaderech
    const samplerLoc = gl.getUniformLocation(program, "texture");
    gl.uniform1i(samplerLoc, 0);
 
    // transformace souřadnic: z normalizovaných souřadnic na souřadnice na plátnu
    gl.viewport(0, 0, canvas.width, canvas.height);
 
    // nastavení barvy pro vymazání barvového bufferu
    gl.clearColor(0, 0, 0, 1);
 
    // vymazání barvového bufferu
    gl.clear(gl.COLOR_BUFFER_BIT);
 
    // spuštění programu
    gl.useProgram(program);
 
    // vykreslení dvojice trojúhelníků
    gl.drawArrays(gl.TRIANGLES, 0, 6);
}
Poznámka: nelze tedy pouze napsat sekvenci operací, protože načtení obrázku je provedeno asynchronně a musíme čekat na načtení a inicializaci textury. Jinak by se sice oba trojúhelníky vykreslily, ale „jen“ černou barvou.

Šestý demonstrační příklad: čtverec s texturou načtenou z externího obrázku

Šestý a současně dnes již poslední demonstrační příklad je spustitelný z adresy https://tisnik.github.io/tem­p/textures5.html. Jeho úplný zdrojový kód vypadá následovně:

const vertexShaderSource = `
    attribute vec2 position;
    attribute vec2 texcoord;
    varying vec2 vTexcoord;
 
    void main() {
        gl_Position = vec4(position, 0, 1);
        vTexcoord = texcoord;
    }
`
 
const fragmentShaderSource = `
    precision mediump float;
    varying vec2 vTexcoord;
    uniform sampler2D texture;
 
    void main() {
        gl_FragColor = texture2D(texture, vTexcoord);
    }
`
 
function print(message) {
    console.log(message);
}
 
// reference na objekt představující kreslicí plátno umístěné na webové stránce
const canvas = document.getElementById("web_gl_canvas");
if (!canvas) {
    throw new Error("Canvas with id 'web_gl_canvas' not found");
}
print("Canvas object retrieved");
 
// vytvoření a získání kontextu WebGL (3D grafika)
const gl = canvas.getContext("webgl");
if (!gl) {
    throw new Error("WebGL is not supported or context creation failed");
}
print("WebGL context created");
 
// vytvoření vertex shaderu
const vertexShader = gl.createShader(gl.VERTEX_SHADER);
if (!vertexShader) {
    throw new Error("Failed to create vertex shader.");
}
print("Vertex shader created");
 
// překlad vertex shaderu
gl.shaderSource(vertexShader, vertexShaderSource);
gl.compileShader(vertexShader);
if (!gl.getShaderParameter(vertexShader, gl.COMPILE_STATUS)) {
    const log = gl.getShaderInfoLog(vertexShader);
    gl.deleteShader(vertexShader);
    throw new Error(`Vertex shader compile failed: ${log}`);
}
print("Vertex shader compiled");
 
// vytvoření fragment shaderu
const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
if (!fragmentShader) {
    throw new Error("Failed to create fragment shader.");
}
print("Fragment shader created");
 
// překlad fragment shaderu
gl.shaderSource(fragmentShader, fragmentShaderSource);
gl.compileShader(fragmentShader);
if (!gl.getShaderParameter(fragmentShader, gl.COMPILE_STATUS)) {
    const log = gl.getShaderInfoLog(fragmentShader);
    gl.deleteShader(fragmentShader);
    throw new Error(`fragment shader compile failed: ${log}`);
}
print("Fragment shader compiled");
 
// registrace obou shaderů
const program = gl.createProgram();
if (!program) {
    throw new Error("Failed to create program.");
}
print("Program created");
 
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
print("Shaders attached to the program");
 
gl.linkProgram(program);
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
    const log = gl.getProgramInfoLog(program);
    gl.deleteProgram(program);
    throw new Error(`Program link failed: ${log}`);
}
print("Program linked");
 
const min_x = -0.9
const max_x = 0.9
const min_y = min_x
const max_y = max_x
 
// souřadnice vrcholů trojúhelníku + souřadnice v prostoru textury
const vertexes = [
  // x, y,   u, v
   min_x, min_y, 0.0, 0.0,
   min_x, max_y, 0.0, 1.0,
   max_x, max_y, 1.0, 1.0,
 
   min_x, min_y, 0.0, 0.0,
   max_x, max_y, 1.0, 1.0,
   max_x, min_y, 1.0, 0.0,
];
 
// buffer, do kterého se překopírují souřadnice bodu
const vertexBuffer = gl.createBuffer();
if (!vertexBuffer) {
    throw new Error("Failed to create buffer.");
}
print("Vertex buffer created");
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
 
const data = new Float32Array(vertexes);
gl.bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW);
print("Vertex buffer filled-in by vertexes");
 
// navázání na parametr předávaný do vertex shaderu
const p = gl.getAttribLocation(program, "position");
if (p === -1) {
    throw new Error("Attribute 'position' not found in shader program");
}
print("Attribute 'position' found in shader program")
 
// navázání na parametr předávaný do vertex shaderu
const texLoc = gl.getAttribLocation(program, "texcoord");
if (p === -1) {
    throw new Error("Attribute 'texcoord' not found in shader program");
}
print("Attribute 'texcoord' found in shader program")
 
// seznam předávaný do vertex shaderu
gl.vertexAttribPointer(p,
                       2,                                // počet prvků v každém atributu
                       gl.FLOAT,                         // typ prvků
                       gl.FALSE,                         // data nejsou normalizována
                       4*Float32Array.BYTES_PER_ELEMENT, // velikost vertexu+texturovacích souřadnic v bajtech
                       0                                 // offset
);
gl.enableVertexAttribArray(p);
 
// seznam předávaný do vertex shaderu
gl.vertexAttribPointer(texLoc,
                       2,                                // počet prvků v každém atributu
                       gl.FLOAT,                         // typ prvků
                       gl.FALSE,                         // data nejsou normalizována
                       4*Float32Array.BYTES_PER_ELEMENT, // velikost vertexu+texturovacích souřadnic v bajtech
                       2*Float32Array.BYTES_PER_ELEMENT  // offset
);
gl.enableVertexAttribArray(texLoc);
 
// obrázek s texturou
const img = new Image();
 
img.onload = () => {
    print("Loading texture");
    print(img);
    const texture = gl.createTexture();
    gl.bindTexture(gl.TEXTURE_2D, texture);
 
    gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);
    gl.texImage2D(
        gl.TEXTURE_2D,      // typ textury: zde 2D textura
        0,                  // úroveň detailu
        gl.RGBA,            // interní formát uložení texelů
        gl.RGBA,            // formát uložení texelů
        gl.UNSIGNED_BYTE,   // typ každé barvové složky texelu
        img                 // objekt s obrázkem
    );
 
    // způsob vykreslení textury
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
 
    print("Texture loaded");
 
    draw();
};
 
img.src = "texture.png";
 
function draw() {
    // navázání na atribut ve shaderech
    const samplerLoc = gl.getUniformLocation(program, "texture");
    gl.uniform1i(samplerLoc, 0);
 
    // transformace souřadnic: z normalizovaných souřadnic na souřadnice na plátnu
    gl.viewport(0, 0, canvas.width, canvas.height);
 
    // nastavení barvy pro vymazání barvového bufferu
    gl.clearColor(0, 0, 0, 1);
 
    // vymazání barvového bufferu
    gl.clear(gl.COLOR_BUFFER_BIT);
 
    // spuštění programu
    gl.useProgram(program);
 
    // vykreslení dvojice trojúhelníků
    gl.drawArrays(gl.TRIANGLES, 0, 6);
}

Repositář s demonstračními příklady

Všechny demonstrační příklady, které jsme si v předchozí pětici článků i v dnešním článku ukázali, je možné spustit přímo ve webovém prohlížeči přes odkazy uvedené v tabulce pod tímto odstavcem. Doplněny jsou i odkazy na zdrojové kódy psané v JavaScriptu (ty se automaticky načítají do HTML stránek):

# Příklad Stručný popis Adresa příkladu
1 step1.html webová stránka s kreslicím plátnem (canvasem) https://tisnik.github.io/tem­p/step1.html
2 step1.js skript s implementací tohoto příkladu https://tisnik.github.io/tem­p/step1.js
     
3 step2.html využití kreslicího plátna s 2D kontextem https://tisnik.github.io/tem­p/step2.html
4 step2.js skript s implementací tohoto příkladu https://tisnik.github.io/tem­p/step2.js
     
5 step3.html vymazání barvového bufferu pro kontext WebGL https://tisnik.github.io/tem­p/step3.html
6 step3.js skript s implementací tohoto příkladu https://tisnik.github.io/tem­p/step3.js
     
7 step4.html transformace souřadnic: z normalizovaných souřadnic na souřadnice na plátnu https://tisnik.github.io/tem­p/step4.html
8 step4.js skript s implementací tohoto příkladu https://tisnik.github.io/tem­p/step4.js
     
9 step5.html vykreslení jediného bodu na plátno v kontextu WebGL https://tisnik.github.io/tem­p/step5.html
10 step5.js skript s implementací tohoto příkladu https://tisnik.github.io/tem­p/step5.js
     
11 step6.html vykreslení bodu s určením jeho souřadnic ve scéně https://tisnik.github.io/tem­p/step6.html
12 step6.js skript s implementací tohoto příkladu https://tisnik.github.io/tem­p/step6.js
     
13 step7.html vykreslení většího množství bodů do scény https://tisnik.github.io/tem­p/step7.html
14 step7.js skript s implementací tohoto příkladu https://tisnik.github.io/tem­p/step7.js
     
15 step8.html kontrola provedení jednotlivých operací https://tisnik.github.io/tem­p/step8.html
16 step8.js skript s implementací tohoto příkladu https://tisnik.github.io/tem­p/step8.js
     
17 step9.html vykreslení trojice úseček na plochu kreslicího plátna https://tisnik.github.io/tem­p/step9.html
18 step9.js skript s implementací tohoto příkladu https://tisnik.github.io/tem­p/step9.js
     
19 stepA.html nastavení konstantní barvy úseček https://tisnik.github.io/tem­p/stepA.html
20 stepA.js skript s implementací tohoto příkladu https://tisnik.github.io/tem­p/stepA.js
     
21 stepB.html vykreslení na sebe navazujících úseček s předáním souřadnic https://tisnik.github.io/tem­p/stepB.html
22 stepB.js skript s implementací tohoto příkladu https://tisnik.github.io/tem­p/stepB.js
     
23 stepC.html vykreslení uzavřené sekvence úseček s předáním souřadnic https://tisnik.github.io/tem­p/stepC.html
24 stepC.js skript s implementací tohoto příkladu https://tisnik.github.io/tem­p/stepC.js
     
25 stepD.html vykreslení bodů, každý bod může mít libovolnou barvu https://tisnik.github.io/tem­p/stepD.html
26 stepD.js skript s implementací tohoto příkladu https://tisnik.github.io/tem­p/stepD.js
     
27 stepE.html vykreslení úseček, barva je vypočtena interpolací barev https://tisnik.github.io/tem­p/stepE.html
28 stepE.js skript s implementací tohoto příkladu https://tisnik.github.io/tem­p/stepE.js
     
29 triangle1.html vykreslení trojúhelníku konstantní barvou https://tisnik.github.io/tem­p/triangle1.html
30 triangle1.js skript s implementací tohoto příkladu https://tisnik.github.io/tem­p/triangle1.js
     
31 triangle2.html interpolace barev při vyplňování trojúhelníku https://tisnik.github.io/tem­p/triangle2.html
32 triangle2.js skript s implementací tohoto příkladu https://tisnik.github.io/tem­p/triangle2.js
     
33 triangle3.html vykreslení pásu trojúhelníků https://tisnik.github.io/tem­p/triangle3.html
34 triangle3.js skript s implementací tohoto příkladu https://tisnik.github.io/tem­p/triangle3.js
     
35 triangle4.html vykreslení trsu trojúhelníků – neúplná varianta https://tisnik.github.io/tem­p/triangle4.html
36 triangle4.js skript s implementací tohoto příkladu https://tisnik.github.io/tem­p/triangle4.js
     
37 triangle5.html vykreslení trsu trojúhelníků – úplná varianta https://tisnik.github.io/tem­p/triangle5.html
38 triangle5.js skript s implementací tohoto příkladu https://tisnik.github.io/tem­p/triangle5.js
     
39 triangle6.html překrývající se trojúhelníky (Z-buffer není použit) https://tisnik.github.io/tem­p/triangle6.html
40 triangle6.js skript s implementací tohoto příkladu https://tisnik.github.io/tem­p/triangle6.js
     
41 triangle7.html překrývající se trojúhelníky (Z-buffer je použit) https://tisnik.github.io/tem­p/triangle7.html
42 triangle7.js skript s implementací tohoto příkladu https://tisnik.github.io/tem­p/triangle7.js
     
43 cube1.html 3D krychle vykreslená do 2D plochy bez aplikace transformací https://tisnik.github.io/tem­p/cube1.html
44 cube1.js skript s implementací tohoto příkladu https://tisnik.github.io/tem­p/cube1.js
     
45 cube2.html transformace reprezentovaná jednotkovou maticí https://tisnik.github.io/tem­p/cube2.html
46 cube2.js skript s implementací tohoto příkladu https://tisnik.github.io/tem­p/cube2.js
     
47 cube3.html zobrazení 3D kostky z různých úhlů pohledu https://tisnik.github.io/tem­p/cube3.html
48 cube3.js skript s implementací tohoto příkladu https://tisnik.github.io/tem­p/cube3.js
     
49 cube4.html zobrazení 3D kostky s využitím základní perspektivní projekce https://tisnik.github.io/tem­p/cube4.html
50 cube4.js skript s implementací tohoto příkladu https://tisnik.github.io/tem­p/cube4.js
     
51 cube5.html krychle v 3D prostoru https://tisnik.github.io/tem­p/cube5.html
52 cube5.js skript s implementací tohoto příkladu https://tisnik.github.io/tem­p/cube5.js
     
53 cube6.html rotace krychle v 3D prostoru s perspektivní projekcí https://tisnik.github.io/tem­p/cube6.html
54 cube6.js skript s implementací tohoto příkladu https://tisnik.github.io/tem­p/cube6.js
     
55 cube7.html animace rotace krychle složené z hran (úseček) v 3D prostoru s perspektivní projekcí https://tisnik.github.io/tem­p/cube7.html
56 cube7.js skript s implementací tohoto příkladu https://tisnik.github.io/tem­p/cube7.js
     
57 cube8.html animace rotace krychle složené z trojúhelníků v 3D prostoru s perspektivní projekcí https://tisnik.github.io/tem­p/cube8.html
58 cube8.js skript s implementací tohoto příkladu https://tisnik.github.io/tem­p/cube8.js
     
59 cube9.html animace rotace krychle složené z trojúhelníků v 3D prostoru s perspektivní projekcí a povolením paměti hloubky https://tisnik.github.io/tem­p/cube9.html
60 cube9.js skript s implementací tohoto příkladu https://tisnik.github.io/tem­p/cube9.js
     
60 cubeA.html změna vzdálenosti bližší pohledové roviny https://tisnik.github.io/tem­p/cubeA.html
61 cubeA.js skript s implementací tohoto příkladu https://tisnik.github.io/tem­p/cubeA.js
     
62 cubeB.html doplnění rotující krychle o obě chybějící stěny https://tisnik.github.io/tem­p/cubeB.html
63 cubeB.js skript s implementací tohoto příkladu https://tisnik.github.io/tem­p/cubeB.js
     
64 texsize.html zjištění maximálního rozlišení textury https://tisnik.github.io/tem­p/texsize.html
65 texsize.js skript s implementací tohoto příkladu https://tisnik.github.io/tem­p/texsize.js
     
66 texture1.html textura namapovaná na jediný trojúhelník https://tisnik.github.io/tem­p/texture1.html
67 texture1.js skript s implementací tohoto příkladu https://tisnik.github.io/tem­p/texture1.js
     
68 texture2.html textura namapovaná na dvojici trojúhelníků tvořících čtvercovou stěnu https://tisnik.github.io/tem­p/texture2.html
69 texture2.js skript s implementací tohoto příkladu https://tisnik.github.io/tem­p/texture2.js
     
70 texture3.html jednoduchá procedurální textura https://tisnik.github.io/tem­p/texture3.html
71 texture3.js skript s implementací tohoto příkladu https://tisnik.github.io/tem­p/texture3.js
     
72 texture4.html modifikace UV souřadnic textury https://tisnik.github.io/tem­p/texture4.html
73 texture4.js skript s implementací tohoto příkladu https://tisnik.github.io/tem­p/texture4.js
     
74 texture5.html textura načtená z rastrového obrázku https://tisnik.github.io/tem­p/texture5.html
75 texture5.js skript s implementací tohoto příkladu https://tisnik.github.io/tem­p/texture5.js

Odkazy na Internetu

  1. WebGL: 3D Graphics for the Web
    https://www.khronos.org/webgl/
  2. OpenGL Shading Language
    https://en.wikipedia.org/wi­ki/OpenGL_Shading_Language
  3. WebGL (Wikipedia)
    https://en.wikipedia.org/wiki/WebGL
  4. WebGL
    https://wikis.khronos.org/web­gl/Main_Page
  5. OpenGL ES (Wikipedia)
    https://en.wikipedia.org/wi­ki/OpenGL_ES
  6. Float32Array
    https://developer.mozilla.org/en-US/docs/Web/JavaScript/Re­ference/Global_Objects/Flo­at32Array
  7. ECMAScript compatibility table
    https://compat-table.github.io/compat-table/es6/
  8. Learn-OpenGLES-Tutorials
    https://github.com/learnopengles/Learn-OpenGLES-Tutorials
  9. Implement OpenGL ES and EGL
    https://source.android.com/doc­s/core/graphics/implement-opengl-es
  10. Khronos Group
    https://www.khronos.org/
  11. Khronos Group (Wikipedia)
    https://en.wikipedia.org/wi­ki/Khronos_Group
  12. Raspberry Pi VideoCore APIs
    http://elinux.org/Raspberry_Pi_Vi­deoCore_APIs
  13. EGL quick reference card
    https://www.khronos.org/files/egl-1–4-quick-reference-card.pdf
  14. EGL Reference Pages Index
    https://www.khronos.org/re­gistry/egl/sdk/docs/man/html/in­dexflat.php
  15. GLSL
    https://glsl.app/
  16. Shader Tutorials by Ronja
    https://www.ronja-tutorials.com/
  17. CanvasRenderingContext2D
    https://developer.mozilla.org/en-US/docs/Web/API/CanvasRen­deringContext2D
  18. WebGL2RenderingContext
    https://developer.mozilla.org/en-US/docs/Web/API/WebGL2RenderingContext
  19. WebGLRenderingContext
    https://developer.mozilla.org/en-US/docs/Web/API/WebGLRenderingContext
  20. Matrix Transformations
    https://vitaminac.github.io/Matrices-in-Computer-Graphics/
  21. Homogeneous coordinates
    https://en.wikipedia.org/wi­ki/Homogeneous_coordinates
  22. Transformation matrix
    https://en.wikipedia.org/wi­ki/Transformation_matrix
  23. Affine transformation
    https://en.wikipedia.org/wi­ki/Affine_transformation
  24. OpenGL Projection Matrix
    https://songho.ca/opengl/gl_pro­jectionmatrix.html
  25. Texture mapping
    https://en.wikipedia.org/wi­ki/Texture_mapping
  26. UV mapping
    https://en.wikipedia.org/wi­ki/UV_mapping
  27. UV Coordinates explained
    https://blog.nobel-joergensen.com/2011/04/05/procedural-generated-mesh-in-unity-part-2-with-uv-mapping/
  28. Introduction to texture mapping
    http://www.decew.net/OSS/Re­ferences/chapter_2_texture_map­ping.pdf
Neutrální ikona do widgetu na odběr článků ze seriálů

Zajímá vás toto téma? Chcete se o něm dozvědět víc?

Objednejte si upozornění na nově vydané články do vašeho mailu. Žádný článek vám tak neuteče.


Autor článku

Vystudoval VUT FIT a v současné době pracuje na projektech vytvářených v jazycích Python a Go.