WebGL: tvorba 3D grafiky s využitím programovatelné vykreslovací pipeline (3. část)

Dnes
Doba čtení: 37 minut

Sdílet

3D konvička wireframe
Autor: Hay Kranen, Wikimedia, podle licence: CC-BY
Budeme se zabývat vykreslováním povrchů těles složených z trojúhelníků. A nezapomeneme ani na popis funkce paměti hloubky (depth buffer, Z-buffer). Tím získáme základní poznatky nutné pro vykreslování 3D scén.

V předchozí dvojici článků o technologiích WebGL a OpenGL ES jsme si vysvětlili postup pro vykreslování bodů a úseček. To je ovšem při požadavcích na vykreslení plochy těles (a nikoli pouze na „drátový model“) nedostačující. Dnes si ukážeme řešení spočívající ve vykreslování vyplněných trojúhelníků.

Co se dozvíte v článku
  1. Postup při vykreslení trojúhelníka konstantní barvou
  2. První demonstrační příklad: vykreslení trojúhelníka konstantní barvou
  3. Interpolace barev při rasterizaci trojúhelníků
  4. Předání barev vrcholů trojúhelníka do vykreslovací pipeline
  5. Druhý demonstrační příklad: vykreslení trojúhelníka s interpolací barev
  6. Optimalizace reprezentace plochy vykreslovaných těles: pruhy a trsy trojúhelníků
  7. Realizace vykreslení pruhu trojúhelníků
  8. Třetí demonstrační příklad: vykreslení pruhu trojúhelníků
  9. Realizace vykreslení trsu trojúhelníků
  10. Čtvrtý demonstrační příklad: vykreslení trsu trojúhelníků: neúplná varianta
  11. Pátý demonstrační příklad: vykreslení trsu trojúhelníků: úplná varianta
  12. Základy vykreslování 3D scén
  13. Paměť hloubky
  14. Postup při použití paměti hloubky pro vykreslování překrývajících se ploch
  15. Demonstrační příklad: vykreslení trojúhelníků, které se překrývají, bez použití paměti hloubky
  16. Vykreslení překrývajících se trojúhelníků s využitím paměti hloubky
  17. Repositář s demonstračními příklady
  18. Seriály a články o OpenGL i o přidružených technologiích
  19. Odkazy na Internetu

OpenGL ES, na rozdíl od klasické knihovny OpenGL, nepodporuje přímé vykreslování čtyřúhelníků ani obecnějších polynomů. To má své dobré důvody, protože by bylo nutné řešit situace, kdy vrcholy neleží v jedné rovině atd. Prakticky jakýkoli povrch tělesa lze rozdělit na trojúhelníky, takže podpora pouze tohoto grafického primitiva by mohla být z tohoto pohledu dostačující. Na druhou stranu je však vhodné omezit množství dat, které se posílají do GPU, takže OpenGL ES podporuje i takzvané pruhy a trsy trojúhelníků, což jsou množiny trojúhelníků, které sdílí společné vrcholy.

3D model čajové konvičky Martina Newella je ve světě 3D grafiky používán ve stejném duchu, jako "Hello, world" pro aplikace psané pro terminál

Obrázek 1: 3D model čajové konvičky Martina Newella je ve světě 3D grafiky používán ve stejném duchu, jako „Hello, world“ pro aplikace psané pro terminál.

Autor: tisnik, podle licence: Rights Managed

Ovšem ani možnost vykreslování trojúhelníků není plně postačující pro vizualizaci skutečných 3D scén. Musíme přidat ještě jednu technologii, konkrétně technologii pro skrývání těch ploch, které jsou (pro konkrétního pozorovatele) zakryty jinými plochami. V případě OpenGL ES je toto skrývání řešeno pamětí hloubky (depth buffer). Základní koncepty práce s pamětí hloubky si ukážeme v závěrečné části dnešního článku.

Postup při vykreslení trojúhelníka konstantní barvou

V dnešním prvním demonstračním příkladu je ukázáno, jak lze vykreslit trojúhelník, což je základní tvar, z něhož se mohou skládat plochy těles atd. Nejjednodušší budou (prozatím) oba shadery, tj. vertex shader i fragment shader, protože budou mít totožný tvar, jako tomu bylo u úvodních příkladů, které jsme si ukázali v prvním článku o WebGL (vykreslení bodů).

Vertex shader pouze zkonstruuje 4D vektor ze zadaných 3D souřadnic:

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

Fragment shader nastaví konstantní barvu vykreslování všech fragmentů:

precision mediump float;
 
void main() {
    gl_FragColor = vec4(0.7, 0.7, 0.0, 1);
}

Dále je pochopitelně nutné definovat souřadnice vrcholů, což je šestice hodnot s plovoucí řádovou čárkou:

const vertexes = [
   // x   y  
   -0.9, -0.8,
    0.9, -0.8,
    0.0,  0.7,
];

Vertexy předáme do bufferu, což je postup, který již známe (a tedy není nutné ho znovu opisovat). Lišit se bude pouze poslední příkaz posílaný do OpenGL ES. Vyžádáme si totiž vykreslení trojúhelníku (nebo většího množství trojúhelníků) a určíme, že se do vykreslovací pipeline předá trojice vrcholů:

// vykreslení trojúhelníku
gl.drawArrays(gl.TRIANGLES, 0, 3);
Jednobarevný trojúhelník.

Obrázek 2: Jednobarevný trojúhelník.

Autor: tisnik, podle licence: Rights Managed

První demonstrační příklad: vykreslení trojúhelníka konstantní barvou

Úplný zdrojový kód dnešního prvního demonstračního příkladu, který na plátno (canvas) vykreslí trojúhelník konstantní barvou, vypadá následovně:

const vertexShaderSource = `
    attribute vec2 position;
 
    void main() {
        gl_Position = vec4(position, 0, 1);
    }
`
 
const fragmentShaderSource = `
    precision mediump float;
 
    void main() {
        gl_FragColor = vec4(0.7, 0.7, 0.0, 1);
    }
`
 
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");
 
// souřadnice vrcholů trojúhelníku
const vertexes = [
   // x   y  
   -0.9, -0.8,
    0.9, -0.8,
    0.0,  0.7,
];
 
// 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")
 
 
// 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
                       2*Float32Array.BYTES_PER_ELEMENT, // velikost vertexu v bajtech
                       0                                 // offset
);
gl.enableVertexAttribArray(p);
 
// 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);

Interpolace barev při rasterizaci trojúhelníků

V předchozím článku jsme si ukázali, že grafická pipeline OpenGL ES při rasterizaci úseček automaticky provádí interpolaci barev. V případě úseček je to jednoduchá lineární interpolace mezi dvojicí barev, které jsou specifikovány pro oba vrcholy úsečky:

Trojice úseček, při jejich vykreslování byla využita lineární interpolace barvy.

Obrázek 3: Trojice úseček, při jejich vykreslování byla využita lineární interpolace barvy.

Autor: tisnik, podle licence: Rights Managed

Podobným způsobem, i když interně nepatrně komplikovanějším, je realizována i interpolace barev při vykreslování trojúhelníků. Zadány jsou tři barvy a výsledek může vypadat následovně (pro trojúhelník, k jehož vrcholům je přiřazena čistě červená, modrá resp. zelená barva):

Klasický RGB trojúhelník.

Obrázek 4: Klasický RGB trojúhelník.

Autor: tisnik, podle licence: Rights Managed

Předání barev vrcholů trojúhelníka do vykreslovací pipeline

Způsob předání barev vrcholů trojúhelníků do vykreslovací pipeline se nijak neliší od postupu, který byl použit při vykreslování úseček. Barvy všech tří vrcholů trojúhelníka mohou být (na straně JavaScriptu) reprezentovány například polem:

// barvy vrcholů trojúhelníku
const colors = [
 // R  G  B  A
    1, 0, 0, 1,
    0, 1, 0, 1,
    0, 0, 1, 1,
];

Vertex shader předá načtenou barvu do dalších kroků pipeline:

attribute vec2 position;
attribute vec4 aColor;
varying vec4 vColor;
 
void main() {
    vColor = aColor;
    gl_Position = vec4(position, 0, 1);
    gl_PointSize = 20.0;
}

Fragment shader barvu z vColor přečte a nastaví gl_FragColor:

precision mediump float;
varying vec4 vColor;
 
void main() {
    gl_FragColor = vColor;
}

I transformaci barev z pole do bufferu OpenGL ES jsme si již popsali:

const colorBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer);
 
const cdata = new Float32Array(colors);
gl.bufferData(gl.ARRAY_BUFFER, cdata, gl.STATIC_DRAW);

Přenos bufferu do grafické pipeline:

const aColor = gl.getAttribLocation(program, "aColor");
gl.enableVertexAttribArray(aColor);
gl.vertexAttribPointer(aColor,
                       4,                                // 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 v bajtech
                       0                                 // offset
);

Druhý demonstrační příklad: vykreslení trojúhelníka s interpolací barev

Postup vysvětlený v předchozí kapitole je použit v dnešním druhém demonstračním příkladu. Ten by měl na plátno vykreslit klasický RGB trojúhelník, který lze najít v mnoha návodech k OpenGL (nebo i OpenGL ES). Není divu, protože se jedná o základ většiny složitějších scén:

const vertexShaderSource = `
    attribute vec2 position;
    attribute vec4 aColor;
    varying vec4 vColor;
 
    void main() {
        vColor = aColor;
        gl_Position = vec4(position, 0, 1);
    }
`
 
const fragmentShaderSource = `
    precision mediump float;
    varying vec4 vColor;
 
    void main() {
        gl_FragColor = vColor;
    }
`
 
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");
 
// souřadnice vrcholů trojúhelníku
const vertexes = [
   // x   y  
   -0.9, -0.8,
    0.9, -0.8,
    0.0,  0.7,
];
 
// barvy vrcholů trojúhelníku
const colors = [
 // R  G  B  A
    1, 0, 0, 1,
    0, 1, 0, 1,
    0, 0, 1, 1,
];
 
// 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")
 
 
// 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
                       2*Float32Array.BYTES_PER_ELEMENT, // velikost vertexu v bajtech
                       0                                 // offset
);
gl.enableVertexAttribArray(p);
 
const colorBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer);
 
const cdata = new Float32Array(colors);
gl.bufferData(gl.ARRAY_BUFFER, cdata, gl.STATIC_DRAW);
 
const aColor = gl.getAttribLocation(program, "aColor");
gl.enableVertexAttribArray(aColor);
gl.vertexAttribPointer(aColor,
                       4,                                // 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 v bajtech
                       0                                 // offset
);
 
// 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);

Optimalizace reprezentace plochy vykreslovaných těles: pruhy a trsy trojúhelníků

Základní grafickou primitivou používanou pro více či méně dokonalou reprezentaci plochy vykreslovaných těles jsou jednotlivé trojúhelníky. Povrch tělesa může být rozložen na jednotlivé trojúhelníky například takto:

Čajová konvička Martina Newella po triangulaci.

Obrázek 5: Čajová konvička Martina Newella po triangulaci.

Autor: Martin Newell, podle licence: Rights Managed

Pokud se nad předchozím modelem zamyslíme, zjistíme, že se sice skutečně jedná o trojúhelníky, které ovšem sdílí své vrcholy s trojúhelníky sousedními. Proto by bylo vhodné do grafické pipeline posílat každou souřadnici (pro společné vrcholy) jen jedenkrát, aby se ušetřila paměť i obsazení sběrnice/portu při přenosu dat. Proto bylo navrženo a implementováno několik technik, které počet přenášených vrcholů do jisté míry snižují. Konkrétně v OpenGL ES můžeme namísto jednotlivých trojúhelníků vykreslovat pruhy trojúhelníků a trsy trojúhelníků. Povšimněte si, v jakém pořadí jsou posílány vrcholy v případě, že tuto optimalizační techniku použijeme:

Grafické primitivum GL_TRIANGLE_FAN - trs trojúhelníků.

Obrázek 6: Grafické primitivum GL_TRIANGLE_FAN – trs trojúhelníků.

Autor: tisnik, podle licence: Rights Managed

Grafické primitivum GL_TRIANGLE_STRIP - pás složený z trojúhelníků.

Obrázek 7: Grafické primitivum GL_TRIANGLE_STRIP – pás složený z trojúhelníků.

Autor: tisnik, podle licence: Rights Managed

Realizace vykreslení pruhu trojúhelníků

Častěji než s trsem trojúhelníků se setkáme s využitím pruhu trojúhelníků (triangle strip) pro reprezentaci částí pláště těles v 3D modelech. Způsob uspořádání vrcholů popisujících pruh trojúhelníků byl ukázán na sedmém obrázku. Pokusme se tedy takový pruh vykreslit (a to i s interpolací barvy – ta je provedena pro každý trojúhelník zvlášť). Vrcholy trojúhelníků uspořádaných do pruhu:

// souřadnice vrcholů trojúhelníků v pruhu
const vertexes = [
   // x   y  
   -0.8, -0.8,
   -0.4,  0.8,
    0.0, -0.8,
    0.4,  0.8,
    0.8, -0.8,
];

V tomto poli jsou vlastně uloženy souřadnice tří trojúhelníků:

   -0.8, -0.8,
   -0.4,  0.8,
    0.0, -0.8,
     
   -0.4,  0.8,
    0.0, -0.8,
    0.4,  0.8,
     
    0.0, -0.8,
    0.4,  0.8,
    0.8, -0.8,

Totéž platí pro barvy:

// barvy vrcholů trojúhelníku
const colors = [
 // R  G  B  A
    1, 0, 0, 1,   // červená
    1, 1, 0, 1,   // žlutá
    0, 1, 0, 1,   // zelená
    0, 1, 1, 1,   // zelenomodrá
    0, 0, 1, 1,   // modrá
];

Což jsou ve skutečnosti barvy tří trojic vrcholů:

    1, 0, 0, 1,   // červená
    1, 1, 0, 1,   // žlutá
    0, 1, 0, 1,   // zelená
     
    1, 1, 0, 1,   // žlutá
    0, 1, 0, 1,   // zelená
    0, 1, 1, 1,   // zelenomodrá
     
    0, 1, 0, 1,   // zelená
    0, 1, 1, 1,   // zelenomodrá
    0, 0, 1, 1,   // modrá

Samotné vykreslení se odlišuje pouze parametrem předaným do operace drawArrays:

// vykreslení pruhu trojúhelníků
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 5);

Výsledek by měl vypadat následovně:

Pruh složený z trojice trojúhelníků.

Obrázek 8: Pruh složený z trojice trojúhelníků.

Autor: tisnik, podle licence: Rights Managed

Třetí demonstrační příklad: vykreslení pruhu trojúhelníků

Úplný zdrojový kód demonstračního příkladu, který vykreslí pruh trojúhelníků (pochopitelně i s korektní interpolací barev) vypadá následovně:

const vertexShaderSource = `
    attribute vec2 position;
    attribute vec4 aColor;
    varying vec4 vColor;
 
    void main() {
        vColor = aColor;
        gl_Position = vec4(position, 0, 1);
    }
`
 
const fragmentShaderSource = `
    precision mediump float;
    varying vec4 vColor;
 
    void main() {
        gl_FragColor = vColor;
    }
`
 
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");
 
// souřadnice vrcholů trojúhelníku
const vertexes = [
   // x   y  
   -0.8, -0.8,
   -0.4,  0.8,
    0.0, -0.8,
    0.4,  0.8,
    0.8, -0.8,
];
 
// barvy vrcholů trojúhelníku
const colors = [
 // R  G  B  A
    1, 0, 0, 1,   // červená
    1, 1, 0, 1,   // žlutá
    0, 1, 0, 1,   // zelená
    0, 1, 1, 1,   // zelenomodrá
    0, 0, 1, 1,   // modrá
];
 
// 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")
 
 
// 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
                       2*Float32Array.BYTES_PER_ELEMENT, // velikost vertexu v bajtech
                       0                                 // offset
);
gl.enableVertexAttribArray(p);
 
const colorBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer);
 
const cdata = new Float32Array(colors);
gl.bufferData(gl.ARRAY_BUFFER, cdata, gl.STATIC_DRAW);
 
const aColor = gl.getAttribLocation(program, "aColor");
gl.enableVertexAttribArray(aColor);
gl.vertexAttribPointer(aColor,
                       4,                                // 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 v bajtech
                       0                                 // offset
);
 
// 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í pruhu trojúhelníků
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 5);

Realizace vykreslení trsu trojúhelníků

Trs trojúhelníků (triangle fan) se může v 3D modelech uplatnit v případě, že je zapotřebí „zalátat díry“ v plášti, který má složitou topologii (póly na kouli, držátko pokličky výše zobrazené čajové konvičky atd.). V poli vrcholů se nejdříve uvádí souřadnice vrcholu, který je společný všem trojúhelníkům v trsu. Další vrcholy se poté mohou zapisovat v různém pořadí (platí, že třetí až předposlední vrchol je vždy sdílený dvěma trojúhelníky). Pokud nebude druhý vrchol zopakován na konci celého seznamu, nebude trs úplný. Rozdíly budou ukázány na dvojici demonstračních příkladů uvedených i s výslednými obrázky v následujících kapitolách.

Čtvrtý demonstrační příklad: vykreslení trsu trojúhelníků: neúplná varianta

Pokusme se vykreslit šestiúhelník s nabídkou barevné palety. Uprostřed šestiúhelníku bude bílá barva a jeho jednotlivé vrcholy budou postupně vybarveny červenou, žlutou, zelenou, zelenomodrou, modrou a fialovou barvou. Nejprve si necháme vypočítat souřadnice jednotlivých vrcholů:

// souřadnice vrcholů trojúhelníků
const vertexes = [
   // x   y  
    0.0,  0.0,
    Math.cos(0*Math.PI/3), Math.sin(0*Math.PI/3),
    Math.cos(1*Math.PI/3), Math.sin(1*Math.PI/3),
    Math.cos(2*Math.PI/3), Math.sin(2*Math.PI/3),
    Math.cos(3*Math.PI/3), Math.sin(3*Math.PI/3),
    Math.cos(4*Math.PI/3), Math.sin(4*Math.PI/3),
    Math.cos(5*Math.PI/3), Math.sin(5*Math.PI/3),
];

Pole s barvami je vyplněno explicitně:

// barvy vrcholů trojúhelníku
const colors = [
 // R  G  B  A
    1, 1, 1, 1,   // bílá
    1, 0, 0, 1,   // červená
    1, 1, 0, 1,   // žlutá
    0, 1, 0, 1,   // zelená
    0, 1, 1, 1,   // zelenomodrá
    0, 0, 1, 1,   // modrá
    1, 0, 1, 1,   // fialová
];

Pokud se pokusíme tato dvě pole převést na bufferu a předat do vykreslovací pipeliny, získáme po spuštění příkazu:

// vykreslení trsu trojúhelníků
gl.drawArrays(gl.TRIANGLE_FAN, 0, 7);

…tento obrázek:

Trs složený z pěti trojúhelníků.

Obrázek 9: Trs složený z pěti trojúhelníků.

Autor: tisnik, podle licence: Rights Managed

Poznámka: pravděpodobně je v tuto chvíli již zřejmé, proč jsme nezískali celý šestiúhelník, ale pouze obrazec tvořený pěti trojúhelníky – nezopakovali jsme totiž souřadnice druhého vrcholu (nikoli prvního – ten je společný všem trojúhelníkům).

Následuje výpis celého zdrojového kódu příkladu, který předchozí obrázek vykreslil:

const vertexShaderSource = `
    attribute vec2 position;
    attribute vec4 aColor;
    varying vec4 vColor;
 
    void main() {
        vColor = aColor;
        gl_Position = vec4(position, 0, 1);
    }
`
 
const fragmentShaderSource = `
    precision mediump float;
    varying vec4 vColor;
 
    void main() {
        gl_FragColor = vColor;
    }
`
 
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");
 
// souřadnice vrcholů trojúhelníku
const vertexes = [
   // x   y  
    0.0,  0.0,
    Math.cos(0*Math.PI/3), Math.sin(0*Math.PI/3),
    Math.cos(1*Math.PI/3), Math.sin(1*Math.PI/3),
    Math.cos(2*Math.PI/3), Math.sin(2*Math.PI/3),
    Math.cos(3*Math.PI/3), Math.sin(3*Math.PI/3),
    Math.cos(4*Math.PI/3), Math.sin(4*Math.PI/3),
    Math.cos(5*Math.PI/3), Math.sin(5*Math.PI/3),
];
 
// barvy vrcholů trojúhelníku
const colors = [
 // R  G  B  A
    1, 1, 1, 1,   // bílá
    1, 0, 0, 1,   // červená
    1, 1, 0, 1,   // žlutá
    0, 1, 0, 1,   // zelená
    0, 1, 1, 1,   // zelenomodrá
    0, 0, 1, 1,   // modrá
    1, 0, 1, 1,   // fialová
];
 
// 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")
 
 
// 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
                       2*Float32Array.BYTES_PER_ELEMENT, // velikost vertexu v bajtech
                       0                                 // offset
);
gl.enableVertexAttribArray(p);
 
const colorBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer);
 
const cdata = new Float32Array(colors);
gl.bufferData(gl.ARRAY_BUFFER, cdata, gl.STATIC_DRAW);
 
const aColor = gl.getAttribLocation(program, "aColor");
gl.enableVertexAttribArray(aColor);
gl.vertexAttribPointer(aColor,
                       4,                                // 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 v bajtech
                       0                                 // offset
);
 
// 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í trsu trojúhelníků
gl.drawArrays(gl.TRIANGLE_FAN, 0, 7);

Pátý demonstrační příklad: vykreslení trsu trojúhelníků: úplná varianta

Nyní je pravděpodobně zřejmé, jak se musí postupovat, abychom získali plný šestiúhelník. Na konec seznamu vrcholů přidáme druhý vrchol (zapsaný ovšem tak, aby byla zachována posloupnost výpočtů):

// souřadnice vrcholů trojúhelníků
const vertexes = [
   // x   y  
    0.0,  0.0,
    Math.cos(0*Math.PI/3), Math.sin(0*Math.PI/3),
    Math.cos(1*Math.PI/3), Math.sin(1*Math.PI/3),
    Math.cos(2*Math.PI/3), Math.sin(2*Math.PI/3),
    Math.cos(3*Math.PI/3), Math.sin(3*Math.PI/3),
    Math.cos(4*Math.PI/3), Math.sin(4*Math.PI/3),
    Math.cos(5*Math.PI/3), Math.sin(5*Math.PI/3),
    Math.cos(6*Math.PI/3), Math.sin(6*Math.PI/3),
];
Poznámka: povšimněte si, že souřadnice posledního vrcholu budou totožné se souřadnicemi vrcholu druhého. Je tomu tak z toho důvodu, že 6*Math.PI/3 se zkrátí na 2*Math.PI a uplatní se tak periodicita funkcí sinu a kosinu.

Kvůli tomu, že druhý a poslední vrchol jsou totožné, je nutné upravit i barvy. Konkrétně mají oba zmíněné vrcholy totožnou (červenou) barvu:

// barvy vrcholů trojúhelníku
const colors = [
 // R  G  B  A
    1, 1, 1, 1,   // bílá
    1, 0, 0, 1,   // červená
    1, 1, 0, 1,   // žlutá
    0, 1, 0, 1,   // zelená
    0, 1, 1, 1,   // zelenomodrá
    0, 0, 1, 1,   // modrá
    1, 0, 1, 1,   // fialová
    1, 0, 0, 1,   // červená
];

Po takto provedených úpravách bude výsledek po spuštění demonstračního příkladu vypadat takto:

Trs složený ze šesti trojúhelníků.

Obrázek 10: Trs složený ze šesti trojúhelníků.

Autor: tisnik, podle licence: Rights Managed

A jak je již dobrým zvykem, uvedeme si i úplný zdrojový kód takto upraveného demonstračního příkladu:

const vertexShaderSource = `
    attribute vec2 position;
    attribute vec4 aColor;
    varying vec4 vColor;
 
    void main() {
        vColor = aColor;
        gl_Position = vec4(position, 0, 1);
    }
`
 
const fragmentShaderSource = `
    precision mediump float;
    varying vec4 vColor;
 
    void main() {
        gl_FragColor = vColor;
    }
`
 
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");
 
// souřadnice vrcholů trojúhelníku
const vertexes = [
   // x   y  
    0.0,  0.0,
    Math.cos(0*Math.PI/3), Math.sin(0*Math.PI/3),
    Math.cos(1*Math.PI/3), Math.sin(1*Math.PI/3),
    Math.cos(2*Math.PI/3), Math.sin(2*Math.PI/3),
    Math.cos(3*Math.PI/3), Math.sin(3*Math.PI/3),
    Math.cos(4*Math.PI/3), Math.sin(4*Math.PI/3),
    Math.cos(5*Math.PI/3), Math.sin(5*Math.PI/3),
    Math.cos(6*Math.PI/3), Math.sin(6*Math.PI/3),
];
 
// barvy vrcholů trojúhelníku
const colors = [
 // R  G  B  A
    1, 1, 1, 1,   // bílá
    1, 0, 0, 1,   // červená
    1, 1, 0, 1,   // žlutá
    0, 1, 0, 1,   // zelená
    0, 1, 1, 1,   // zelenomodrá
    0, 0, 1, 1,   // modrá
    1, 0, 1, 1,   // fialová
    1, 0, 0, 1,   // červená
];
 
// 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")
 
 
// 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
                       2*Float32Array.BYTES_PER_ELEMENT, // velikost vertexu v bajtech
                       0                                 // offset
);
gl.enableVertexAttribArray(p);
 
const colorBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer);
 
const cdata = new Float32Array(colors);
gl.bufferData(gl.ARRAY_BUFFER, cdata, gl.STATIC_DRAW);
 
const aColor = gl.getAttribLocation(program, "aColor");
gl.enableVertexAttribArray(aColor);
gl.vertexAttribPointer(aColor,
                       4,                                // 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 v bajtech
                       0                                 // offset
);
 
// 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í trsu trojúhelníků
gl.drawArrays(gl.TRIANGLE_FAN, 0, 8);

Základy vykreslování 3D scén

V dalším textu se již budeme zabývat vykreslováním 3D scén. Základ již máme připravený, protože dokážeme vykreslovat trojúhelníky i větší celky složené z trojúhelníků. To je ovšem skutečně jen základ, protože budeme muset vyřešit další problémy, zejména pak:

  1. Nastavení pozorovatele (kamery) do scény
  2. Vykreslení skutečně viditelných ploch, nikoli těch částí scény, které jsou zakryty či leží zcela mimo zorné pole pozorovatele
  3. Nastavení osvětlení
  4. Výpočet osvětlení ploch (osvětlovací model)
  5. Namapování textur na trojúhelníky tvořící povrch zobrazovaných těles
  6. Výpočet mlhy a podobných efektů
  7. Získání efektu pohybu v případě, že se zobrazují animace (motion blur atd.)

Paměť hloubky

Jak v klasické variantě OpenGL, tak i v OpenGL ES se způsob vykreslování překrývajících se ploch na obrazovce většinou řídí paměti hloubky (depth buffer), která je někdy nazývána Z-buffer (což sice nemusí být příliš přesné, ovšem tento termín je mezi programátory zažit). Jedná se o paměť, ve které jsou většinou uloženy informace zajišťující vykreslení pouze viditelných částí těles, což konkrétně znamená, že vzdálenější plošky (přesněji fragmenty) jsou překryty ploškami (fragmenty) bližšími. Funkci a význam paměti hloubky lze však při vykreslování průběžně měnit, takže je možné naprogramovat některé zajímavé efekty, například zobrazení stínů.

Poznámka: tím, že se rozhodování, jaká část tělesa se má vykreslit, provádí na úrovni jednotlivých fragmentů, je vykreslování korektní například i tehdy, pokud se tři plochy překrývají takovým způsobem, který není řešitelný malířovým algoritmem a podobnými postupy pracujícími s celými trojúhelníky.

V naprosté většině případů je funkce paměti hloubky nastavena tak, že se při rasterizaci testuje Z-ová hloubka (vzdálenost od stínítka obrazovky, ve skutečnosti se většinou testuje převrácená hodnota Z-ové hloubky, ale to je již interní věcí GPU) vytvořeného fragmentu s hloubkou uloženou ve framebufferu na pozici vkládaného fragmentu. V případě, že je Z-ová hloubka fragmentu menší než Z-ová hodnota uložená na příslušném místě ve framebufferu, fragment se do framebufferu uloží a samozřejmě přepíše i Z-ovou pozici. Pokud je Z-ová hloubka fragmentu naopak větší, je jasné, že je fragment ve výsledné scéně neviditelný a proto je zahozen (není zapsán ani do barvového bufferu ani do paměti hloubky).

U paměti hloubky je velmi důležitá hodnota počtu bitů pro uložení hloubky. Pokud je například depth buffer pouze osmibitový (tak ho měly implementovány první grafické akcelerátory na PC), je možné rozlišit pouze 256 vzdáleností, což může vést k vytvoření vizuálních artefaktů ve scéně. V dnešní době se používají 16ti bitové, 24bitové hloubky i hodnoty reprezentované s plovoucí řádovou čárkou (navíc se nemusí ukládat přímo Z-ová souřadnice, ale její převrácená hodnota). V takovém případě se množství nežádoucích artefaktů snižuje (avšak nastat mohou, typicky pokud vytvoříme dva trojúhelníky, které se částečně překrývají).

Postup při použití paměti hloubky pro vykreslování překrývajících se ploch

Aby bylo možné využít paměť hloubky pro korektní vykreslování těch ploch, které jsou (i když někdy jen částečně) zakryty jinými plochami, je nutné provést několik operací:

  1. Vyžádat si paměť hloubky (depth buffer) při získávání kontextu WebGL
  2. Vertex shader musí zpracovávat 3D souřadnice
  3. Vymazat paměť hloubky zápisem zvolené hloubky do všech prvků
  4. Nastavit operaci prováděnou při zjišťování, zda je zpracovávaný fragment blíže k pozorovateli, než již zapsaný fragment
  5. A samozřejmě je nutné předávat 3D souřadnice vrcholů, nejenom 2D souřadnice

Aby bylo možné paměť hloubky povolit, je nutné si nejdříve vyžádat, aby vůbec byla v paměti dostupné GPU alokována. To se ve WebGL provádí následujícím způsobem:

// vytvoření a získání kontextu WebGL (3D grafika)
const gl = canvas.getContext("webgl", {
  depth: true
});
if (!gl) {
    throw new Error("WebGL is not supported or context creation failed");
}
print("WebGL context created");

Následně se upraví vertex shader. Ten bude akceptovat atribut position, který ale už nebude typu vec2 (dvourozměrný vektor), ale vec3 (vektor se třemi prvky). A to vede k nutnosti úpravy řádku, ve kterém se nastavuje stavová proměnná gl_Position:

attribute vec3 position;
attribute vec4 aColor;
varying vec4 vColor;
 
void main() {
    vColor = aColor;
    gl_Position = vec4(position, 1);
}

V dalším kroku se bude mazat obsah paměti hloubky. Provádí se to podobným způsobem, jako mazání barvového bufferu. Pro připomenutí:

// 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);

Smazání paměti hloubky se provede totožným příkazem gl.clear, ovšem nejdříve musíme nastavit hodnotu, která se do paměti hloubky zapíše:

// hodnota zapisovaná do paměti hloubky
gl.clearDepth(1.0);
 
// vymazání barvového bufferu i paměti hloubky
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);

Dále je nutné paměť hloubky povolit a nastavit podmínku, která se provádí s každým fragmentem v grafické pipelině. Konkrétně budeme požadovat, aby se zapsaly (zobrazily) ty fragmenty, které jsou blíže k pozorovateli:

// povolení Z-bufferu
gl.enable(gl.DEPTH_TEST);
 
// podmínka prováděná s fragmenty
gl.depthFunc(gl.LESS);

Demonstrační příklad: vykreslení trojúhelníků, které se překrývají, bez použití paměti hloubky

Nejprve si ukažme, jakým způsobem se vykreslí scéna se třemi trojúhelníky definovanými v ploše, které se částečně překrývají. Souřadnice těchto trojúhelníků jsou skutečně jen dvourozměrné a vertex shader doplňuje za souřadnice z a w výchozí hodnoty:

// souřadnice vrcholů trojúhelníku
const vertexes = [
   // x   y  
   -0.8, -0.5,
    0.2, -0.5,
   -0.3,  0.4,
 
   -0.2, -0.5,
    0.8, -0.5,
    0.3,  0.4,
 
   -0.5,  0.0,
    0.5,  0.0,
    0.0,  0.9,
];

Paměť hloubky je zakázána (resp. přesněji řečeno není povolena) a trojúhelníky se vykreslí běžným způsobem příkazem:

// vykreslení trojúhelníků
gl.drawArrays(gl.TRIANGLES, 0, 9);

V tomto případě se trojúhelníky vykreslují (rasterizují) v takovém pořadí, v jakém jsou uloženy do bufferu, jenž je posílán do GPU. Druhý trojúhelník tedy částečně překreslí trojúhelník první a třetí trojúhelník částečně překreslí první dva trojúhelníky:

Překrývající se trojúhelníky.

Obrázek 11: Překrývající se trojúhelníky.

Autor: tisnik, podle licence: Rights Managed

Úplný zdrojový kód tohoto demonstračního příkladu vypadá následovně:

const vertexShaderSource = `
    attribute vec2 position;
    attribute vec4 aColor;
    varying vec4 vColor;
 
    void main() {
        vColor = aColor;
        gl_Position = vec4(position, 0, 1);
    }
`
 
const fragmentShaderSource = `
    precision mediump float;
    varying vec4 vColor;
 
    void main() {
        gl_FragColor = vColor;
    }
`
 
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");
 
// souřadnice vrcholů trojúhelníku
const vertexes = [
   // x   y  
   -0.8, -0.5,
    0.2, -0.5,
   -0.3,  0.4,
 
   -0.2, -0.5,
    0.8, -0.5,
    0.3,  0.4,
 
   -0.5,  0.0,
    0.5,  0.0,
    0.0,  0.9,
];
 
// barvy vrcholů trojúhelníku
const colors = [
 // R  G  B  A
    1, 0, 0, 1,
    1, 0, 0, 1,
    1, 0, 0, 1,
    0, 1, 0, 1,
    0, 1, 0, 1,
    0, 1, 0, 1,
    0, 0, 1, 1,
    0, 0, 1, 1,
    0, 0, 1, 1,
];
 
// 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")
 
 
// 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
                       2*Float32Array.BYTES_PER_ELEMENT, // velikost vertexu v bajtech
                       0                                 // offset
);
gl.enableVertexAttribArray(p);
 
const colorBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer);
 
const cdata = new Float32Array(colors);
gl.bufferData(gl.ARRAY_BUFFER, cdata, gl.STATIC_DRAW);
 
const aColor = gl.getAttribLocation(program, "aColor");
gl.enableVertexAttribArray(aColor);
gl.vertexAttribPointer(aColor,
                       4,                                // 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 v bajtech
                       0                                 // offset
);
 
// 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, 9);

Vykreslení překrývajících se trojúhelníků s využitím paměti hloubky

V dnešním posledním demonstračním příkladu se opět vykreslí trojice trojúhelníků, které se budou na obrazovce překrývat. Ovšem nyní je u trojúhelníků specifikována i vzdálenost vrcholů od projekční roviny (třetí souřadnice):

// souřadnice vrcholů trojúhelníku
const vertexes = [
   // x   y  
   -0.8, -0.5, -0.5,
    0.2, -0.5, -0.5,
   -0.3,  0.4, -0.5,
 
   -0.2, -0.5, 0.0,
    0.8, -0.5, 0.0,
    0.3,  0.4, 0.0,
 
   -0.5,  0.0, 0.5,
    0.5,  0.0, 0.5,
    0.0,  0.9, 0.5,
];

První trojúhelník (červený) je nejblíže pozorovateli a z tohoto důvodu překrývá ostatní dva trojúhelníky. A to přesto, že je vykreslen jako první. V grafické pipeline se tedy při vykreslování zeleného a modrého trojúhelníku některé fragmenty vůbec neuloží do barvového bufferu a ani do paměti hloubky:

Trojúhelníky, které mají odlišnou vzdálenost od pozorovatele.

Obrázek 12: Trojúhelníky, které mají odlišnou vzdálenost od pozorovatele.

Autor: tisnik, podle licence: Rights Managed

Úplný zdrojový kód dnešního posledního demonstračního příkladu vypadá následovně:

Wazuh školení

const vertexShaderSource = `
    attribute vec3 position;
    attribute vec4 aColor;
    varying vec4 vColor;
 
    void main() {
        vColor = aColor;
        gl_Position = vec4(position, 1);
    }
`
 
const fragmentShaderSource = `
    precision mediump float;
    varying vec4 vColor;
 
    void main() {
        gl_FragColor = vColor;
    }
`
 
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", {
  depth: true
});
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");
 
// souřadnice vrcholů trojúhelníku
const vertexes = [
   // x   y  
   -0.8, -0.5, -0.5,
    0.2, -0.5, -0.5,
   -0.3,  0.4, -0.5,
 
   -0.2, -0.5, 0.0,
    0.8, -0.5, 0.0,
    0.3,  0.4, 0.0,
 
   -0.5,  0.0, 0.5,
    0.5,  0.0, 0.5,
    0.0,  0.9, 0.5,
];
 
// barvy vrcholů trojúhelníku
const colors = [
 // R  G  B  A
    1, 0, 0, 1,
    1, 0, 0, 1,
    1, 0, 0, 1,
    0, 1, 0, 1,
    0, 1, 0, 1,
    0, 1, 0, 1,
    0, 0, 1, 1,
    0, 0, 1, 1,
    0, 0, 1, 1,
];
 
// 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")
 
 
// seznam předávaný do vertex shaderu
gl.vertexAttribPointer(p,
                       3,                                // počet prvků v každém atributu
                       gl.FLOAT,                         // typ prvků
                       gl.FALSE,                         // data nejsou normalizována
                       3*Float32Array.BYTES_PER_ELEMENT, // velikost vertexu v bajtech
                       0                                 // offset
);
gl.enableVertexAttribArray(p);
 
const colorBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer);
 
const cdata = new Float32Array(colors);
gl.bufferData(gl.ARRAY_BUFFER, cdata, gl.STATIC_DRAW);
 
const aColor = gl.getAttribLocation(program, "aColor");
gl.enableVertexAttribArray(aColor);
gl.vertexAttribPointer(aColor,
                       4,                                // 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 v bajtech
                       0                                 // offset
);
 
// 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);
 
// povolení Z-bufferu
gl.enable(gl.DEPTH_TEST);
gl.depthFunc(gl.LESS);
 
gl.clearDepth(1.0);
 
// vymazání barvového bufferu
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
 
// spuštění programu
gl.useProgram(program);
 
// vykreslení trojúhelníku
gl.drawArrays(gl.TRIANGLES, 0, 9);

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

Všechny demonstrační příklady, které jsme si v předminulém, minulém 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

Seriály a články o OpenGL i o přidružených technologiích

OpenGL ES a shadery jsou sice nové téma, ovšem na stránkách Roota jsme se již několikrát setkali s podobnými technologiemi. Především byla (a nutno přiznat, že to bylo hodně dávno) popsána původní knihovna OpenGL (pro pamětníky: tehdy testovaná přímo na pracovní stanici SGI), dále OpenGL evaluátory, OpenGL IS (Imaging Subset) a taktéž knihovna GLUT a OpenVG. Odkazy na tyto seriály a články jsou uvedeny pod odstavcem. Pro úplnost byly přidány i předchozí dva články o WebGL a OpenGL ES:

  1. Seriál Grafická knihovna OpenGL
    https://www.root.cz/serialy/graficka-knihovna-opengl/
  2. Seriál OpenGL a nadstavbová knihovna GLU
    https://www.root.cz/serialy/opengl-a-nadstavbova-knihovna-glu/
  3. Seriál OpenGL evaluátory
    https://www.root.cz/serialy/opengl-evaluatory/
  4. Seriál OpenGL Imaging Subset
    https://www.root.cz/serialy/opengl-imaging-subset/
  5. Seriál Tvorba přenositelných grafických aplikací využívajících knihovnu GLUT
    https://www.root.cz/serialy/tvorba-prenositelnych-grafickych-aplikaci-vyuzivajicich-knihovnu-glut/
  6. Stručný přehled grafických knihoven pro Raspberry Pi
    https://www.root.cz/clanky/strucny-prehled-grafickych-knihoven-pro-raspberry-pi/
  7. Práce s rastrovými obrázky v knihovně OpenVG
    https://www.root.cz/clanky/prace-s-rastrovymi-obrazky-v-knihovne-openvg/
  8. Práce s rastrovými obrázky v knihovně OpenVG (pokračování)
    https://www.root.cz/clanky/prace-s-nbsp-rastrovymi-obrazky-v-nbsp-knihovne-openvg-pokracovani/
  9. Práce s rastrovými obrázky v knihovně OpenVG (okomentované demonstrační příklady)
    https://www.root.cz/clanky/prace-s-rastrovymi-obrazky-v-knihovne-openvg-okomentovane-demonstracni-priklady/
  10. Použití knihoven OpenVG a EGL (nejenom) na Raspberry Pi
    https://www.root.cz/clanky/pouziti-knihoven-openvg-a-egl-nejenom-na-raspberry-pi/
  11. WebGL: tvorba 3D grafiky s využitím programovatelné vykreslovací pipeline
    https://www.root.cz/clanky/webgl-tvorba-3d-grafiky-s-vyuzitim-programovatelne-vykreslovaci-pipeline/
  12. WebGL: tvorba 3D grafiky s využitím programovatelné vykreslovací pipeline (2. část)
    https://www.root.cz/clanky/webgl-tvorba-3d-grafiky-s-vyuzitim-programovatelne-vykreslovaci-pipeline-2-cast/

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

Autor článku

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