इलस्ट्रेटर के लिए वेब की सुविधा: 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 के इस्तेमाल से, ब्रश की बारीकियों में ज़्यादा वैरिएंट जोड़े जा सके. साथ ही, उपलब्ध ब्रश की संख्या सात हो गई.

pixiv में सात अलग-अलग ब्रश हैं. इनमें फ़ाइन से लेकर कोर्स, शार्प से लेकर अनशार्प, पिक्सलेट से लेकर स्मूद वगैरह शामिल हैं.

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 को इंटरपोलेट करके ज़्यादा सटीक इवेंट क्रम में बदला जाना चाहिए. 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 में लेयर की सुविधा को लागू किया जाएगा. इसके लिए, वेबजीएल का इस्तेमाल किया जाएगा, ताकि इसे ज़्यादा आसानी से इस्तेमाल किया जा सके और इसे बड़े पैमाने पर इस्तेमाल किया जा सके.

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

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 का इस्तेमाल करने से परफ़ॉर्मेंट सलूशन चालू हो गया. asm.js के मुकाबले, सामान्य JavaScript को एक्ज़ीक्यूट करने में लगने वाले समय की तुलना करने पर, 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 LIVE वेब ऐप्लिकेशन की मदद से, ड्रॉइंग करते समय लाइव स्ट्रीम करने की सुविधा देता है. यह सुविधा, 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 जैसे नए एपीआई की मदद से, वेब प्लैटफ़ॉर्म पर एक जटिल ऐप्लिकेशन बनाया जा सकता है और उसे किसी भी डिवाइस पर इस्तेमाल किया जा सकता है. इस केस स्टडी में बताई गई टेक्नोलॉजी के बारे में ज़्यादा जानने के लिए, यहां दिए गए लिंक पर जाएं: