Vidéo
Cette vidéo a des sous-titres en français.
Nous continuons cette série en examinant comment configurer le vertex buffer avec les sommets (vertices).
Les sommets seront entassés dans le buffer GPU de manière à constituer les trois pointes du triangle comme ceci :
Ne pouvez-vous pas attendre la fin de la série ?
Si vous souhaitez aller de l’avant sans attendre la prochaine vidéo de la série, je vous recommande de faire défiler jusqu’au code ci-dessous ou de consulter mon article : dessinons un triangle avec WebGPU.
Le code de la série vidéo
Au cours de cette série, nous étudierons ce code que vous pouvez retrouver dans cet article où on commence la série.
Avant de pouvoir rendre ce code, vous devez télécharger un navigateur capable d’exécuter du code WebGPU.
Les sommets
Les sommets d’une scène WebGPU sont des points que nous voulons rendre. Imaginons un triangle comme celui-ci :
Ce triangle a trois points (évidemment).
En termes graphiques, ces trois points sont des sommets. C’est notre travail de définir les attributs de chaque sommet.
Nous pouvons définir n’importe quel attribut tant qu’il est représenté par un nombre ou plusieurs nombres (virgule flottante, entier, etc.). Nous verrons ce que cela signifie dans un instant.
Les sommets de notre triangle
Dans le code, je définis ces trois sommets :
const vertices = new Float32Array([
-1.0, -1.0, 0, 1, 1, 0, 0, 1,
-0.0, 1.0, 0, 1, 0, 1, 0, 1,
1.0, -1.0, 0, 1, 0, 0, 1, 1
]);
À première vue, il n’est probablement pas évident, mais j’ai défini deux attributs pour chaque sommet.
Quoi ?!
Les deux attributs que j’ai définis sont la position et la couleur. Chaque somment a ces deux attributs.
De plus, ces attributs sont bien emballés dans un seul tampon comme vous pouvez le voir bien.
Pour mieux voir exactement ce que je veux dire, regardons l’image ci-dessous
Pour référence, voici à nouveau notre tampon vertices
:
const vertices = new Float32Array([
-1.0, -1.0, 0, 1, 1, 0, 0, 1,
-0.0, 1.0, 0, 1, 0, 1, 0, 1,
1.0, -1.0, 0, 1, 0, 0, 1, 1
]);
Sur la première ligne du tampon vertices
- on voit -1.0, -1.0, 0, 1, 1, 0, 0, 1
.
Les quatre premiers nombres représentent la position d’un sommet donné :
Si la quatrième coordonnée, , vous confond, ce n’est pas grave ! Ce n’est pas important pour le moment, mais je vous conseille de jeter un œil à cet article sur les coordonnées homogènes si vous êtes curieux.
Si le centre de notre triangle se trouve au point (0, 0, 0, 1)
, une position de (-1.0, -1.0, 0, 1.0)
veut dire que le sommet est à gauche et en bas.
Pouvez-vous déterminer quel point c’est sur le triangle?
Le point rouge en bas à gauche !
Nous voyons également que les quatre prochains nombres à virgule flottante sont définis comme 1, 0, 0, 1
.
Ils représentent la couleur du vertex en mode RGBA (Red Green Blue Alpha).
Donc, en utilisant les nombres ci-dessus, nous devrions voir rouge. Pourquoi ?
Décomposons chaque numéro ! :
- R : 1, ce qui signifie que 100 % du canal rouge doit être utilisé.
- G : 0, c’est-à-dire 0 %
- B : 0
- A : 1 - l’image doit être 100 % opaque.
On devrait donc voir ce sommet en rouge !
OK, mais comment est-il possible pour notre application de différencier ces deux attributs ?
Nous devons créer un descripteur. On y va !
Nous verrons comment ces attributs sont utilisés dans un shader de sommet (shader de vertex) dans un article ultérieur.
Descripteur de sommet (Vertex Descriptor)
Les vertexBuffersDescriptors
sont des instructions instruisant au GPU comment décoder le tampon.
const vertexBuffersDescriptors = [{
attributes: [
{
shaderLocation: 0,
offset: 0,
format: "float32x4"
}, // POSITION
{
shaderLocation: 1,
offset: 16,
format: "float32x4"
} // COLOR
],
arrayStride: 32,
stepMode: "vertex"
}];
Dans notre cas, on utilise 32
octets pour décrire tous les attributs d’un sommet donné. Dans nos shaders, le GPU pourra trouver le vecteur de position à l’offset 0
, et le vecteur de couleur à l’offset 16
- comme nous l’avons déjà vu.
Nous définissons également arrayStride
à et le stepMode
à vertex
ce qui signifie que pour chaque 32
octets, le GPU doit également analyser les attributs pour les 32
octets suivants, comme définis dans le champ attributes
.
Le champ dans chaque définition d’attribut, shaderLocation
sera nécessaire lorsque nous définirons nos shaders et rendrons le pipeline. Pour l’instant, les valeurs que j’ai utilisées fonctionnent bien.
Créer le tampon GPU
Après avoir défini nos sommets, nous créons le vertexBuffer
qui est le tampon qui vivra dans le GPU en appelant device.createBuffer()
. Cette fonction prend plusieurs arguments :
- size - la taille du tampon en
bytes
. - usage - nous décrivons comment le tampon sera utilisé pendant sa durée de vie. Nous définissons sa valeur comme des drapeaux de bitwise.
- mappedAtCreation - Un tampon mappé signifie que le CPU peut y accéder, mais pas le GPU. Inversement, si le tampon est non mappé, le GPU pourra y accéder et le CPU ne pourra pas.
Regardons ce code :
const vertexBuffer = device.createBuffer({
size: vertices.byteLength,
usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST,
mappedAtCreation: true
});
new Float32Array(vertexBuffer.getMappedRange()).set(vertices);
vertexBuffer.unmap();
Après la création du vertexBuffer
, il est accessible en appelant getMappedRange()
.
Ensuite, on invoque .set()
pour copier nos sommets dans le tampon GPU. Enfin, nous supprimons l’accès en écriture du CPU et accordons l’accès en lecture au GPU en appelant vertexBuffer.unmap()
.
Le code de la partie 4
Vous pouvez trouver le code de cette partie dans ce GitHub Gist.
La suite
Nous continuerons ce projet sur YouTube ! À la prochaine !