Bygg shaders med Babylon.js og WebGL Teori og eksempler

I keynote for dag 2 av // Build 2014 (se 2: 24-2: 28) dempet Microsoft evangelister Steven Guggenheimer og John Shewchuk hvordan Oculus Rift-støtte ble lagt til Babylon.js. Og en av de viktigste tingene for denne demoen var det arbeidet vi gjorde på en bestemt skygge for å simulere linser, som du kan se på dette bildet:

Jeg presenterte også en økt med Frank Olivier og Ben Constable om grafikk på IE og Babylon.js. 

Dette fører meg til et av spørsmålene folk ofte spør meg om Babylon.js: "Hva mener du med shaders?"Så i dette innlegget skal jeg forklare for deg hvordan shaders fungerer, og gi noen eksempler på vanlige typer shaders.

Teorien

Før vi begynner å eksperimentere, må vi først se hvordan ting fungerer internt.

Når vi arbeider med hardware-akselerert 3D, diskuterer vi to CPUer: Hovedprosessoren og GPU. GPU er en slags ekstremt spesialisert CPU.

GPU er en statlig maskin som du har satt opp ved hjelp av CPU. For eksempel vil CPUen konfigurere GPUen til å gjengi linjer i stedet for trekanter. Eller det vil definere at gjennomsiktighet er på, og så videre.

Når alle statene er satt, vil CPU definere hva som skal gjengis - geometrien, som består av en liste over punkter (kalt toppunkter og lagret i en oppringt gruppe vertex buffer), og en liste over indekser (ansikter eller trekanter, lagret i en oppringt gruppe indeksbuffer).

Det endelige trinnet for CPU er å definere hvordan geometrien skal gjengis, og for denne spesifikke oppgaven vil CPUen definere shaders for GPU. Shaders er et stykke kode som GPUen vil utføre for hver av kryssene og pikslene den skal gjengi.

Først noen ordforråd: tenk på et toppunkt (vertices når det er flere av dem) som et "punkt" i et 3D-miljø (i motsetning til et punkt i et 2D-miljø).

Det finnes to typer shaders: vertex shaders og piksel (eller fragment) shaders.

Grafikkledning

Før vi graver inn shaders, la oss ta et skritt tilbake. For å gjøre piksler, vil GPUen ta geometrien definert av CPUen og vil gjøre følgende:

Ved hjelp av indeksbufferen samles tre vertikaler for å definere en trekant: indeksbufferen inneholder en liste over verteksindekser. Dette betyr at hver oppføring i indeksbufferen er tallet til et toppunkt i toppunktbufferen. Dette er veldig nyttig for å unngå duplisering av hjørner. 

For eksempel er følgende indeksbuffer en liste over to ansikter: [1 2 3 1 3 4]. Det første ansiktet inneholder vertex 1, vertex 2 og toppunkt 3. Det andre ansiktet inneholder vertex 1, vertex 3 og toppunkt 4. Så det er fire hjørner i denne geometrien: 

Vertex shader brukes på hvert toppunkt av trekanten. Det primære målet for vertex shader er å produsere en piksel for hvert toppunkt (projeksjonen på 2D-skjermen i 3D-vertexet): 

Ved å bruke disse tre pikslene (som definerer en 2D-trekant på skjermen), vil GPUen interpolere alle verdiene som er knyttet til pikselet (i det minste sin posisjon), og pikselskyggeren vil bli brukt på hver piksel som er innbefattet i 2D-trekant for å generer en farge for hver piksel: 

Denne prosessen er gjort for hvert ansikt definert av indeksbufferen. 

Åpenbart, på grunn av sin parallelle karakter, er GPUen i stand til å behandle dette trinnet for mange ansikter samtidig, og dermed oppnå virkelig god ytelse.

GLSL

Vi har nettopp sett det for å gi trekanter, GPU trenger to shaders: vertex shader og pixel shader. Disse shaders er skrevet ved hjelp av et språk som heter GLSL (Graphics Library Shader Language). Det ser ut som C.

For Internet Explorer 11 har vi utviklet en kompilator for å omdanne GLSL til HLSL (High Level Shader Language), som er skyggespråket til DirectX 11. Dette gjør at IE11 kan sikre at shader-koden er trygg (du vil ikke bruke WebGL for å tilbakestille datamaskinen din!):

Her er et eksempel på en felles vertex shader:

presisjon highp float; // Attributter attributt vec3 posisjon; attributt vec2 uv; // Uniforms uniform mat4 worldViewProjection; // Varierende varierende vec2 vUV; void main (void) gl_Position = worldViewProjection * vec4 (posisjon, 1.0); vUV = uv; 

Vertex Shader Structure

En toppunktsskygge inneholder følgende:

  • Egenskaper: En attributt definerer en del av et toppunkt. Som standard skal et toppunkt inneholde en posisjon (a vektor3: x, y, z). Men som utvikler kan du bestemme deg for å legge til mer informasjon. For eksempel, i den tidligere shader, er det en Vektor2 oppkalt uv (teksturkoordinater som tillater oss å bruke en 2D-tekstur på et 3D-objekt).
  • uniformer: En uniform er en variabel som brukes av skyggeren og definert av CPU. Den eneste uniformen vi har her er en matrise som brukes til å projisere posisjonen til toppunktet (x, y, z) til skjermen (x, y).
  • varierende: Varierende variabler er verdier opprettet av vertex shader og overført til pixel shader. Her vil vertex shader overføre a VUV (en enkel kopi av uv) verdi til pixel shader. Dette betyr at en piksel er definert her med en posisjon og teksturkoordinater. Disse verdiene blir interpolert av GPU og brukes av pikselskyggeren. 
  • hoved: Funksjonen heter hoved() er koden utført av GPU for hvert vertex og må minst gi en verdi for gl_position (posisjonen på skjermen av dagens vertex). 

Vi kan se i vår prøve at vertex shader er ganske enkelt. Det genererer en systemvariabel (starter med gl_) oppkalt gl_position å definere plasseringen til den tilknyttede piksel, og den angir en varierende variabel som kalles VUV

Voodoo Bak Matrices

I vår shader har vi en matrise kalt worldViewProjection. Vi bruker denne matrisen til å projisere toppunktet til gl_position variabel. Det er kult, men hvordan får vi verdien av denne matrisen? Det er en uniform, så vi må definere den på CPU-siden (ved hjelp av JavaScript).

Dette er en av de komplekse delene av å gjøre 3D. Du må forstå komplisert matte (eller du må bruke en 3D-motor, som Babylon.js, som vi skal se senere).

De worldViewProjection Matrise er kombinasjonen av tre forskjellige matriser:

Ved å bruke den resulterende matrisen kan vi omdanne 3D-hjørner til 2D-piksler mens du tar hensyn til synspunktet og alt relatert til posisjonen / skalaen / rotasjonen av det nåværende objektet.

Dette er ditt ansvar som en 3D-utvikler: å opprette og holde denne matrisen oppdatert.

Tilbake til Shaders

Når vertex shader er utført på hvert vertex (tre ganger, da) har vi tre piksler med riktig gl_position og a VUV verdi. GPUen vil da interpolere disse verdiene på hver piksel som finnes i trekanten produsert av disse pikslene.

Så, for hver piksel, vil den utføre pixel shader:

presisjon highp float; varierende vec2 vUV; uniform sampler2D textureSampler; void main (void) gl_FragColor = texture2D (textureSampler, vUV); 

Pixel (eller Fragment) Shader Structure

Strukturen til en pikselskygger ligner en toppskygger:

  • varierende: Varierende variabler er verdier opprettet av vertex shader og overført til pixel shader. Her får pikselskyggeren en VUV verdi fra vertex shader. 
  • uniformer: En uniform er en variabel som brukes av skyggeren og definert av CPU. Den eneste uniformen vi har her er en sampler, som er et verktøy som brukes til å lese teksturfarger.
  • hoved: Funksjonen heter hoved- er koden utført av GPU for hver piksel og må minst gi en verdi for gl_FragColor (fargen på den nåværende piksel). 

Denne pikselskyggeren er ganske enkel: Den leser fargen fra tekstur ved hjelp av teksturkoordinater fra vertex shader (som igjen fikk den fra vertexen).

Vil du se resultatet av en slik shader? Her er det:

Dette blir gjengitt i sanntid; du kan trekke sfæren med musen.

For å oppnå dette resultatet må du håndtere a mye av WebGL-koden. Faktisk er WebGL en veldig kraftig, men veldig lav nivå-API, og du må gjøre alt for deg selv, fra å lage buffere til å definere topptekststrukturer. Du må også gjøre all matte og sette alle statene og håndtere teksturbelastning og så videre ...

For hardt? BABYLON.ShaderMaterial til redning

Jeg vet hva du tenker: shaders er veldig kule, men jeg vil ikke bry meg med WebGL intern VVS eller til og med matte.

Og det er greit! Dette er en helt legitim spør, og det er nettopp derfor jeg opprettet Babylon.js.

La meg presentere koden som ble brukt av den tidligere rullende sfæredemoen. Først av alt trenger du en enkel nettside:

   Babylon.js          

Du vil merke at shaders er definert av > tags. Med Babylon.js kan du også definere dem i separate filer (.fx filer).

Du kan få Babylon.js her eller på vår GitHub repo. Du må bruke versjon 1.11 eller høyere for å få tilgang til BABYLON.StandardMaterial.

Og til slutt er hoved JavaScript-koden følgende:

"bruk strenge"; document.addEventListener ("DOMContentLoaded", startGame, feil); funksjon startGame () hvis (BABYLON.Engine.isSupported ()) var canvas = document.getElementById ("renderCanvas"); var motor = ny BABYLON.Engine (lerret, falsk); var scene = ny BABYLON.Scene (motor); var kamera = ny BABYLON.ArcRotateCamera ("Kamera", 0, Math.PI / 2, 10, BABYLON.Vector3.Zero (), scene); camera.attachControl (canvas); // Opprette sphere var sfære = BABYLON.Mesh.CreateSphere ("Sphere", 16, 5 scene); var amigaMaterial = nytt BABYLON.ShaderMaterial ("amiga", scene, vertexElement: "vertexShaderCode", fragmentElement: "fragmentShaderCode",, attributter: ["posisjon", "uv"], uniformer: ["worldViewProjection"] ); amigaMaterial.setTexture ("textureSampler", ny BABYLON.Texture ("amiga.jpg", scene)); sphere.material = amigaMaterial; engine.runRenderLoop (funksjon () sphere.rotation.y + = 0.05; scene.render ();); ;

Du kan se at jeg bruker en BABYLON.ShaderMaterial å bli kvitt all byrden av å kompilere, knytte og håndtere shaders.

Når du lager en BABYLON.ShaderMaterial, du må spesifisere DOM-elementet som brukes til å lagre shaders eller basenavnet til filene der shaders er. Hvis du velger å bruke filer, må du opprette en fil for hver skygge og bruke følgende filnavn mønster: basename.vertex.fx og basename.fragment.fx. Da må du lage materialet slik:

var cloudMaterial = nytt BABYLON.ShaderMaterial ("cloud", scene, "./myShader", attributes: ["posisjon", "uv"], uniformer: ["worldViewProjection"]);

Du må også angi navnene på eventuelle attributter og uniformer du bruker. Deretter kan du sette verdien av dine uniformer og samplere direkte ved hjelp av setTexture, setFloat, setFloats, setColor3, setColor4, setVector2, setVector3, setVector4, og setMatrix funksjoner.

Ganske enkelt, rett?

Husker du det forrige worldViewProjection matrise? Bruke Babylon.js og BABYLON.ShaderMaterial, du har ingenting å bekymre deg for! De BABYLON.ShaderMaterial vil automatisk beregne det for deg fordi du erklærer det på listen over uniformer.

BABYLON.ShaderMaterial kan også håndtere følgende matriser for deg:

  • verden 
  • utsikt 
  • projeksjon 
  • Worldview 
  • worldViewProjection 

Ingen behov for matte lenger. For eksempel, hver gang du kjører sphere.rotation.y + = 0.05, verdensmatrisen til sfæren genereres for deg og overføres til GPU.

CYOS: Lag din egen Shader

Så la oss gå større og lage en side der du dynamisk kan lage dine egne shaders og se resultatet umiddelbart. Denne siden skal bruke samme kode som vi tidligere diskuterte, og skal bruke en BABYLON.ShaderMaterial objekt å kompilere og utføre shaders som du vil opprette.

Jeg brukte ACE-kodeditor for CYOS. Dette er en utrolig kodeditor med syntax-highlightere. Ta gjerne en titt på det her. Du finner CYOS her.

Ved å bruke den første kombinasjonsboksen, vil du kunne velge forhåndsdefinerte shaders. Vi vil se hver av dem rett etterpå.

Du kan også endre nettverket (3D-objektet) som brukes til å forhåndsvise shaders med den andre kombinasjonsboksen.

De Kompilere knappen brukes til å lage en ny BABYLON.ShaderMaterial fra shaders. Koden som brukes av denne knappen, er følgende: 

// Kompilere shaderMaterial = nytt BABYLON.ShaderMaterial ("shader", scene, vertexElement: "vertexShaderCode", fragmentElement: "fragmentShaderCode",, attributter: ["posisjon", "normal", "uv"], uniformer: ["verden", "worldView", "worldViewProjection"]); var refTexture = ny BABYLON.Texture ("ref.jpg", scene); refTexture.wrapU = BABYLON.Texture.CLAMP_ADDRESSMODE; refTexture.wrapV = BABYLON.Texture.CLAMP_ADDRESSMODE; var amigaTexture = ny BABYLON.Texture ("amiga.jpg", scene); shaderMaterial.setTexture ("textureSampler", amigaTexture); shaderMaterial.setTexture ("refSampler", refTexture); shaderMaterial.setFloat ("tid", 0); shaderMaterial.setVector3 ("cameraPosition", BABYLON.Vector3.Zero ()); shaderMaterial.backFaceCulling = false; mesh.material = shaderMaterial;

Brutalt enkelt, ikke sant? Materialet er klar til å sende deg tre forhåndsberegnede matriser (verden, Worldview og worldViewProjection). Vertikaler kommer med posisjon, normal og teksturkoordinater. To teksturer er også allerede lastet for deg:

amiga.jpgref.jpg

Og til slutt, her er det renderLoop hvor jeg oppdaterer to praktiske uniformer:

  • en som heter tid for å få noen morsomme animasjoner 
  • en som heter kameraposisjon for å få posisjonen til kameraet i shaders (som vil være nyttig for lysekvasjoner) 
engine.runRenderLoop (funksjon () mesh.rotation.y + = 0.001; hvis (shaderMaterial) shaderMaterial.setFloat ("tid", tid); tid + = 0.02; shaderMaterial.setVector3 ("cameraPosition", camera.position) ; scene.render (););

Takket være arbeidet vi gjorde på Windows Phone 8.1, kan du også bruke CYOS på Windows Phone (det er alltid en god tid å lage en skygger):

Grunnleggende Shader

Så la oss starte med den aller første shader definert på CYOS: Basic Shader.

Vi vet allerede denne skyggen. Det beregner gl_position og bruker teksturkoordinater for å hente en farge for hver piksel.

For å beregne pixelposisjonen trenger vi bare worldViewProjection matrise og toppunktets posisjon:

presisjon highp float; // Attributter attributt vec3 posisjon; attributt vec2 uv; // Uniforms uniform mat4 worldViewProjection; // Varierende varierende vec2 vUV; void main (void) gl_Position = worldViewProjection * vec4 (posisjon, 1.0); vUV = uv; 

Tekstur koordinater (uv) overføres uendret til pikselskyggeren.

Vær oppmerksom på at vi må legge til presisjon mediump float; på den første linjen for både toppunkt og pixel shaders fordi Chrome krever det. Det definerer at, for bedre ytelse, bruker vi ikke fullverdig flytende verdier.

Pikselskyggeren er enda enklere, fordi vi bare trenger å bruke teksturkoordinater og hente en teksturfarge:

presisjon highp float; varierende vec2 vUV; uniform sampler2D textureSampler; void main (void) gl_FragColor = texture2D (textureSampler, vUV); 

Vi så tidligere at textureSampler uniform er fylt med "amiga" tekstur, så resultatet er følgende:

Svart og hvitt Shader

La oss fortsette med en ny skygge: den svarte og hvite skyggen.

Målet med denne skyggen er å bruke den forrige men med en "svart og hvit eneste" gjengivelsesmodus. For å gjøre det kan vi beholde samme toppunktsskygge, men pikselskyggeren må være litt modifisert.

Det første alternativet vi har er å ta bare en komponent, for eksempel den grønne:

presisjon highp float; varierende vec2 vUV; uniform sampler2D textureSampler; void main (void) gl_FragColor = vec4 (texture2D (textureSampler, vUV) .ggg, 1.0); 

Som du kan se, i stedet for å bruke .rgb (denne operasjonen kalles a swizzle), vi brukte .ggg.

Men hvis vi ønsker en virkelig nøyaktig svart-hvitt effekt, ville det være en bedre ide å beregne luminansen (som tar hensyn til alle fargekomponenter):

presisjon highp float; varierende vec2 vUV; uniform sampler2D textureSampler; void main (tomrom) float luminance = dot (texture2D (textureSampler, vUV) .rgb, vec3 (0.3, 0.59, 0.11)); gl_FragColor = vec4 (luminans, luminans, luminans, 1,0); 

Dotoperasjonen (eller punktproduktet) beregnes slik:

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

Så i vårt tilfelle:

luminans = r * 0,3 + g * 0,59 + b * 0,11 (disse verdiene er basert på det faktum at menneskets øye er mer fornuftig til grønt)

Høres kult, gjør det ikke?

Cell Shading Shader

La oss nå flytte til en mer kompleks skygge: celleskyggelyseren.

Dette vil kreve at vi får toppunktets normale og toppunktets posisjon i pikselskyggeren. Så vertex shader vil se slik ut:

presisjon highp float; // Attributter attributt vec3 posisjon; attributt vec3 normal; attributt vec2 uv; // Uniforms uniform mat4 world; uniform mat4 worldViewProjection; // Varierende varierende vec3 vPositionW; varierende vec3 vNormalW; varierende vec2 vUV; void main (void) vec4 outPosition = worldViewProjection * vec4 (posisjon, 1.0); gl_Position = outPosition; vPositionW = vec3 (world * vec4 (posisjon, 1.0)); vNormalW = normaliser (vec3 (verden * vec4 (normal, 0,0))); vUV = uv; 

Vær oppmerksom på at vi også bruker verdensmatrisen fordi posisjon og normal lagres uten transformasjon, og vi må bruke verdensmatrisen for å ta hensyn til objektets rotasjon.

Pikselskyggeren er følgende:

presisjon highp float; // Lights varierende vec3 vPositionW; varierende vec3 vNormalW; varierende 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; float 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 = normalisere (vLightPosition - vPositionW); / diffus float ndl = max (0, punktum (vNormalW, lightVectorW)); vec3 color = texture2D (textureSampler, vUV) .rgb; hvis (ndl> ToonThresholds [0]) color * = ToonBrightnessLevels [0];  ellers hvis (ndl> ToonThresholds [1]) color * = ToonBrightnessLevels [1];  ellers hvis (ndl> ToonThresholds [2]) color * = ToonBrightnessLevels [2];  ellers hvis (ndl> ToonThresholds [3]) color * = ToonBrightnessLevels [3];  else color * = ToonBrightnessLevels [4];  gl_FragColor = vec4 (farge, 1.); 

Målet med denne skyggen er å simulere et lys, og i stedet for å beregne en jevn skygge, vil vi vurdere at lyset vil gjelde i henhold til bestemte lysstyrkenivåer. For eksempel, hvis lysintensiteten er mellom 1 (maksimum) og 0,95, Fargen på objektet (hentet fra tekstur) vil bli brukt direkte. Hvis intensiteten er mellom 0,95 og 0.5, fargen vil bli dempet av en faktor av 0.8, og så videre.

Så det er hovedsakelig fire trinn i denne shader:

  • Først erklærer vi terskler og nivåer konstanter.
  • Da må vi beregne belysningen ved hjelp av Phong-ligningen (vi antar at lyset ikke beveger seg): 
vec3 vLightPosition = vec3 (0, 20, 10); // Light vec3 lightVectorW = normalisere (vLightPosition - vPositionW); / diffus float ndl = max (0, punktum (vNormalW, lightVectorW));

Lysstyrken per piksel er avhengig av vinkelen mellom normal og lysets retning.

  • Da får vi teksturfargen til pikselet.
  • Og til slutt ser vi terskelen og bruker nivået til fargen.

Resultatet ser ut som et tegneserieobjekt: 

Phong Shader

Vi brukte en del av Phong-ligningen i forrige skygge. Så la oss prøve å bruke hele greia nå.

Vertex shader er tydelig enkelt her, fordi alt vil bli gjort i pixel shader:

presisjon highp float; // Attributter attributt vec3 posisjon; attributt vec3 normal; attributt vec2 uv; // Uniforms uniform mat4 worldViewProjection; // Varierende varierende vec3 vPosition; varierende vec3 vNormal; varierende vec2 vUV; void main (void) vec4 outPosition = worldViewProjection * vec4 (posisjon, 1.0); gl_Position = outPosition; vUV = uv; vPosition = posisjon; vNormal = normal; 

I følge ligningen må du beregne diffus og spekulær del ved å bruke lysretningen og toppunktets normale:

presisjon highp float; // Varierende varierende vec3 vPosition; varierende vec3 vNormal; varierende vec2 vUV; // Uniforms uniform mat4 world; // Refs uniform vec3 cameraPosition; uniform sampler2D textureSampler; void main (void) vec3 vLightPosition = vec3 (0, 20, 10); // Verden verdier vec3 vPositionW = vec3 (world * vec4 (vPosition, 1.0)); vec3 vNormalW = normalisere (vec3 (verden * vec4 (vNormal, 0,0))); vec3 viewDirectionW = normalisere (kameraposisjon - vPositionW); // Light vec3 lightVectorW = normalisere (vLightPosition - vPositionW); vec3 color = texture2D (textureSampler, vUV) .rgb; / diffus float ndl = max (0, punktum (vNormalW, lightVectorW)); // Specular vec3 angleW = normalisere (viewDirectionW + lightVectorW); flyte specComp = max (0, punktum (vNormalW, vinkelW)); specComp = pow (specComp, max (1, 64.)) * 2 .; gl_FragColor = vec4 (farge * ndl + vec3 (specComp), 1.); 

Vi brukte allerede den diffuse delen i forrige skygge, så her må vi bare legge til den spekulære delen. Dette bildet fra en Wikipedia-artikkel forklarer hvordan shader fungerer:

Av Brad Smith aka Rainwarrior.

Resultatet på vår sfære:

Slett Shader

For å kaste bort skjermen, vil jeg gjerne introdusere et nytt konsept: The forkaste søkeord. Denne shader vil kaste bort alle ikke-røde piksler og vil skape en illusjon av et "grått" objekt.

Vertex shader er den samme som den som brukes av grunnleggende shader:

presisjon highp float; // Attributter attributt vec3 posisjon; attributt vec3 normal; attributt vec2 uv; // Uniforms uniform mat4 worldViewProjection; // Varierende varierende vec2 vUV; void main (void) gl_Position = worldViewProjection * vec4 (posisjon, 1.0); vUV = uv; 

Pikselskyggeren må teste fargen og bruken forkaste når for eksempel den grønne komponenten er for høy:

presisjon highp float; varierende vec2 vUV; // Refs uniform sampler2D textureSampler; void main (void) vec3 color = texture2D (textureSampler, vUV) .rgb; hvis (color.g> 0.5) kaste bort;  gl_FragColor = vec4 (farge, 1.); 

Resultatet er morsomt:

Wave Shader

Vi har spilt mye med pixel shaders, men jeg ville også vise deg at vi kan gjøre mange ting med vertex shaders.

For bølgeskyggeren vil vi gjenbruke Phong Pixel Shader.

Den vertex shader vil bruke uniformen kalt tid for å få noen animerte verdier. Ved å bruke denne uniformen, vil skyggeren generere en bølge med toppunktene:

presisjon highp float; // Attributter attributt vec3 posisjon; attributt vec3 normal; attributt vec2 uv; // Uniforms uniform mat4 worldViewProjection; uniform flyt tid; // Varierende varierende vec3 vPosition; varierende vec3 vNormal; varierende vec2 vUV; void main (void) vec3 v = posisjon; v.x + = sin (2,0 * posisjon.y + (tid)) * 0,5; gl_Position = worldViewProjection * vec4 (v, 1.0); vPosition = posisjon; vNormal = normal; vUV = uv; 

En sinus brukes til position.y, og resultatet er følgende:

Sfærisk miljømapping

Denne var i stor grad inspirert av denne opplæringen. Jeg vil la deg lese den gode artikkelen og spille med tilhørende shader. 

Fresnel Shader

Jeg vil gjerne fullføre denne artikkelen med min favoritt: Fresnel shader.

Denne shader brukes til å bruke en annen intensitet i henhold til vinkelen mellom visningsretningen og toppunktets normale.

Vertex shader er den samme som brukes av celleskyggingsskyggeren, og vi kan enkelt beregne Fresnel-termen i vår pixel shader (fordi vi har det normale og kameraets posisjon, som kan brukes til å evaluere visningsretningen):

presisjon highp float; // Lights varierende vec3 vPositionW; varierende vec3 vNormalW; // Refs uniform vec3 cameraPosition; uniform sampler2D textureSampler; void main (void) vec3 color = vec3 (1., 1., 1.); vec3 viewDirectionW = normalisere (kameraposisjon - vPositionW); // Fresnel float fresnelTerm = prikk (viewDirectionW, vNormalW); fresnelTerm = klemme (1,0 - fresnelTerm, 0., 1.); gl_FragColor = vec4 (farge * fresnelTerm, 1.); 

Din Shader?

Du er nå mer forberedt på å skape din egen skygge. Du er velkommen til å bruke kommentarene her eller på Babylon.js forum for å dele eksperimenter!

Hvis du vil gå videre, her er noen nyttige lenker:

  • Babylon.js repo 
  • Babylon.js forum 
  • CYOS
  • GLSL på Wikipedia 
  • GLSL dokumentasjon

Og litt mer læring som jeg har skapt om emnet:

  • Introduksjon til WebGL 3D med HTML5 og Babylon.JS
  • Skjærekantgrafikk i HTML

Eller gå tilbake, lagets læringsserie på JavaScript: 

  • Praktiske ytelsestips for å gjøre HTML / JavaScript raskere (en syvdelte serie fra lydhør design til uformelle spill til ytelsesoptimalisering)
  • Den moderne webplattformen Jump Start (grunnleggende for HTML, CSS og JS)
  • Utvikle Universal Windows App med HTML og JavaScript Jump Start (bruk JS du allerede har opprettet for å bygge en app)

Og selvfølgelig er du alltid velkommen til å bruke noen av våre gratis verktøy for å bygge din neste nettopplevelse: Visual Studio Community, Azure Trial og testverktøy for kryssbrowser for Mac, Linux eller Windows.

Denne artikkelen er en del av web dev-teknologiserien fra Microsoft. Vi er glade for å dele Microsoft Edge og den nye EdgeHTML rendering motor med deg. Få gratis virtuelle maskiner eller test eksternt på Mac, IOS, Android eller Windows-enheten @ http://dev.modern.ie/.