قوة الويب بالنسبة إلى الرسامين: كيفية استخدام pixiv لتقنيات الويب في تطبيق الرسم الخاص بهم

‫pixiv هي خدمة منتدى على الإنترنت تتيح لرسامي الرسوم المخطّطة والمهتمين بالرسوم المخطّطة التواصل مع بعضهم البعض من خلال المحتوى الذي ينشرونه. يتيح هذا التطبيق للمستخدمين نشر رسوماتهم التوضيحية. ويضم التطبيق أكثر من 84 مليون مستخدم في جميع أنحاء العالم، وأكثر من 120 مليون قطعة فنية تم نشرها اعتبارًا من أيار (مايو) 2023.

‫pixiv Sketch هي إحدى الخدمات التي تقدّمها شركة pixiv. ويُستخدَم لرسم أعمال فنية على الموقع الإلكتروني باستخدام الأصابع أو أقلام الشاشة. يتيح التطبيق مجموعة متنوعة من الميزات لرسم رسوم توضيحية رائعة، بما في ذلك العديد من أنواع الفرش والطبقات والرسم باستخدام الدلو، كما يتيح للمستخدمين بث عملية الرسم بشكل مباشر.

في هذه الدراسة، سنلقي نظرة على كيفية تحسين تطبيق pixiv Sketch لأداء تطبيق الويب وجودته باستخدام بعض ميزات منصّة الويب الجديدة، مثل WebGL وWebAssembly وWebRTC.

ما أهمية تطوير تطبيق للرسم على الويب؟

تم إصدار تطبيق pixiv Sketch لأول مرة على الويب وأجهزة iOS في عام 2015. كان الجمهور المستهدف لإصدار الويب هو أجهزة الكمبيوتر المكتبي في المقام الأول، والتي لا تزال المنصة الأكثر استخدامًا من قِبل منتدى الرسوم التوضيحية.

في ما يلي أهم سببَين لاختيار pixiv تطوير إصدار على الويب بدلاً من تطبيق مخصّص لأجهزة الكمبيوتر المكتبي:

  • إنّ إنشاء تطبيقات لنظام التشغيل Windows وMac وLinux وغير ذلك من الأنظمة يتطلّب تكلفة عالية جدًا. يمكن للموقع الإلكتروني الوصول إلى أي متصفح على الكمبيوتر المكتبي.
  • تحقّق الويب أكبر مدى وصول على مستوى جميع المنصّات. تتوفّر الويب على أجهزة الكمبيوتر المكتبي والأجهزة الجوّالة وعلى كل نظام تشغيل.

تكنولوجيا

تتضمّن تطبيق pixiv Sketch عددًا من الفرش المختلفة التي يمكن للمستخدمين الاختيار من بينها. قبل استخدام WebGL، كان هناك نوع واحد فقط من الفرشاة لأنّ اللوحة ثنائية الأبعاد كانت محدودة جدًا لتصوير النسيج المعقد للفرشاة المختلفة، مثل الحواف الخشنة للقلم الرصاص واختلاف العرض وكثافة اللون التي تتغير حسب الضغط على القلم.

أنواع فُرش إبداعية باستخدام WebGL

ومع ذلك، من خلال استخدام WebGL، تمكّنوا من إضافة المزيد من الأنواع في تفاصيل الفرشاة وزيادة عدد الفرش المتاحة إلى سبعة.

الفرش السبع المختلفة في pixiv التي تتراوح بين الدقة والخشونة، والحدة وعدم الحدة، والدقة المنخفضة والدقة العالية، وما إلى ذلك

باستخدام سياق اللوحة ثنائية الأبعاد، كان من الممكن فقط رسم خطوط لها текстурة بسيطة بعرض موزّع بالتساوي، مثل لقطة الشاشة التالية:

لمسة فرشاة بنسيج بسيط

تم رسم هذه الخطوط من خلال إنشاء مسارات ورسم خطوط، ولكن 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);
}

يتيح استخدام النقاط المتحرّكة تغيير السماكة والتظليل استجابةً لضغط الرسم، ما يسمح بالتعبير عن الخطوط القوية والضعيفة التالية، مثل:

لمسات فرشاة حادة ومتسقة ذات نهايات رفيعة

لمسة فرشاة غير حادة مع تطبيق ضغط أكبر في المنتصف

بالإضافة إلى ذلك، يمكن الآن للعمليات التي تستخدم عناصر نقطية متحركة إرفاق مواد باستخدامshader منفصل، ما يسمح بتمثيل فعال للفرشاة باستخدام مواد مثل القلم الرصاص والقلم الفلمّي.

توافق مع قلم الشاشة على المتصفّح

أصبح استخدام قلم الشاشة الرقمي شائعًا جدًا لدى الفنانين الرقميين. تتيح browsers الحديثة استخدام PointerEvent API التي تتيح للمستخدمين استخدام قلم الشاشة على أجهزتهم: استخدِم PointerEvent.pressure لقياس ضغط القلم، واستخدِم PointerEvent.tiltX وPointerEvent.tiltY لقياس زاوية القلم بالنسبة إلى الجهاز.

لتنفيذ ضغطات الفرشاة باستخدام صورة متحركة نقطية، يجب تضمين PointerEvent وتحويلها إلى تسلسل أحداث أكثر دقة. في PointerEvent، يمكن الحصول على اتجاه قلم الشاشة على شكل إحداثيات قطبَية، ولكنّ pixiv Sketch تحوّلها إلى متجه يمثّل اتجاه قلم الشاشة قبل استخدامها.

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 وظائف الطبقات مثلها مثل التطبيقات الرقمية الأخرى لرسم الرسومات.

من الشائع تنفيذ الطبقات باستخدام عدّة <canvas> عناصر مع drawImage() وعمليات الدمج. ومع ذلك، يمثّل ذلك مشكلة لأنّه لا يوجد خيار آخر سوى استخدام CanvasRenderingContext2D.globalCompositeOperation وضع التركيب، وهو وضع محدّد مسبقًا ويحدّ من قابلية التوسيع إلى حد كبير في سياق اللوحة ثنائية الأبعاد. من خلال استخدام WebGL وكتابة برنامج التظليل، يسمح للمطوّرين باستخدام أوضاع التركيب التي لم يتم تحديدها مسبقًا من خلال واجهة برمجة التطبيقات. في المستقبل، سيتم استخدام 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++.

استخدَم فريق pixiv Sketch واجهة برمجة التطبيقات Emscripten وملف ‎asm.js لتنفيذ وظيفة الحزمة في إصدار الويب، وذلك لأنّ قاعدة التعليمات البرمجية كانت متاحة في C++.

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 LIVE. ويستخدم هذا البث واجهة برمجة التطبيقات WebRTC API، مع دمج ملف صوتي من الميكروفون تم الحصول عليه من getUserMedia() وملف فيديوMediaStream تم استرجاعه من عنصر <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());

الاستنتاجات

باستخدام إمكانات واجهات برمجة التطبيقات الجديدة، مثل WebGL وWebAssembly وWebRTC، يمكنك إنشاء تطبيق معقّد على منصة الويب وتوسيع نطاقه على أي جهاز. يمكنك الاطّلاع على مزيد من المعلومات عن التقنيات التي تمّ تقديمها في دراسة الحالة هذه من خلال الاطّلاع على الرابطين التاليين: