Shaders bouwen met Babylon.js en WebGL theorie en voorbeelden

In de keynote voor Dag 2 van // Build 2014 (zie 2: 24-2: 28) demonstreerden Microsoft-evangelisten Steven Guggenheimer en John Shewchuk hoe Oculus Rift-ondersteuning werd toegevoegd aan Babylon.js. En een van de belangrijkste dingen voor deze demo was het werk dat we hebben gedaan op een specifieke shader om lenzen te simuleren, zoals je op deze foto kunt zien:

Ik presenteerde ook een sessie met Frank Olivier en Ben Constable over afbeeldingen op IE en Babylon.js. 

Dit leidt me naar een van de vragen die mensen me vaak stellen over Babylon.js: "Wat bedoel je met shaders?"Dus in dit bericht ga ik je uitleggen hoe shaders werken en enkele voorbeelden geven van veelvoorkomende soorten shaders.

De theorie

Voordat we beginnen met experimenteren, moeten we eerst zien hoe dingen intern werken.

Bij hardware-versnelde 3D bespreken we twee CPU's: de hoofd-CPU en de GPU. De GPU is een soort uiterst gespecialiseerde CPU.

De GPU is een toestandsmachine die u instelt met behulp van de CPU. De CPU zal bijvoorbeeld de GPU configureren om lijnen te renderen in plaats van driehoeken. Of het bepaalt dat transparantie aan is, enzovoort.

Nadat alle statussen zijn ingesteld, definieert de CPU wat moet worden weergegeven: de geometrie, die is samengesteld uit een lijst met punten (de hoekpunten en opgeslagen in een array genaamd vertex buffer) en een lijst met indexen (de vlakken of driehoeken worden opgeslagen in een array met de naam index buffer).

De laatste stap voor de CPU is om te definiëren hoe de geometrie moet worden gerenderd en voor deze specifieke taak zal de CPU bepalen shaders voor de GPU. Shaders zijn een stuk code dat de GPU zal uitvoeren voor elk van de hoekpunten en pixels die het moet renderen.

Eerst wat woordenschat: denk aan een vertex (hoekpunten als er meerdere zijn) als een "punt" in een 3D-omgeving (in tegenstelling tot een punt in een 2D-omgeving).

Er zijn twee soorten shaders: vertex shaders en pixel (of fragment) shaders.

Grafische pipeline

Voordat we in de schaduw graven, laten we een stap terug doen. Om pixels te renderen, neemt de GPU de geometrie aan die door de CPU is gedefinieerd en doet hij het volgende:

Met behulp van de indexbuffer worden drie hoekpunten verzameld om een ​​driehoek te definiëren: de indexbuffer bevat een lijst met vertex-indexen. Dit betekent dat elke vermelding in de indexbuffer het nummer is van een hoekpunt in de hoekpuntbuffer. Dit is echt handig om dubbele hoekpunten te vermijden. 

De volgende indexbuffer is bijvoorbeeld een lijst met twee vlakken: [1 2 3 1 3 4]. Het eerste vlak bevat vertex 1, vertex 2 en vertex 3. Het tweede vlak bevat vertex 1, vertex 3 en vertex 4. Er zijn dus vier hoekpunten in deze geometrie: 

De hoekpuntshader wordt toegepast op elke hoek van de driehoek. Het primaire doel van de hoekpuntshader is om een ​​pixel te produceren voor elk hoekpunt (de projectie op het 2D-scherm van de 3D-vertex): 

Met behulp van deze drie pixels (die een 2D-driehoek op het scherm definiëren) interpoleert de GPU alle waarden die aan de pixel zijn bevestigd (ten minste de positie) en wordt de pixel-arcering toegepast op elke pixel in de 2D-driehoek om genereer een kleur voor elke pixel: 

Dit proces wordt uitgevoerd voor elk vlak dat wordt gedefinieerd door de indexbuffer. 

Het is duidelijk dat, vanwege zijn parallelle aard, de GPU in staat is om deze stap voor veel gezichten gelijktijdig te verwerken en daardoor echt goede prestaties te behalen.

GLSL

We hebben zojuist gezien dat voor het renderen van driehoeken, de GPU twee shaders nodig heeft: de vertex-arcering en de pixel-arcering. Deze shaders zijn geschreven met behulp van de taal GLSL (Graphics Library Shader Language). Het lijkt op C.

Voor Internet Explorer 11 hebben we een compiler ontwikkeld om GLSL te transformeren naar HLSL (High Level Shader Language), de shadertaal van DirectX 11. Hiermee kan IE11 ervoor zorgen dat de shadercode veilig is (je wilt niet gebruiken WebGL om uw computer opnieuw in te stellen!):

Hier is een voorbeeld van een gemeenschappelijke vertex-arcering:

precisie highp vlotter; // Attributenattribuut vec3-positie; kenmerk vec2 uv; // Uniformen uniforme mat4 worldViewProjection; // Variërend variërende vec2 vUV; void main (void) gl_Position = worldViewProjection * vec4 (position, 1.0); vUV = uv; 

Vertex Shader-structuur

Een vertex-arcering bevat de volgende:

  • attributen: Een attribuut definieert een deel van een hoekpunt. Standaard moet een hoekpunt op zijn minst een positie bevatten (a vector3: x, y, z). Maar als ontwikkelaar kun je besluiten om meer informatie toe te voegen. In de vorige shader is er bijvoorbeeld een vector2 genaamd uv (textuurcoördinaten waarmee we een 2D-structuur op een 3D-object kunnen toepassen).
  • uniformen: Een uniform is een variabele die door de arcering wordt gebruikt en door de CPU wordt gedefinieerd. Het enige uniform dat we hier hebben, is een matrix die wordt gebruikt om de positie van de top te projecteren (x, y, z) naar het scherm (x, y).
  • Variërend: Variërende variabelen zijn waarden die door de vertex-arcering zijn gemaakt en naar de pixelarray zijn verzonden. Hier verzendt de hoekpuntshader een VUV (een eenvoudige kopie van uv) waarde voor de pixelschaduw. Dit betekent dat hier een pixel wordt gedefinieerd met een positie- en textuurcoördinaten. Deze waarden worden geïnterpoleerd door de GPU en gebruikt door de pixelschaduw. 
  • hoofd: De functie met de naam hoofd() is de code die door de GPU wordt uitgevoerd voor elke vertex en moet op zijn minst een waarde voor produceren gl_position (de positie op het scherm van de huidige vertex). 

We kunnen in onze sample zien dat de vertex-shader vrij eenvoudig is. Het genereert een systeemvariabele (te beginnen met gl_) genaamd gl_position om de positie van de bijbehorende pixel te definiëren en wordt een variabele variabele ingesteld die wordt genoemd VUV

The Voodoo behind Matrices

In onze arcering hebben we een matrix met de naam worldViewProjection. We gebruiken deze matrix om de vertex-positie naar de projectie te projecteren gl_position variabel. Dat is cool, maar hoe krijgen we de waarde van deze matrix? Het is een uniform, dus we moeten het definiëren aan de CPU-kant (met behulp van JavaScript).

Dit is een van de complexe onderdelen van het doen van 3D. Je moet complexe wiskunde begrijpen (of je zult een 3D-engine moeten gebruiken, zoals Babylon.js, die we later zullen zien).

De worldViewProjection matrix is ​​de combinatie van drie verschillende matrices:

Met behulp van de resulterende matrix kunnen we 3D-hoekpunten transformeren in 2D-pixels, rekening houdend met het gezichtspunt en alles met betrekking tot de positie / schaal / rotatie van het huidige object.

Dit is uw verantwoordelijkheid als 3D-ontwikkelaar: om deze matrix up-to-date te maken en te houden.

Terug naar de Shaders

Zodra de vertex-arcering op elk hoekpunt wordt uitgevoerd (driemaal, toen), hebben we drie pixels met een correcte waarde gl_position en een VUV waarde. De GPU interpoleert deze waarden vervolgens op elke pixel in de driehoek die door deze pixels wordt geproduceerd.

Vervolgens voert het voor elke pixel de pixel shader uit:

precisie highp vlotter; variërende vec2 vUV; uniforme sampler2D textureSampler; void main (void) gl_FragColor = texture2D (textureSampler, vUV); 

Pixel (of fragment) Shader-structuur

De structuur van een pixel-shader is vergelijkbaar met een hoekpunt-arcering:

  • Variërend: Variërende variabelen zijn waarden die door de vertex-arcering zijn gemaakt en naar de pixelarray zijn verzonden. Hier ontvangt de pixel-shader een VUV waarde van de vertex-arcering. 
  • uniformen: Een uniform is een variabele die door de arcering wordt gebruikt en door de CPU wordt gedefinieerd. Het enige uniform dat we hier hebben, is een sampler, een tool die wordt gebruikt om textuurkleuren te lezen.
  • hoofd: De functie met de naam hoofd is de code die door de GPU voor elke pixel wordt uitgevoerd en moet op zijn minst een waarde voor produceren gl_FragColor (de kleur van de huidige pixel). 

Deze pixel shader is vrij eenvoudig: hij leest de kleur uit de textuur met textuurcoördinaten van de vertex-arcering (die deze op zijn beurt uit de top heeft gehaald).

Wilt u het resultaat van zo'n shader zien? Hier is het:

Dit wordt in realtime weergegeven; je kunt de bol met je muis slepen.

Om dit resultaat te bereiken, zult u te maken krijgen met een lot van WebGL-code. Inderdaad, WebGL is een echt krachtige maar echt low-level API, en je moet alles zelf doen, van het maken van de buffers tot het definiëren van vertex-structuren. Je moet ook alle wiskunde doen en alle toestanden instellen en de textuur laden en zo verder ...

Te hard? BABYLON.ShaderMateriaal naar de redding

Ik weet wat je denkt: shaders zijn echt gaaf, maar ik wil geen moeite doen met WebGL intern loodgieterswerk of zelfs met wiskunde.

En dat is prima! Dit is een volkomen legitieme vraag, en dat is precies waarom ik Babylon.js heb gemaakt.

Laat me je de code voorstellen die werd gebruikt door de vorige rolling sphere demo. Allereerst heeft u een eenvoudige webpagina nodig:

   Babylon.js          

U zult merken dat de shaders worden gedefinieerd door > -tags. Met Babylon.js kun je ze ook in aparte bestanden definiëren (.fx -bestanden).

Je kunt Babylon.js hier downloaden of op onze GitHub-repo. U moet versie 1.11 of hoger gebruiken om toegang toe te krijgen BABYLON.StandardMaterial.

En tot slot is de belangrijkste JavaScript-code de volgende:

"gebruik strikt"; document.addEventListener ("DOMContentLoaded", startGame, false); function startGame () if (BABYLON.Engine.isSupported ()) var canvas = document.getElementById ("renderCanvas"); var engine = new BABYLON.Engine (canvas, false); var scene = new BABYLON.Scene (engine); var camera = new BABYLON.ArcRotateCamera ("Camera", 0, Math.PI / 2, 10, BABYLON.Vector3.Zero (), scène); camera.attachControl (canvas); // Bol bol var creëren = BABYLON.Mesh.CreateSphere ("Sphere", 16, 5, scène); var amigaMaterial = new BABYLON.ShaderMaterial ("amiga", scène, vertexElement: "vertexShaderCode", fragmentElement: "fragmentShaderCode",, attributes: ["position", "uv"], uniformen: ["worldViewProjection"] ); amigaMaterial.setTexture ("textureSampler", nieuwe BABYLON.Texture ("amiga.jpg", scène)); sphere.material = amigaMaterial; engine.runRenderLoop (function () sphere.rotation.y + = 0.05; scene.render ();); ;

Je kunt zien dat ik a gebruik BABYLON.ShaderMaterial om alle last van het compileren, koppelen en verwerken van shaders kwijt te raken.

Wanneer u een maakt BABYLON.ShaderMaterial, u moet het DOM-element opgeven dat wordt gebruikt om de shaders op te slaan of de basisnaam van de bestanden waar de shaders staan. Als u ervoor kiest om bestanden te gebruiken, moet u een bestand voor elke arcering maken en het volgende bestandsnaampatroon gebruiken: basename.vertex.fx en basename.fragment.fx. Dan zul je het materiaal als volgt moeten maken:

var cloudMaterial = new BABYLON.ShaderMaterial ("cloud", scène, "./myShader", attributes: ["position", "uv"], uniformen: ["worldViewProjection"]);

U moet ook de namen opgeven van alle kenmerken en uniformen die u gebruikt. Vervolgens kunt u rechtstreeks de waarde van uw uniformen en samplers instellen met behulp van de setTexture, setFloat, setFloats, setColor3, setColor4, setVector2, setVector3, setVector4, en setMatrix functies.

Best simpel, toch?

Herinner je je het vorige worldViewProjection Matrix? Babylon.js en gebruiken BABYLON.ShaderMaterial, Er is niets waar je je zorgen over hoeft te maken! De BABYLON.ShaderMaterial zal het automatisch voor u berekenen omdat u het in de lijst met uniformen verklaart.

BABYLON.ShaderMaterial kan ook de volgende matrices voor u verwerken:

  • wereld- 
  • uitzicht 
  • projectie 
  • wereldbeeld 
  • worldViewProjection 

Reken niet langer nodig. Bijvoorbeeld elke keer dat u het uitvoert sphere.rotation.y + = 0.05, de wereldmatrix van de bol wordt voor u gegenereerd en doorgegeven aan de GPU.

CYOS: maak je eigen shader

Dus laten we groter gaan en een pagina maken waar je dynamisch je eigen shaders kunt maken en het resultaat meteen kunt zien. Deze pagina gaat dezelfde code gebruiken die we eerder besproken hebben, en gaat een a gebruiken BABYLON.ShaderMaterial object om shaders te compileren en uit te voeren die je gaat maken.

Ik heb een ACE-code-editor voor CYOS gebruikt. Dit is een ongelooflijke code-editor met syntaxismarkers. Bekijk het hier eens. Je kunt CYOS hier vinden.

Met de eerste keuzelijst met invoervak ​​kunt u vooraf gedefinieerde shaders selecteren. We zullen ze allemaal meteen zien.

U kunt ook het net (het 3D-object) dat wordt gebruikt om uw shaders te bekijken, wijzigen met behulp van de tweede keuzelijst met invoervak.

De Compileren knop wordt gebruikt om een ​​nieuwe te maken BABYLON.ShaderMaterial van je shaders. De code die wordt gebruikt door deze knop is de volgende: 

// Compile shaderMaterial = new BABYLON.ShaderMaterial ("shader", scene, vertexElement: "vertexShaderCode", fragmentElement: "fragmentShaderCode",, attributes: ["position", "normal", "uv"], uniformen: ["world", "worldView", "worldViewProjection"]); var refTexture = new BABYLON.Texture ("ref.jpg", scène); refTexture.wrapU = BABYLON.Texture.CLAMP_ADDRESSMODE; refTexture.wrapV = BABYLON.Texture.CLAMP_ADDRESSMODE; var amigaTexture = new BABYLON.Texture ("amiga.jpg", scène); shaderMaterial.setTexture ("textureSampler", amigaTexture); shaderMaterial.setTexture ("refSampler", refTexture); shaderMaterial.setFloat ("time", 0); shaderMaterial.setVector3 ("cameraPosition", BABYLON.Vector3.Zero ()); shaderMaterial.backFaceCulling = false; mesh.material = shaderMaterial;

Bruut eenvoudig, toch? Het materiaal is klaar om u drie vooraf berekende matrices te sturen (wereld-, wereldbeeld en worldViewProjection). Vertices worden geleverd met positie-, normaal- en textuurcoördinaten. Er zijn ook twee texturen voor je geladen:

amiga.jpgref.jpg

En tot slot, hier is de renderLoop waar ik twee handige uniformen update:

  • één belde tijd om grappige animaties te krijgen 
  • één belde cameraPosition om de positie van de camera in uw shaders te krijgen (wat handig zal zijn voor het verlichten van vergelijkingen) 
engine.runRenderLoop (function () mesh.rotation.y + = 0.001; if (shaderMaterial) shaderMaterial.setFloat ("time", time); time + = 0.02; shaderMaterial.setVector3 ("cameraPosition", camera.position) ; scene.render (););

Dankzij het werk dat we hebben gedaan op Windows Phone 8.1, kun je CYOS ook gebruiken op je Windows Phone (het is altijd een goed moment om een ​​arcering te maken):

Basis Shader

Dus laten we beginnen met de allereerste arcering die is gedefinieerd op CYOS: de eenvoudige arcering.

We kennen deze shader al. Het berekent de gl_position en gebruikt textuurcoördinaten om een ​​kleur voor elke pixel op te halen.

Om de pixelpositie te berekenen, hebben we alleen de worldViewProjection matrix en de positie van de vertex:

precisie highp vlotter; // Attributenattribuut vec3-positie; kenmerk vec2 uv; // Uniformen uniforme mat4 worldViewProjection; // Variërend variërende vec2 vUV; void main (void) gl_Position = worldViewProjection * vec4 (position, 1.0); vUV = uv; 

Textuur coördinaten (uv) worden ongewijzigd verzonden naar de pixelschaduw.

Houd er rekening mee dat we moeten toevoegen precisie mediump float; op de eerste regel voor zowel vertex- als pixelladers omdat Chrome dit vereist. Het definieert dat we voor betere prestaties geen zwevende waarden met volledige precisie gebruiken.

De pixel-shader is nog eenvoudiger, omdat we alleen textuurcoördinaten hoeven te gebruiken en een structuurkleur moeten ophalen:

precisie highp vlotter; variërende vec2 vUV; uniforme sampler2D textureSampler; void main (void) gl_FragColor = texture2D (textureSampler, vUV); 

We hebben eerder gezien dat de textureSampler uniform is gevuld met de "amiga" textuur, dus het resultaat is het volgende:

Zwart en wit Shader

Laten we nu verder gaan met een nieuwe arcering: de zwart-witte arcering.

Het doel van deze arcering is om de vorige te gebruiken, maar met een weergavemodus voor 'alleen zwart-wit'. Om dit te doen, kunnen we dezelfde vertex-arcering behouden, maar de pixel-arcering moet enigszins worden gewijzigd.

De eerste optie die we hebben, is om maar één component te nemen, zoals de groene:

precisie highp vlotter; variërende vec2 vUV; uniforme sampler2D textureSampler; void main (void) gl_FragColor = vec4 (texture2D (textureSampler, vUV) .ggg, 1.0); 

Zoals je kunt zien, in plaats van gebruiken .rgb (deze bewerking wordt een a genoemd swizzle), we gebruikten .ggg.

Maar als we een echt nauwkeurig zwart-witeffect willen, is het een beter idee om de helderheid te berekenen (waarbij rekening wordt gehouden met alle kleurcomponenten):

precisie highp vlotter; variërende vec2 vUV; uniforme sampler2D textureSampler; void main (void) float luminance = dot (texture2D (textureSampler, vUV) .rgb, vec3 (0.3, 0.59, 0.11)); gl_FragColor = vec4 (luminantie, luminantie, luminantie, 1,0); 

De puntbewerking (of puntproduct) wordt als volgt berekend:

resultaat = v0.x * v1.x + v0.y * v1.y + v0.z * v1.z

Dus in ons geval:

luminantie = r * 0,3 + g * 0,59 + b * 0,11 (deze waarden zijn gebaseerd op het feit dat het menselijk oog verstandiger is voor groen)

Klinkt cool, niet??

Cell Shading Shader

Laten we nu gaan naar een meer complexe arcering: de celschaduwshader.

Deze vereist dat we de top van de vertex en de positie van de vertex in de pixel-shader krijgen. Dus de vertex-shader ziet er als volgt uit:

precisie highp vlotter; // Attributenattribuut vec3-positie; kenmerk vec3 normaal; kenmerk vec2 uv; // Uniformen uniforme mat4-wereld; uniforme mat4 worldViewProjection; // Variërend variërend vec3 vPositionW; variërend vec3 vNormalW; variërende vec2 vUV; void main (void) vec4 outPosition = worldViewProjection * vec4 (position, 1.0); gl_Position = outPosition; vPositionW = vec3 (world * vec4 (position, 1.0)); vNormalW = normaliseren (vec3 (world * vec4 (normal, 0,0))); vUV = uv; 

Houd er rekening mee dat we ook de wereldmatrix gebruiken, omdat positie en normaal worden opgeslagen zonder enige transformatie en we de wereldmatrix moeten toepassen om rekening te houden met de rotatie van het object.

De pixel-shader is de volgende:

precisie highp vlotter; // Lichten variërend vec3 vPositionW; variërend vec3 vNormalW; variërende vec2 vUV; // Refs uniform sampler2D textureSampler; void main (void) float ToonThresholds [4]; ToonThresholds [0] = 0,95; ToonThresholds [1] = 0,5; ToonThresholds [2] = 0,2; ToonThresholds [3] = 0,03; zweven ToonBrightnessLevels [5]; ToonBrightnessLevels [0] = 1.0; ToonBrightnessLevels [1] = 0,8; ToonBrightnessLevels [2] = 0.6; ToonBrightnessLevels [3] = 0.35; ToonBrightnessLevels [4] = 0.2; vec3 vLightPosition = vec3 (0, 20, 10); // Light vec3 lightVectorW = normalize (vLightPosition - vPositionW); // diffuse float ndl = max (0, punt (vNormalW, lightVectorW)); vec3 color = texture2D (textureSampler, vUV) .rgb; if (ndl> ToonThreshold [0]) color * = ToonBrightnessLevels [0];  else if (ndl> ToonThresholds [1]) color * = ToonBrightnessLevels [1];  else if (ndl> ToonThresholds [2]) color * = ToonBrightnessLevels [2];  else if (ndl> ToonThresholds [3]) color * = ToonBrightnessLevels [3];  else color * = ToonBrightnessLevels [4];  gl_FragColor = vec4 (kleur, 1.); 

Het doel van deze arcering is om een ​​licht te simuleren en in plaats van een vloeiende arcering te berekenen, zullen we overwegen dat licht wordt toegepast op basis van specifieke helderheidsdrempels. Bijvoorbeeld, als de lichtintensiteit er tussen zit 1 (maximum) en 0.95, de kleur van het object (opgehaald uit de textuur) wordt direct toegepast. Als de intensiteit tussen ligt 0.95 en 0.5, de kleur wordt verzwakt door een factor 0.8, enzovoorts.

Er zijn dus voornamelijk vier stappen in deze arcering:

  • Eerst verklaren we drempels en niveausconstanten.
  • Vervolgens moeten we de verlichting berekenen met behulp van de Phong-vergelijking (we nemen aan dat het licht niet beweegt): 
vec3 vLightPosition = vec3 (0, 20, 10); // Light vec3 lightVectorW = normalize (vLightPosition - vPositionW); // diffuse float ndl = max (0, punt (vNormalW, lightVectorW));

De intensiteit van het licht per pixel is afhankelijk van de hoek tussen de richting van het normale en het licht.

  • Dan krijgen we de textuurkleur voor de pixel.
  • En tot slot controleren we de drempel en passen we het niveau op de kleur toe.

Het resultaat ziet eruit als een cartoonobject: 

Phong Shader

We gebruikten een deel van de Phong-vergelijking in de vorige arcering. Laten we nu proberen het hele ding te gebruiken.

De vertex-shader is hier duidelijk eenvoudig, omdat alles in de pixel-shader wordt gedaan:

precisie highp vlotter; // Attributenattribuut vec3-positie; kenmerk vec3 normaal; kenmerk vec2 uv; // Uniformen uniforme mat4 worldViewProjection; // Variërend variërende vec3 vPosition; variërend vec3 vNormal; variërende vec2 vUV; void main (void) vec4 outPosition = worldViewProjection * vec4 (position, 1.0); gl_Position = outPosition; vUV = uv; v Positie = positie; v Normaal = normaal; 

Volgens de vergelijking moet je het diffuse en spiegelend deel berekenen door de richting van het licht en de normaal van de vertex te gebruiken:

precisie highp vlotter; // Variërend variërende vec3 vPosition; variërend vec3 vNormal; variërende vec2 vUV; // Uniformen uniforme mat4-wereld; // Refs uniforme vec3 cameraPosition; uniforme sampler2D textureSampler; void main (void) vec3 vLightPosition = vec3 (0, 20, 10); // Wereldwaarden vec3 vPositionW = vec3 (world * vec4 (vPosition, 1.0)); vec3 vNormalW = normaliseren (vec3 (world * vec4 (vNormal, 0,0))); vec3 viewDirectionW = normalize (cameraPosition - vPositionW); // Light vec3 lightVectorW = normalize (vLightPosition - vPositionW); vec3 color = texture2D (textureSampler, vUV) .rgb; // diffuse float ndl = max (0, punt (vNormalW, lightVectorW)); // Specular vec3 angleW = normalize (viewDirectionW + lightVectorW); float specComp = max (0, punt (vNormalW, angleW)); specComp = pow (specComp, max (1, 64)) * 2; gl_FragColor = vec4 (kleur * ndl + vec3 (specComp), 1.); 

We hebben het diffuse gedeelte al in de vorige arcering gebruikt, dus hier hoeven we alleen het spiegelgedeelte toe te voegen. Deze afbeelding uit een Wikipedia-artikel legt uit hoe de arcering werkt:

Door Brad Smith alias Rainwarrior.

Het resultaat op onze bol:

Gooi Shader weg

Voor de aflegshader wil ik een nieuw concept introduceren: de afdanken trefwoord. Deze arcering verwijdert elke niet-rode pixel en creëert de illusie van een "gegraven" object.

De hoekpuntshader is dezelfde als die wordt gebruikt door de standaard-arcering:

precisie highp vlotter; // Attributenattribuut vec3-positie; kenmerk vec3 normaal; kenmerk vec2 uv; // Uniformen uniforme mat4 worldViewProjection; // Variërend variërende vec2 vUV; void main (void) gl_Position = worldViewProjection * vec4 (position, 1.0); vUV = uv; 

De pixel shader moet de kleur testen en gebruiken afdanken wanneer, bijvoorbeeld, de groene component te hoog is:

precisie highp vlotter; variërende vec2 vUV; // Refs uniform sampler2D textureSampler; void main (void) vec3 color = texture2D (textureSampler, vUV) .rgb; if (color.g> 0,5) weggooien;  gl_FragColor = vec4 (kleur, 1.); 

Het resultaat is grappig:

Wave Shader

We hebben veel met pixel shaders gespeeld, maar ik wilde je ook laten zien dat we veel dingen kunnen doen met vertex shaders.

