इलस्ट्रेटर के लिए वेब की सुविधा: pixiv अपने ड्रॉइंग ऐप्लिकेशन के लिए, वेब टेक्नोलॉजी का इस्तेमाल कैसे करता है

pixiv एक ऑनलाइन कम्यूनिटी सेवा है, जो इलस्ट्रेटर और इमेज बनाने में दिलचस्पी रखने वाले लोगों के लिए है, ताकि वे अपने कॉन्टेंट के ज़रिए एक-दूसरे से बातचीत कर सकें. इससे लोग अपनी इमेज पोस्ट कर सकते हैं. दुनिया भर में, इस संगठन के 8 करोड़ 40 लाख से ज़्यादा उपयोगकर्ता हैं और मई 2023 तक, 12 करोड़ से ज़्यादा कलाकृतियां पोस्ट की गई हैं.

pixiv Sketch, pixiv की सेवाओं में से एक है. इसका इस्तेमाल उंगलियों या स्टाइलस का इस्तेमाल करके, वेबसाइट पर आर्टवर्क बनाने के लिए किया जाता है. यह शानदार इलस्ट्रेशन बनाने के लिए कई तरह की सुविधाएं देता है. इनमें कई तरह के ब्रश, लेयर, और बकेट पेंटिंग की मदद से, लोग अपनी ड्रॉइंग प्रोसेस को लाइव स्ट्रीम कर सकते हैं.

इस केस स्टडी में हम देखेंगे कि pixiv Sketch ने WebGL, WebAssembly, और WebRTC जैसी कुछ नई वेब प्लैटफ़ॉर्म सुविधाओं का इस्तेमाल करके अपने वेब ऐप्लिकेशन की परफ़ॉर्मेंस और उसकी क्वालिटी को किस तरह बेहतर बनाया है.

वेब पर स्केच करने वाला ऐप्लिकेशन क्यों डेवलप करना चाहिए?

pixiv Sketch को पहली बार साल 2015 में वेब और iOS पर रिलीज़ किया गया था. वेब वर्शन के लिए, उनकी टारगेट ऑडियंस मुख्य रूप से डेस्कटॉप थी. यह अब भी इलस्ट्रेशन कम्यूनिटी का सबसे अहम प्लैटफ़ॉर्म है.

यहां pixiv की ये दो मुख्य वजहें दी गई हैं कि आपने डेस्कटॉप ऐप्लिकेशन के बजाय वेब वर्शन डेवलप करने का विकल्प चुना:

  • Windows, Mac, Linux वगैरह के लिए ऐप्लिकेशन बनाना बहुत महंगा पड़ता है. वेब, डेस्कटॉप पर किसी भी ब्राउज़र तक पहुंचता है.
  • अलग-अलग प्लैटफ़ॉर्म पर वेब की पहुंच सबसे ज़्यादा है. वेब, डेस्कटॉप और मोबाइल के साथ-साथ सभी ऑपरेटिंग सिस्टम पर उपलब्ध है.

टेक्नोलॉजी

pixiv Sketch में उपयोगकर्ताओं के लिए कई अलग-अलग ब्रश चुनने का विकल्प मौजूद है. WebGL को अपनाने से पहले, सिर्फ़ एक तरह का ब्रश उपलब्ध था, क्योंकि 2D कैनवस अलग-अलग ब्रश की जटिल बनावट को दिखाने के लिए काफ़ी सीमित था. जैसे, पेंसिल के मोटे किनारे और अलग-अलग चौड़ाई और रंग की तीव्रता, जो स्केच के दबाव में बदल जाती है.

WebGL का इस्तेमाल करके क्रिएटिव तरह के ब्रश

हालांकि, WebGL को अपनाने से, वे ब्रश की जानकारी में कई तरह के ब्रश शामिल कर पाए और उपलब्ध ब्रश की संख्या बढ़कर सात हो गई.

पिक्सिव के सात अलग-अलग ब्रश, जिनमें बारीकी से लेकर मोटे, शार्प, शार्प, बिना शार्प, पिक्सलेट, और स्मूद ब्रश शामिल हैं.

2D कैनवस कॉन्टेक्स्ट का इस्तेमाल करके, सिर्फ़ ऐसी लाइनें बनाई जा सकती हैं जिनकी सामान्य बनावट और चौड़ाई समान हो, जैसे कि नीचे दिया गया स्क्रीनशॉट:

आसान टेक्सचर वाला ब्रश स्ट्रोक.

ये लाइनें, पाथ बनाकर और ड्राइंग स्ट्रोक की मदद से बनाई गई थीं, लेकिन WebGL पॉइंट स्प्राइट और शेडर का इस्तेमाल करके इसे दोबारा बनाता है. इन्हें इन कोड सैंपल में दिखाया गया है

नीचे दिए गए उदाहरण में वर्टेक्स शेडर दिखाया गया है.

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;
}

नीचे दिए गए उदाहरण में, फ़्रैगमेंट शेडर के लिए सैंपल कोड दिखाया गया है.

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);
}

पॉइंट स्प्राइट के इस्तेमाल से, ड्रॉइंग के दबाव के हिसाब से मोटाई और शेडिंग को अलग-अलग करना आसान हो जाता है. इसकी मदद से, नीचे बताई गई मज़बूत और कमज़ोर लाइनों को इस तरह से दिखाया जा सकता है:

पतले सिरों के साथ तेज़, यहां तक कि ब्रश स्ट्रोक भी.

बीच में ज़्यादा दबाव देकर, ब्रश स्ट्रोक को अनशाप करें.

इसके अलावा, पॉइंट स्प्राइट का इस्तेमाल करके अब अलग-अलग शेडर का इस्तेमाल करके टेक्सचर जोड़े जा सकते हैं. इससे पेंसिल और फ़ील-टिप पेन जैसी टेक्सचर वाले ब्रशों को अच्छे से दिखाया जा सकता है.

ब्राउज़र पर स्टाइलस की सुविधा

डिजिटल स्टाइलस का इस्तेमाल करना, डिजिटल कलाकारों के बीच काफ़ी लोकप्रिय हो गया है. आधुनिक ब्राउज़र पर PointerEvent API काम करता है, जिसकी मदद से उपयोगकर्ता अपने डिवाइस पर स्टाइलस का इस्तेमाल कर सकते हैं: पेन का दबाव मापने के लिए PointerEvent.pressureका इस्तेमाल करें और डिवाइस के साथ पेन का ऐंगल मापने के लिए PointerEvent.tiltX, PointerEvent.tiltY का इस्तेमाल करें.

पॉइंट स्प्राइट की मदद से ब्रश स्ट्रोक करने के लिए, PointerEvent को इंटरपोलेट करके ज़्यादा सटीक इवेंट क्रम में बदलना चाहिए. पॉइंटर इवेंट में, स्टाइलस का ओरिएंटेशन पोलर निर्देशांक के रूप में लिया जा सकता है. हालांकि, पिक्सिव स्केचबुक का इस्तेमाल करने से पहले, स्टाइलस की स्क्रीन की दिशा दिखाने वाले वेक्टर में बदल जाता है.

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);
}

ड्रॉइंग की एक से ज़्यादा लेयर

डिजिटल ड्रॉइंग में लेयर एक सबसे यूनीक कॉन्सेप्ट है. ये उपयोगकर्ताओं को एक-दूसरे के ऊपर अलग-अलग इलस्ट्रेशन बनाने और हर लेयर में बदलाव करने की सुविधा देते हैं. pixiv Sketch, दूसरे डिजिटल ड्रॉइंग ऐप्लिकेशन की तरह ही लेयर फ़ंक्शन उपलब्ध कराता है.

