💊 Pilulky WebGL:Úvod

Foto Andy Holmes on Unsplash · Sandwich od Johna Vestevicha z Noun Project

(Tento příspěvek se poprvé objevil na mém blogu)

Toto je první ze série článků, které prozkoumají kouzelný svět kreslení v prohlížeči . Cílem je publikovat řadu praktických mikro tutoriálů - ilustrovaných a v jednoduché angličtině - aby bylo WebGL jasné a přístupné a umožnilo komukoli začít vytvářet zázraky, jako je toto, nebo toto, nebo toto, nebo toto.

Co mě žene k napsání této série, je to, že když sám přistupuji k WebGL, příliš často ztrácím orientaci v moři technických termínů a cizích konceptů (co je to vlastně „shader“?). Trávím hodiny nad oficiálním i neoficiálním vzdělávacím materiálem, dokud mi to v jednu chvíli neklapne. Ale mohlo to klapnout mnohem dříve a mnohem snadněji, kdyby byly pojmy vysvětleny v základnějších pojmech. (Mimochodem, shader není nic jiného než materiál. S trochou magie navíc. Časem uvidíme více.)

Můj první příspěvek nebude ve skutečnosti pilulka ani mikro, ale slibuji, že každý další příspěvek bude zveřejněn ve snadno stravitelné formě. Chci vám nabídnout něco, co vám může poskytnout základy pro pochopení nového konceptu nebo nástroje během několika minut. Ale jak jsem řekl, tento první příspěvek bude trochu delší, aby se vytvořil dostatečně dobrý základ.

Ach, ještě poslední věc, než začneme. Zde je orientační nástin struktury série (jsem si jistý, že se bude postupem času měnit a přizpůsobovat, ale měl by vám poskytnout představu o tom, co můžete očekávat):

  1. Úvod, co je WebGL, jaké jsou jeho možnosti, "Hello Cube" 👆 jsme tady
  2. Co je to „scéna“? Pojďme si jeden postavit.
  3. Co je to „shader“? Pojďme si jeden vyrobit.
  4. Pojďme vytvořit nějaké objekty pomocí kódu!
  5. Pojďme vytvořit nějaké objekty pomocí externího programu a importovat je!
  6. Pojďme si hrát se světly
  7. Pojďme si hrát s materiály
  8. Jak mohu komunikovat se svou scénou? Myš a klávesnice
  9. Zvuk
  10. React a three.js (react-three-fiber)
  11. Pokročilé:pojďme vytvořit hru v prohlížeči
  12. Pokročilé:pojďme vytvořit vizualizér hudby
  13. Pokročilé:pojďme vytvořit webovou stránku, která žije ve 3D prostoru
  14. Pokročilé:fyzika a srážky

Poznámka:Jedna „kapitola“ může být rozdělena do několika pilulek.

Toto je trochu dlouhý úvod, ale cítil jsem, že je důležité poskytnout vám kontext, ve kterém budete tento článek číst. A teď je čas pustit se do práce a promluvit si o tom, proč tu jste:WebGL.

WebGL (není 3D API)

Tohle jste nečekali, že? I když existují kontroverzní názory na tuto záležitost, pravdou je, že WebGL neposkytuje mnoho z hlediska 3D hned po vybalení. Ve skutečnosti 3D není primárním cílem WebGL, a proto při své každodenní práci pravděpodobně budete chtít využívat knihovny jako OGL, three.js nebo Babylon. Budeme se jim věnovat později v tomto článku, ale vraťme se na chvíli k WebGL. Pokud nám neposkytuje 3D nástroje, co to vlastně dělá?

WebGL kreslí body, čáry a trojúhelníky v <canvas> prvky využívající GPU. A je to. To je ten tweet. Je to tak jednoduché. Ok, ve skutečnosti to není tak jednoduché, a pokud hledáte králičí díru, neváhejte hledat „GPU vs CPU“ a jaké jsou výhody a nevýhody využití GPU ke spouštění programů.

Ale pokud existuje jedna informace, kterou bychom si z celého tohoto článku měli ponechat, je to, že WebGL je nízká úroveň knihovny a pravděpodobně nemáte zájem se ji do hloubky právě naučit .

Svět možností

Jak jste možná viděli, pokud jste sledovali odkazy na začátku článku (pokud ne, doporučuji to udělat hned, budu tu čekat) Zdá se, že WebGL otevírá celý svět možností . Pokud jste jako já, budete se téměř cítit ohromeni naprostou rozmanitostí věcí, které můžete s WebGL dělat. Naučit se to všechno musí být jistě ohromné ​​úsilí, že? A určitě musíte věnovat hodiny a hodiny výzkumu a vývoji den za dnem po měsíce nebo dokonce roky, než dokážete postavit něco krásného, ​​že?

Špatně.

Vykreslení růžové rotující kostky na vámi zvolené webové stránce trvá 5 minut. 2, pokud to uděláte potřetí. Zní to teď zajímavěji?

Ale vážně, tohle je pro mě WebGL:možnosti (všimněte si množného čísla). Můžete si postavit prakticky cokoli, co chcete, 2D nebo 3D, od hudebních přehrávačů po hry v prohlížeči až po efektní efekty vznášení. Obloha je limit a kreativita je vaším přítelem. Během několika příštích týdnů prozkoumáme, jak na to, v sérii jednoduchých a nepřekvapivých kroků. Nebo měsíce. Uvidíme.

3D knihovny

Dobře, takže. WebGL je příliš komplikovaná nízkoúrovňová knihovna, ale animace 3D věcí v prohlížeči má být jednoduchá? Svým způsobem ano, díky řadě knihoven, které nad WebGL poskytují užitečné abstrakce. Tři nejoblíbenější, seřazené podle nejdůležitějších po nejúplnější, jsou:

  1. OGL
  2. tři.js
  3. Babylon.js

V tomto článku vytvoříme růžovou točící se kostku ve všech třech, abyste ochutnali každou. Ale nejprve, jak se porovnávají?

Obecně řečeno OGL se snaží být minimální a abstraktní tak málo, jak je to jen možné, až do bodu, kdy budete často muset psát nativní příkazy WebGL. Poskytuje několik předem připravených tvarů a nástrojů (kostka, koule, mlha, stín...), ale zdaleka ne tolik jako úplnější knihovna, jako je three.js . Je to dobrá volba, pokud neplánujete stavět nic přehnaně složitého a chtěli byste mít perfektní záminku k tomu, abyste se o WebGL naučili trochu víc.

Tři.js je zdaleka nejpoužívanější 3D knihovnou. Někdy má špatnou pověst, protože vývojáři mají tendenci "pohybovat se rychle a rozbíjet věci", takže váš kód může fungovat s dnešním r113 verzi, ale pokud zítra upgradujete na r114, může se něco zlomit . Ano, nepoužívají semver. Vzhledem k jeho všudypřítomnosti a popularitě je však těžké udělat chybu, pokud si ho vyberete (stačí se podívat na stránku s příklady). Ve skutečnosti ve většině budoucnosti 💊 pilulky Budu používat three.js.

Babylon.js je pravděpodobně nejvýkonnější a nejúplnější knihovna této skupiny. I když je méně populární než three.js, je sponzorován (vyvíjen?) společností Microsoft. Má mnoho funkcí, o kterých pravděpodobně ani nevíte, že jsou něco (a já také ne), ale hlavně přichází se sadou nástrojů pro vytváření her. Byla by to knihovna, kterou bych si vybral, kdybych měl postavit něco složitého, nebo hru v prohlížeči.

Ahoj Cube

Uvědomuji si, že jsem utratil hodně slov, která nejprve představí tuto sérii a poté svět WebGL. Snažil jsem se to omezit na minimum a v následujících týdnech se toho určitě dozvíme mnohem víc, ale teď jedna dobrá zpráva:konečně nastal čas pro „Hello world“ WebGL 🙌

Upozornění:cílem tohoto cvičení je něco udělat. Budou tam termíny a pojmy, které zatím nemusí dávat moc smysl. Navrhuji, abyste na chvíli pozastavili svou zvědavost a zkusili to následovat a strčili si rychlou výhru do kapsy (a možná ji ukázali svým přátelům). Budeme mít spoustu času na pochopení všeho ostatního, jak budeme pokračovat v sérii!

Nastavení

Navrhuji, abyste na CodeSandbox vytvořili sandbox pro každou kostku, kterou vytvoříme. Kód, který ukážu, lze vložit do index.js poskytnutý soubor a na pravé straně obrazovky získáte okamžitý náhled. Pro vaše pohodlí můžete jednoduše otevřít tuto šablonu:https://codesandbox.io/s/pills-of-webgl-hello-cube-8tft5 a kliknout na Fork vpravo nahoře.

OGL

Začněme nejobtížnější knihovnou :)

Nejdříve:v našem nově rozvětveném sandbox, klikněte na Add Dependency (najdete jej na postranním panelu), vyhledejte ogl a kliknutím na něj ho přidáte do našeho projektu.

Začněme inicializací Rendereru , která je v konečném důsledku zodpovědná za komunikaci s WebGL a kreslení pixelů na plátno:

import {
  Renderer,
  Camera,
  Program,
  Mesh,
  Box,
  Transform
} from 'ogl/dist/ogl.umd.js';

// Initialize the OGL renderer and attach the canvas to our document
const renderer = new Renderer();
const gl = renderer.gl;

// Append the canvas which will be used by OGL to our document
document.getElementById('app').appendChild(gl.canvas);

Upozornění:normálně by stačilo napsat import { ... } from 'ogl'; , ale kvůli chybě v CodeSandbox musíme zadat, že chceme verze. UMD

Pokud se podíváme na náhled, uvidíme jeden černý obdélník o rozměrech 300x150px. Perfektní. To je výchozí velikost <canvas> a vykreslí se celý černý, protože jsme toho zatím moc neudělali:

Pojďme přidat Fotoaparát . A když už jsme u toho, nastavíme velikost našeho <canvas> aby pokryl celou stránku. Přidejte následující kód do index.js :

...

// Append the canvas which will be used by OGL to our document
document.getElementById('app').appendChild(gl.canvas);

// Add a camera
const camera = new Camera(gl);
camera.position.z = 5; // <- this moves the camera "back" 5 units

// Set the size of the canvas
renderer.setSize(window.innerWidth, window.innerHeight);

// Set the aspect ratio of the camera to the canvas size
camera.perspective({
  aspect: gl.canvas.width / gl.canvas.height
});

Mmm 🤔 bílá zešedla, ale ta černá krabička 300x150px tam stále je. Co dává? To je v pořádku. Máme vykreslovače, který vykresluje na plátně (pokud zaškrtnete nástroje pro vývojáře, uvidíte, že plátno ve skutečnosti pokrývá celé okno) a máme kameru, přes kterou se můžeme dívat. Chybí to, na co by se měl fotoaparát vlastně dívat. Přidáme scénu a řekněte vykreslovači, aby vykreslil scénu přes naši kameru:

...

// Set the aspect ratio of the camera to the canvas size
camera.perspective({
  aspect: gl.canvas.width / gl.canvas.height
});

// Add a scene (don't worry about what Transform actually does for the moment)
const scene = new Transform();

// Draw!
renderer.render({ scene, camera });

Hurá! Celá stránka je konečně černá. Dobrá práce!

Nyní potřebujeme kostku . Zde jsou věci trochu složitější:uvidíte nějaké věci a nebude to dávat moc smysl, a pak uvidíte, že se podobné vzory opakují na tři.js a Babylon.js příklady a pak ve svém dalším článku vysvětlím, oč vlastně jde. Stačí na chvíli důvěřovat následujícímu kódu a přidat jej do svého index.js před losováním návod:

...

// Add a scene (don't worry about what Transform actually does for the moment)
const scene = new Transform();

// Let's use the Box helper from OGL
const geometry = new Box(gl);

// This complicated set of instructions tells our box to be pink. It's called
// "program" for a reason, but it doesn't matter right now.
const program = new Program(gl, {
  vertex: `
            attribute vec3 position;

            uniform mat4 modelViewMatrix;
            uniform mat4 projectionMatrix;

            void main() {
                gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
            }
            `,
  fragment: `
            void main() {
                gl_FragColor = vec4(0.92, 0.48, 0.84, 1.0); // Pink!
            }
        `
});

// Here we say that we want our box (geometry), to be pink (program)
const mesh = new Mesh(gl, { geometry, program });

// And finally we add it to the scene
mesh.setParent(scene);

// Draw!
renderer.render({ scene, camera });

Dostávat se tam? Nyní byste měli vidět růžový čtverec uprostřed našeho plátna. Je to vlastně krychle, ale díváme se na ni rovnou zepředu. Pojďme to protočit, ano?

Přidejte následující řádky před renderer.render({ scene, camera }); a stiskněte Save :

...

// And finally we add it to the scene
mesh.setParent(scene);

// Remember, `mesh` is our pink cube.
// And we can directly mutate some of it's properties!
mesh.rotation.y -= 0.04;
mesh.rotation.x += 0.03;

// One last thing: MOVE the `draw` instruction that we added earlier down here:
renderer.render({ scene, camera });

Dobře, dělal jsem si srandu. To k oživení našeho objektu rozhodně nestačí. Potřebujeme malého pomocníka a náš malý pomocník se jmenuje requestAnimationFrame . Velmi stručně, requestAnimationFrame je rozhraní API prohlížeče, které nám umožňuje spustit funkci těsně předtím, než prohlížeč překreslí okno. Pokud udržíme naši animaci dostatečně jednoduchou, bude překreslování probíhat 60krát za sekundu, což je přibližně jednou za 16 ms. Toto je také známé jako "máslové hladké".

Smažte předchozí dva řádky a řádek renderer.render({... a místo toho přidejte následující:

...

// And finally we add it to the scene
mesh.setParent(scene);

// Update the cube spin every 16ms
requestAnimationFrame(update);
function update() {
  requestAnimationFrame(update);

  mesh.rotation.y -= 0.04;
  mesh.rotation.x += 0.03;
  renderer.render({ scene, camera });
}

//EOF

Dokázali jsme to 🥳
Zde je konečný výsledek:

Pokud váš program nefunguje tak, jak bylo zamýšleno, klikněte na tlačítko „Otevřít karanténu“, abyste viděli komentovaný zdrojový kód a porovnejte jej se svým výsledkem!

Cvičení pro čtenáře: podívejte se, zda mu nemůžete dát různé barvy, rotace a animovat jeho polohu.

three.js

Chápu, že toho začíná být hodně a článek se prodlužuje, ale chtěl jsem sestavit náš první Hello Cube krok za krokem, abych rozebral vše, co je potřeba k animaci věcí v našem prohlížeči. Dobrá zpráva je, že je to tak. Vše, co bude od nynějška následovat, bude v podstatě variací toho, co jsme doposud viděli.

Pojďme získat naše tři.js Příklad běhu a místo toho uvidíte, jak věci dělají. Tentokrát přeskočím některé kroky a budeme hotovi, než se nadějete, slibuji.

Rozdělme naši šablonu https://codesandbox.io/s/pills-of-webgl-hello-cube-8tft5 (opět) a tentokrát přidejte three závislost. Dále připravíme naši scénu. Přidejte následující do našeho index.js :

import * as THREE from 'three';

// Create our renderer and append the canvas to our document
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.getElementById('app').appendChild(renderer.domElement);

// Add a camera, and move it back 5 units
const FOV = 45; // This corresponds approximately to a 30mm lens
const ASPECT = window.innerWidth / window.innerHeight;
const NEAR = 0.1; // Anything closer than 0.1 units will not be visible
const FAR = 1000; // Anything further than 0.1 units will not be visible
const camera = new THREE.PerspectiveCamera(FOV, ASPECT, NEAR, FAR);
camera.position.z = 5;

// Make a scene (lol)
const scene = new THREE.Scene();

// Draw!
renderer.render(scene, camera);

Zatím nic nového, jsme ve fázi „all black“. Rozhraní API poskytovaná three.js jsou trochu jiné, ale stále je to převážně angličtina a snadno najdeme mnoho podobností s OGL . Pokračujme s naší kostkou :

...

// Make a scene (lol)
const scene = new THREE.Scene();

// Our helper from three.js
const geometry = new THREE.BoxGeometry();

// In OGL, this was called `program`. It's the same thing, just easier.
const material = new THREE.MeshBasicMaterial({ 
  color: 0xea7ad7 // Pink!
});

// Putting everything together
const cube = new THREE.Mesh(geometry, material);

// And finally adding the cube to the scene
scene.add(cube);

// Draw!
renderer.render(scene, camera);

Pamatujte na spoustu matoucích řádků s názvem program ? Program je shader je materiál. Tři.js nazývá to materiál a poskytuje nám spoustu užitečných předvoleb, se kterými můžeme začít, jako je MeshBasicMaterial . Pojďme nyní animovat kostku:

...

// And finally adding the cube to the scene
scene.add(cube);

// Update the cube spin every 16ms
requestAnimationFrame(update);
function update() {
  requestAnimationFrame(update);

  cube.rotation.y -= 0.04;
  cube.rotation.x += 0.03;
  renderer.render(scene, camera);
}

//EOF

Tadááá!

Vše hotovo. Ale víte co? Pojďme ještě o malý krok dále. Ten plochý vzhled se mi moc nelíbí, takhle kostky nevypadají, že? Hledejte řádek:

const material = new THREE.MeshBasicMaterial({

...a změňte to na:

const material = new THREE.MeshLambertMaterial({

Vidíš teď celý černě? Dobrý. Nastavili jsme naši kostku tak, aby používala fyzikálně založený materiál. To znamená, že nyní musíme přidat... Světlo !

...

// And finally adding the cube to the scene
scene.add(cube);

// White directional light (by default it looks at the center of the scene)
const directionalLight = new THREE.DirectionalLight(0xffffff, 1);

// Position it to the top left
directionalLight.position.set(-1, 1, 1);

// Add it to the scene
scene.add(directionalLight);

// Update the cube spin every 16ms
requestAnimationFrame(update);
function update() {
  requestAnimationFrame(update);

  cube.rotation.y -= 0.04;
  cube.rotation.x += 0.03;
  renderer.render(scene, camera);
}

//EOF

Není to mnohem lepší? A s méně řádky kódu než v OGL příklad.

To je síla tři.js :máme sadu nástrojů, díky kterým je nastavení scény hračkou. Samozřejmě, pokud bychom chtěli, mohli jsme se vždy odhlásit z pomocníků a použít vlastní program/shader na naši kostku. Tak se dělají jedny z nejlepších věcí. Ale je to volitelné a v tuto chvíli máme víc, než potřebujeme, abychom mohli začít.

Cvičení pro čtenáře: three.js poskytuje kompletní sadu základních tvarů, zkuste se podívat, co ještě můžete točit.

Nakonec se podívejme na Babylon.js příklad.

Babylon.js

Jako obvykle rozvětvte naši šablonu https://codesandbox.io/s/pills-of-webgl-hello-cube-8tft5 (ještě jednou) a tentokrát přidejte @babylonjs/core závislost (pozor, existuje balíček nazvaný jednoduše babylon což je analyzátor, NE 3D knihovna, kterou hledáme). A pojďme připravit naši scénu.

Pokud si pamatujete, v našich předchozích dvou příkladech se knihovny samy postaraly o vytvoření <canvas> prvek, který jsme poté připojili k našemu #app živel. Babylon.js místo toho chce plátno připravené k použití, takže otevřete index.html a přidejte následující řádek:

...

<div id="app">
  <canvas id="renderCanvas" touch-action="none"></canvas>
</div>

...

Vracím se na index.js , přidáme obvyklý renderer , fotoaparát a scéna a nakreslete náš černý obdélník:

import { 
  Engine,
  Scene,
  UniversalCamera,
  MeshBuilder,
  StandardMaterial,
  DirectionalLight,
  Vector3,
  Color3, 
} from '@babylonjs/core';

// Get the canvas element and resize it to cover the full window
const canvas = document.getElementById('renderCanvas'); 
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;

// In the previous examples this was called "renderer"
const engine = new Engine(canvas, true);

// Create the scene
const scene = new Scene(engine);

// Add a camera called "Camera" 🤓, and move it back 5 units
const camera = new UniversalCamera('Camera', new Vector3(0, 0, 5), scene);

// Point the camera towards the scene origin
camera.setTarget(Vector3.Zero());

// And finally attach it to the canvas
camera.attachControl(canvas, true);

// Draw!
scene.render();

Pokud stisknete Save nyní uvidíte, že náhled bude fialový a ne černý. To je v pořádku, jde jen o to, že Babylon.js má to rád méně tmavé než naši ostatní přátelé 🙃. To však neznamená, že naši scénu osvětluje výchozí světlo. Je to jen jakási barva pozadí plátna (ne přesně, ale pro tuto chvíli je to dost dobré vysvětlení).

Přidáme naši kostku a Světlo jde to:

...

// And finally attach it to the canvas
camera.attachControl(canvas, true);

// Create a 1x1 cube (Babylon.js automatically adds it to our scene)
// Note: there is an odler method called simply "Mesh". It is recommended
// to use the newer "MeshBuilder" instead.
const box = MeshBuilder.CreateBox('', {});

// Make it pink
const pink = new StandardMaterial('Pink', scene);
pink.diffuseColor = new Color3(0.92, 0.48, 0.84);
box.material = pink;

// And add a light source. Note that it works slightly differently than in
// three.js. The Vector here is not the light's position, but the direction
// it points to.
const light = new DirectionalLight('DirectionalLight', new Vector3(-1, -1, -1), scene);

// Draw!
scene.render();

Jako obvykle bude naším posledním krokem dát to roztočení! Všimnete si, že tentokrát místo přímého použití requestAnimationFrame rozhraní API prohlížeče, zavoláme několik nástrojů poskytovaných Babylon.js .

Nejprve řekneme rendereru, že před každým průchodem chceme upravit rotaci naší krychle. Dále upravíme náš nákres instrukce k použití vestavěné smyčky motoru:

...

const light = new DirectionalLight('DirectionalLight', new Vector3(-1, -1, -1), scene);

// Our beforeRender function
scene.registerBeforeRender(function() {
  box.rotation.x += 0.03;
  box.rotation.y += 0.04;
});

// Register a render loop to repeatedly render the scene
engine.runRenderLoop(function() {
  scene.render();
});

// EOF

Hurá 🙌

Opět, pokud jste někde uvízli nebo nedostáváte tento výsledek, otevřete sandbox a prohlédněte si komentovaný kód, abyste našli nějaké rozdíly!

Cvičení pro čtenáře: různé materiály reagují na různá světla odlišně, prozkoumejte, co dalšího Babylon.js nabízí.

Závěry

Tak a to je pro tento první díl vše :)

V tomto článku jsme prošli několika základními pojmy, které stačí k tomu, abychom pochopili, co to WebGL je, a začali si špinit ruce. Prozkoumali jsme také řadu nástrojů, které nám usnadňují život při práci s kreslením v prohlížeči. Doufejme, že když uvidíte rozdíly a podobnosti v přístupech těchto knihoven, pomůže vám to definovat vaši mentální mapu kolem WebGL. Například OGL nám ukázal, jak vytvořit materiál (nebo program nebo shader ) psaní pokynů pro WebGL (v další 💊 pilulce prozkoumáme to podrobněji) a pak jsme viděli, jak tři.js a Babylon.js poskytnout své vlastní abstrakce.

Doufám, že se vám to líbilo a doufám, že to vyvolalo zájem a zvědavost na toto téma. Doufám také, že má slova byla přístupná a praktické a užitečné a praktické. Rád bych slyšel vaše komentáře:najdete mě na Twitteru (@mjsarfatti, DM jsou otevřené) a samozřejmě zde!

Pokud byste chtěli být informováni o dalším článku, můžete mě buď sledovat, nebo přejít na můj blog a přihlásit se k odběru mého seznamu e-mailů (nikdy žádný spam, zrušit kdykoli a nikdy více než jeden e-mail za týden – ve skutečnosti pravděpodobně mnohem méně ).

Děkujeme za přečtení a brzy se uvidíme 👋