pixiv est un service communautaire en ligne qui permet aux illustrateurs et aux passionnés d'illustration de communiquer entre eux grâce à leurs contenus. Il permet aux utilisateurs de publier leurs propres illustrations. Ils comptent plus de 84 millions d'utilisateurs dans le monde et plus de 120 millions d'œuvres d'art publiées en mai 2023.
pixiv Sketch est l'un des services fournis par pixiv. Il permet de dessiner des œuvres d'art sur le site Web, à l'aide des doigts ou d'un stylet. Il est compatible avec de nombreuses fonctionnalités permettant de créer des illustrations étonnantes, y compris de nombreux types de pinceaux, de calques et de peinture au pot, et permet également aux utilisateurs de diffuser en direct leur processus de dessin.
Dans cette étude de cas, nous allons examiner comment pixiv Sketch a amélioré les performances et la qualité de son application Web en utilisant de nouvelles fonctionnalités de la plate-forme Web telles que WebGL, WebAssembly et WebRTC.
Pourquoi développer une application de dessin sur le Web ?
pixiv Sketch a été lancé sur le Web et sur iOS en 2015. Leur audience cible pour la version Web était principalement les utilisateurs d'ordinateurs de bureau, qui constituent toujours la plate-forme la plus utilisée par la communauté des illustrateurs.
Voici les deux principales raisons pour lesquelles pixiv a choisi de développer une version Web plutôt qu'une application pour ordinateur :
- La création d'applications pour Windows, Mac, Linux et d'autres systèmes d'exploitation est très coûteuse. Le Web est accessible depuis n'importe quel navigateur sur ordinateur.
- Le Web offre la meilleure couverture sur les plates-formes. Le Web est disponible sur ordinateur et mobile, et sur tous les systèmes d'exploitation.
Technologie
pixiv Sketch propose aux utilisateurs un certain nombre de pinceaux différents. Avant l'adoption de WebGL, il n'existait qu'un seul type de pinceau, car le canevas 2D était trop limité pour représenter la texture complexe des différents pinceaux, comme les bords épais d'un crayon et les différentes intensités de largeur et de couleur qui changent en fonction de la pression exercée sur le croquis.
Types de pinceaux créatifs utilisant WebGL
Toutefois, avec l'adoption de WebGL, ils ont pu ajouter plus de variétés dans les détails des pinceaux et augmenter le nombre de pinceaux disponibles à sept.

Avec le contexte de canevas 2D, il n'était possible de dessiner que des lignes avec une texture simple et une largeur uniformément répartie, comme dans la capture d'écran suivante :

Ces lignes ont été dessinées en créant des chemins et en dessinant des traits, mais WebGL les reproduit à l'aide de sprites de points et de nuanceurs, comme le montrent les exemples de code suivants.
L'exemple suivant illustre un nuanceur de vertex.
precision highp float;
attribute vec2 pos;
attribute float thicknessFactor;
attribute float opacityFactor;
uniform float pointSize;
varying float varyingOpacityFactor;
varying float hardness;
// Calculate hardness from actual point size
float calcHardness(float s) {
float h0 = .1 * (s - 1.);
float h1 = .01 * (s - 10.) + .6;
float h2 = .005 * (s - 30.) + .8;
float h3 = .001 * (s - 50.) + .9;
float h4 = .0002 * (s - 100.) + .95;
return min(h0, min(h1, min(h2, min(h3, h4))));
}
void main() {
float actualPointSize = pointSize * thicknessFactor;
varyingOpacityFactor = opacityFactor;
hardness = calcHardness(actualPointSize);
gl_Position = vec4(pos, 0., 1.);
gl_PointSize = actualPointSize;
}
L'exemple suivant montre un exemple de code pour un nuanceur de fragment.
precision highp float;
const float strength = .8;
const float exponent = 5.;
uniform vec4 color;
varying float hardness;
varying float varyingOpacityFactor;
float fallOff(const float r) {
// w is for width
float w = 1. - hardness;
if (w < 0.01) {
return 1.;
} else {
return min(1., pow(1. - (r - hardness) / w, exponent));
}
}
void main() {
vec2 texCoord = (gl_PointCoord - .5) * 2.;
float r = length(texCoord);
if (r > 1.) {
discard;
}
float brushAlpha = fallOff(r) * varyingOpacityFactor * strength * color.a;
gl_FragColor = vec4(color.rgb, brushAlpha);
}
L'utilisation de sprites de points permet de faire varier facilement l'épaisseur et l'ombrage en fonction de la pression exercée sur le stylet, ce qui permet d'exprimer les lignes fortes et faibles suivantes, comme celles-ci :


De plus, les implémentations utilisant des sprites de points peuvent désormais associer des textures à l'aide d'un nuanceur distinct, ce qui permet de représenter efficacement les pinceaux avec des textures telles que le crayon et le feutre.
Compatibilité avec les stylets dans le navigateur
L'utilisation d'un stylet numérique est devenue extrêmement populaire auprès des artistes numériques. Les navigateurs modernes sont compatibles avec l'API PointerEvent, qui permet aux utilisateurs d'utiliser un stylet sur leur appareil : utilisez PointerEvent.pressure pour mesurer la pression du stylet et PointerEvent.tiltX, PointerEvent.tiltY pour mesurer l'angle du stylet par rapport à l'appareil.
Pour effectuer des coups de pinceau avec un sprite ponctuel, le PointerEvent doit être interpolé et converti en une séquence d'événements plus précise. Dans PointerEvent, l'orientation du stylet peut être obtenue sous la forme de coordonnées polaires, mais pixiv Sketch les convertit en vecteur représentant l'orientation du stylet avant de les utiliser.
function getTiltAsVector(event: PointerEvent): [number, number, number] {
const u = Math.tan((event.tiltX / 180) * Math.PI);
const v = Math.tan((event.tiltY / 180) * Math.PI);
const z = Math.sqrt(1 / (u * u + v * v + 1));
const x = z * u;
const y = z * v;
return [x, y, z];
}
function handlePointerDown(event: PointerEvent) {
const position = [event.clientX, event.clientY];
const pressure = event.pressure;
const tilt = getTiltAsVector(event);
interpolateAndRender(position, pressure, tilt);
}
Plusieurs calques de dessin
Les calques sont l'un des concepts les plus uniques du dessin numérique. Elles permettent aux utilisateurs de dessiner différentes parties d'une illustration les unes sur les autres et de les modifier couche par couche. pixiv Sketch propose des fonctions de calques semblables à celles d'autres applications de dessin numérique.
Il est possible d'implémenter des calques en utilisant plusieurs éléments <canvas> avec des opérations drawImage() et de composition. Toutefois, cela pose problème, car avec le contexte de canevas 2D, il n'y a pas d'autre choix que d'utiliser le mode de composition CanvasRenderingContext2D.globalCompositeOperation, qui est prédéfini et limite considérablement l'évolutivité. En utilisant WebGL et en écrivant le nuanceur, les développeurs peuvent utiliser des modes de composition qui ne sont pas prédéfinis par l'API. À l'avenir, pixiv Sketch implémentera la fonctionnalité de calques à l'aide de WebGL pour une évolutivité et une flexibilité accrues.
Voici un exemple de code pour la composition des calques :
precision highp float;
uniform sampler2D baseTexture;
uniform sampler2D blendTexture;
uniform mediump float opacity;
varying highp vec2 uv;
// for normal mode
vec3 blend(const vec4 baseColor, const vec4 blendColor) {
return blendColor.rgb;
}
// for multiply mode
vec3 blend(const vec4 baseColor, const vec4 blendColor) {
return blendColor.rgb * blendColor.rgb;
}
void main()
{
vec4 blendColor = texture2D(blendTexture, uv);
vec4 baseColor = texture2D(baseTexture, uv);
blendColor.a *= opacity;
float a1 = baseColor.a * blendColor.a;
float a2 = baseColor.a * (1. - blendColor.a);
float a3 = (1. - baseColor.a) * blendColor.a;
float resultAlpha = a1 + a2 + a3;
const float epsilon = 0.001;
if (resultAlpha > epsilon) {
vec3 noAlphaResult = blend(baseColor, blendColor);
vec3 resultColor =
noAlphaResult * a1 + baseColor.rgb * a2 + blendColor.rgb * a3;
gl_FragColor = vec4(resultColor / resultAlpha, resultAlpha);
} else {
gl_FragColor = vec4(0);
}
}
Peindre une grande zone avec la fonction de remplissage
Les applications pixiv Sketch pour iOS et Android proposaient déjà la fonctionnalité de bucket, mais pas la version Web. La version application de la fonction de bucket a été implémentée en C++.
Le code de base étant déjà disponible en C++, pixiv Sketch a utilisé Emscripten et asm.js pour implémenter la fonction de bucket dans la version Web.
bfsQueue.push(startPoint);
while (!bfsQueue.empty()) {
Point point = bfsQueue.front();
bfsQueue.pop();
/* ... */
bfsQueue.push(anotherPoint);
}
L'utilisation d'asm.js a permis de trouver une solution performante. En comparant le temps d'exécution de JavaScript pur à celui d'asm.js, on constate que le temps d'exécution avec asm.js est réduit de 67 %. Cela devrait être encore mieux avec WASM.
Détails du test :
- Comment : peignez une zone de 1 180 x 800 px avec la fonction de remplissage.
- Appareil de test : MacBook Pro (M1 Max)
Temps d'exécution :
- JavaScript pur : 213,8 ms
- asm.js: : 70,3 ms
En utilisant Emscripten et asm.js, pixiv Sketch a pu déployer la fonctionnalité de bucket en réutilisant la base de code de la version de l'application spécifique à la plate-forme.
Diffuser en direct pendant que vous dessinez
pixiv Sketch propose une fonctionnalité de diffusion en direct pendant le dessin, via l'application Web pixiv Sketch LIVE. Celle-ci utilise l'API WebRTC, en combinant la piste audio du micro obtenue à partir de getUserMedia() et la piste vidéo MediaStream récupérée à partir de l'élément <canvas>.
const canvasElement = document.querySelector('#DrawCanvas');
const framerate = 24;
const canvasStream = canvasElement.captureStream(framerate);
const videoStreamTrack = canvasStream.getVideoTracks()[0];
const audioStream = await navigator.mediaDevices.getUserMedia({
video: false,
audio: {},
});
const audioStreamTrack = audioStream.getAudioTracks()[0];
const stream = new MediaStream();
stream.addTrack(audioStreamTrack.clone());
stream.addTrack(videoStreamTrack.clone());
Conclusions
Grâce à la puissance de nouvelles API telles que WebGL, WebAssembly et WebRTC, vous pouvez créer une application complexe sur la plate-forme Web et la déployer sur n'importe quel appareil. Pour en savoir plus sur les technologies présentées dans cette étude de cas, consultez les liens suivants :
- WebGL
- Consultez également WebGPU, le successeur de WebGL.
- WebAssembly
- WebRTC
- Article d'origine en japonais