पारंपरिक तौर पर, drawImage() के साथ कई <canvas> एलिमेंट का इस्तेमाल करके और कंपोज़िट ऑपरेशन करके लेयर को लागू किया जा सकता है. हालांकि, यह समस्या सही है, क्योंकि 2D कैनवस पर, कंपोज़िशन मोड के लिए CanvasRenderingContext2D.globalCompositeOperation का इस्तेमाल करने के अलावा कोई और विकल्प नहीं है. यह पहले से ही बना हुआ है और इसकी स्केलेबिलिटी को काफ़ी सीमित किया जाता है. WebGL का इस्तेमाल करके और शेडर लिखकर, यह डेवलपर को ऐसे कंपोज़िशन मोड इस्तेमाल करने की अनुमति देता है जो एपीआई की मदद से पहले से तय नहीं होते. आने वाले समय में, pixiv Sketch बड़े पैमाने पर बढ़ाए जा सकने और लचीलेपन के लिए WebGL का इस्तेमाल करके लेयर की सुविधा लागू करेगा.

लेयर कंपोज़िशन के लिए सैंपल कोड यहां दिया गया है:

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);
  }
}

बकेट फ़ंक्शन के साथ बड़े हिस्से की पेंटिंग

pixiv Sketch iOS और Android ऐप्लिकेशन में बकेट की सुविधा पहले से ही मौजूद है, लेकिन वेब वर्शन में ऐसा नहीं है. बकेट फ़ंक्शन का ऐप्लिकेशन वर्शन C++ में लागू किया गया था.

C++ में पहले से मौजूद कोडबेस की मदद से, pixiv Sketch ने वेब वर्शन में बकेट फ़ंक्शन लागू करने के लिए, Emscripten और asm.js का इस्तेमाल किया है.

bfsQueue.push(startPoint);

while (!bfsQueue.empty()) {
  Point point = bfsQueue.front();
  bfsQueue.pop();
  /* ... */
  bfsQueue.push(anotherPoint);
}

asm.js का इस्तेमाल करने से परफ़ॉर्मेंस से जुड़ा समाधान चालू हो गया है. सिर्फ़ JavaScript को एक्ज़ीक्यूट करने के समय और asm.js के इस्तेमाल की तुलना करने पर, asm.js का इस्तेमाल करके, एक्ज़ीक्यूशन के समय को 67% कम किया जाता है. WASM का इस्तेमाल करने पर यह और बेहतर हो सकता है.

जांच की जानकारी:

  • कैसे: बकेट फ़ंक्शन की मदद से 1180x800 पिक्सल के एरिया को पेंट करें
  • टेस्ट डिवाइस: MacBook Pro (M1 Max)

एक्ज़ीक्यूशन का समय:

  • प्योर JavaScript: 213.8 मि॰से॰
  • asm.js: 70.3 मि॰से॰

Emscripten और asm.js का इस्तेमाल करके, pixiv Sketch प्लैटफ़ॉर्म के ऐप्लिकेशन वर्शन के कोड बेस का फिर से इस्तेमाल करके, बकेट की सुविधा को सही तरीके से रिलीज़ किया गया.

ड्रॉइंग करते समय लाइव स्ट्रीमिंग करें

pixiv Sketch, pixiv Sketch लाइव वेब ऐप्लिकेशन की मदद से ड्रॉइंग करते समय लाइव स्ट्रीम करने की सुविधा देता है. यह WebRTC API का इस्तेमाल करता है और इसमें getUserMedia() से मिले माइक्रोफ़ोन ऑडियो ट्रैक और <canvas> एलिमेंट से लिया गया MediaStream वीडियो ट्रैक दोनों को शामिल किया जाता है.

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());

मीटिंग में सामने आए नतीजे

WebGL, WebAssembly, और WebRTC जैसे नए एपीआई की मदद से, आप वेब प्लैटफ़ॉर्म पर मुश्किल ऐप्लिकेशन बना सकते हैं और उसे किसी भी डिवाइस पर स्केल कर सकते हैं. इस केस स्टडी में जिन टेक्नोलॉजी के बारे में बताया गया है उनके बारे में ज़्यादा जानने के लिए, यहां दिए गए लिंक पर जाएं: