Dans le discours liminaire du deuxième jour de // Build 2014 (voir 2: 24-2: 28), les évangélistes de Microsoft, Steven Guggenheimer et John Shewchuk, ont présenté en démonstration comment l’aide d’Oculus Rift avait été ajoutée à Babylon.js. Et l'un des éléments clés de cette démo a été le travail que nous avons effectué sur un shader spécifique pour simuler des objectifs, comme vous pouvez le voir sur cette image:
J'ai également présenté une session avec Frank Olivier et Ben Constable à propos des graphiques sur IE et Babylon.js.
Ceci m’amène à l’une des questions que les gens me posent souvent à propos de Babylon.js: "Que voulez-vous dire par shaders?"Dans cet article, je vais vous expliquer le fonctionnement des shaders et vous donner quelques exemples de types courants de shaders..
Avant de commencer à expérimenter, nous devons d'abord voir comment les choses fonctionnent en interne.
En ce qui concerne la 3D à accélération matérielle, nous discutons de deux processeurs: le processeur principal et le GPU. Le GPU est une sorte de CPU extrêmement spécialisé.
Le GPU est une machine à états que vous avez configurée à l'aide de la CPU. Par exemple, le processeur va configurer le GPU pour qu'il rende des lignes au lieu de triangles. Ou il va définir que la transparence est activée, et ainsi de suite.
Une fois que tous les états sont définis, la CPU définit ce qu’il faut rendre: la géométrie, composée d’une liste de points (appelée sommets et stocké dans un tableau appelé vertex buffer) et une liste d’index (les faces, ou triangles, stockés dans un tableau appelé tampon d'index).
La dernière étape pour la CPU consiste à définir le mode de rendu de la géométrie. Pour cette tâche spécifique, la CPU définira shaders pour le GPU. Les shaders sont un morceau de code que le GPU exécutera pour chacun des sommets et des pixels qu'il doit restituer..
Premièrement, un peu de vocabulaire: considérez un sommet (sommets quand il y en a plusieurs) comme un «point» dans un environnement 3D (par opposition à un point dans un environnement 2D).
Il existe deux types de shaders: les vertex shaders et les pixels (ou fragments)..
Avant de creuser dans les shaders, faisons un pas en arrière. Pour restituer les pixels, le GPU prendra la géométrie définie par le CPU et procédera comme suit:
À l'aide du tampon d'index, trois sommets sont rassemblés pour définir un triangle: le tampon d'index contient une liste d'indices de sommets. Cela signifie que chaque entrée dans la mémoire tampon d'index est le numéro d'un sommet dans la mémoire tampon de vertex. Ceci est vraiment utile pour éviter la duplication de sommets.
Par exemple, le tampon d'index suivant est une liste de deux faces: [1 2 3 1 3 4]
. La première face contient le sommet 1, le sommet 2 et le sommet 3. La deuxième face contient le sommet 1, le sommet 3 et le sommet 4. Il y a donc quatre sommets dans cette géométrie:
Le vertex shader est appliqué sur chaque sommet du triangle. L'objectif principal du vertex shader est de produire un pixel pour chaque sommet (la projection sur l'écran 2D du sommet 3D):
En utilisant ces trois pixels (qui définissent un triangle 2D sur l’écran), le GPU interpolera toutes les valeurs attachées au pixel (au moins sa position) et le pixel shader sera appliqué sur chaque pixel inclus dans le triangle 2D afin de: générer une couleur pour chaque pixel:
Ce processus est effectué pour chaque visage défini par le tampon d'index..
De toute évidence, en raison de sa nature parallèle, le processeur graphique est capable de traiter cette étape pour plusieurs visages simultanément et d'obtenir ainsi de très bonnes performances..
Nous venons de voir que pour rendre les triangles, le processeur graphique a besoin de deux shaders: le vertex shader et le pixel shader. Ces shaders sont écrits en utilisant un langage appelé GLSL (Graphics Library Shader Language). Il ressemble à c.
Pour Internet Explorer 11, nous avons développé un compilateur pour transformer GLSL en HLSL (langage de shader de haut niveau), qui est le langage de shader de DirectX 11. Cela permet à IE11 de s’assurer que le code de shader est sûr (vous ne voulez pas utiliser WebGL pour réinitialiser votre ordinateur!):
Voici un exemple d'un vertex shader commun:
float highp de précision; // Attributs attribut vec3 position; attribut vec2 uv; // Uniformes uniformes mat4 worldViewProjection; // Variation variable vec2 vUV; void main (void) gl_Position = worldViewProjection * vec4 (position, 1.0); VUV = UV;
Un vertex shader contient les éléments suivants:
vecteur3: x, y, z
). Mais en tant que développeur, vous pouvez décider d'ajouter plus d'informations. Par exemple, dans l’ancien shader, il existe un vecteur2
nommé uv
(coordonnées de texture qui nous permettent d'appliquer une texture 2D sur un objet 3D).(x, y, z)
à l'écran (x, y)
.VUV
(une simple copie de uv
) valeur au pixel shader. Cela signifie qu'un pixel est défini ici avec une position et des coordonnées de texture. Ces valeurs seront interpolées par le GPU et utilisées par le pixel shader. principale()
est le code exécuté par le GPU pour chaque sommet et doit au moins produire une valeur pour gl_position
(la position sur l'écran du sommet actuel). Nous pouvons voir dans notre échantillon que le vertex shader est assez simple. Il génère une variable système (commençant par gl_
) nommé gl_position
définir la position du pixel associé, et définit une variable variable appelée VUV
.
Dans notre shader, nous avons une matrice nommée worldViewProjection
. Nous utilisons cette matrice pour projeter la position du sommet sur la gl_position
variable. C'est cool, mais comment pouvons-nous obtenir la valeur de cette matrice? C'est un uniforme, il faut donc le définir côté CPU (en utilisant JavaScript).
C'est l'une des parties complexes de la 3D. Vous devez comprendre les mathématiques complexes (ou vous devrez utiliser un moteur 3D, comme Babylon.js, que nous verrons plus tard).
le worldViewProjection
La matrice est la combinaison de trois matrices différentes:
L'utilisation de la matrice résultante nous permet de transformer des sommets 3D en pixels 2D tout en tenant compte du point de vue et de tout ce qui concerne la position / l'échelle / la rotation de l'objet en cours..
C’est votre responsabilité en tant que développeur 3D: créer et maintenir cette matrice à jour.
Une fois que le vertex shader est exécuté sur chaque sommet (trois fois, ensuite), nous avons trois pixels avec un gl_position
et un VUV
valeur. Le GPU interpolera ensuite ces valeurs sur chaque pixel contenu dans le triangle produit par ces pixels..
Ensuite, pour chaque pixel, il exécutera le pixel shader:
float highp de précision; variant vec2 vUV; sampler2D textureSampler uniforme; void main (void) gl_FragColor = texture2D (textureSampler, vUV);
La structure d'un pixel shader est similaire à un vertex shader:
VUV
valeur du vertex shader. principale
est le code exécuté par le GPU pour chaque pixel et doit au moins produire une valeur pour gl_FragColor
(la couleur du pixel actuel). Ce pixel shader est assez simple: il lit la couleur de la texture en utilisant les coordonnées de texture du vertex shader (qui à son tour l'a obtenue du vertex).
Voulez-vous voir le résultat d'un tel shader? C'est ici:
Ceci est rendu en temps réel; vous pouvez faire glisser la sphère avec votre souris.Pour atteindre ce résultat, vous devrez faire face à une lot de code WebGL. En effet, WebGL est une API très puissante, mais de bas niveau, et vous devez tout faire vous-même, de la création des tampons à la définition des structures de vertex. Vous devez également faire tous les calculs et définir tous les états et gérer le chargement de texture, etc.
Je sais ce que vous pensez: les shaders sont vraiment cool, mais je ne veux pas m'embêter avec la plomberie interne de WebGL ni même avec les mathématiques.
Et c'est bon! C’est une demande parfaitement légitime, et c’est exactement pourquoi j’ai créé Babylon.js.
Permettez-moi de vous présenter le code utilisé par la précédente démo Rolling Sphere. Tout d’abord, vous aurez besoin d’une simple page Web:
Babylon.js
Vous remarquerez que les shaders sont définis par >