Voor de wave shader zullen we de Phong-pixelschaduw opnieuw gebruiken.

De hoekpuntshader gebruikt het opgeroepen uniform tijd om geanimeerde waarden te krijgen. Met dit uniform genereert de arcering een golf met de posities van de hoekpunten:

precisie highp vlotter; // Attributenattribuut vec3-positie; kenmerk vec3 normaal; kenmerk vec2 uv; // Uniformen uniforme mat4 worldViewProjection; uniforme zweeftijd; // Variërend variërende vec3 vPosition; variërend vec3 vNormal; variërende vec2 vUV; void main (void) vec3 v = position; v.x + = sin (2.0 * position.y + (tijd)) * 0.5; gl_Position = worldViewProjection * vec4 (v, 1.0); v Positie = positie; v Normaal = normaal; vUV = uv; 

Er wordt een sinus toegepast op position.y, en het resultaat is het volgende:

Sferische omgevingstoewijzing

Deze was grotendeels geïnspireerd door deze tutorial. Ik laat je dat uitstekende artikel lezen en spelen met de bijbehorende arcering. 

Fresnel Shader

Ik wil dit artikel graag afsluiten met mijn favoriet: de Fresnel-arcering.

Deze arcering wordt gebruikt om een ​​andere intensiteit toe te passen volgens de hoek tussen de weergaverichting en de normaal van de vertex.

De vertex-arcering is dezelfde die door de celschaduwader wordt gebruikt, en we kunnen de Fresnel-term gemakkelijk berekenen in onze pixelschader (omdat we de normale en de camerapositie hebben, die kan worden gebruikt om de weergaverichting te evalueren):

precisie highp vlotter; // Lichten variërend vec3 vPositionW; variërend vec3 vNormalW; // Refs uniforme vec3 cameraPosition; uniforme sampler2D textureSampler; void main (void) vec3 color = vec3 (1., 1., 1.); vec3 viewDirectionW = normalize (cameraPosition - vPositionW); // Fresnel float fresnelTerm = punt (viewDirectionW, vNormalW); fresnelTerm = klem (1,0 - fresnelTerm, 0., 1.); gl_FragColor = vec4 (color * fresnelTerm, 1.); 

Je Shader?

U bent nu meer voorbereid om uw eigen arcering te maken. Gebruik de opmerkingen hier of op het Babylon.js-forum om uw experimenten te delen!

Als je verder wilt gaan, zijn hier enkele handige links:

  • Babylon.js repo 
  • Babylon.js-forum 
  • CYOS
  • GLSL op Wikipedia 
  • GLSL-documentatie

En wat meer leren dat ik heb gemaakt over het onderwerp:

  • Introductie tot WebGL 3D met HTML5 en Babylon.JS
  • Cutting Edge Graphics in HTML

Of, een stap terug, de leerseries over JavaScript van ons team: 

  • Praktische prestatie-tips om uw HTML / JavaScript sneller te maken (een zevendelige serie van responsief ontwerp tot informele games tot prestatie-optimalisatie)
  • Het startschot voor het moderne webplatform (de fundamenten van HTML, CSS en JS)
  • Universele Windows-app ontwikkelen met HTML en JavaScript Jump Start (gebruik de JS die u al hebt gemaakt om een ​​app te bouwen)

En natuurlijk bent u altijd welkom om een ​​aantal van onze gratis tools te gebruiken bij het bouwen van uw volgende webervaring: Visual Studio Community, Azure Trial en testtools voor meerdere browsers voor Mac, Linux of Windows.

Dit artikel maakt deel uit van de web dev tech-serie van Microsoft. We zijn verheugd om te delen Microsoft Edge en het nieuwe EdgeHTML-renderingengine met jou. Download gratis virtuele machines of test op afstand op uw Mac, iOS, Android of Windows-apparaat @ http://dev.modern.ie/.