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، تمكّنوا من إضافة المزيد من التنوع في تفاصيل الفرشاة وزيادة عدد الفرش المتاحة إلى سبعة.
باستخدام سياق اللوحة ثنائية الأبعاد، كان من الممكن فقط رسم خطوط لها текстурة بسيطة بعرض موزّع بالتساوي، مثل لقطة الشاشة التالية:
تم رسم هذه الخطوط من خلال إنشاء مسارات ورسم خطوط، ولكن 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، يمكنك إنشاء تطبيق معقّد على منصة الويب وتوسيع نطاقه على أي جهاز. يمكنك الاطّلاع على مزيد من المعلومات عن التقنيات التي تمّ تقديمها في دراسة الحالة هذه من خلال الاطّلاع على الرابطين التاليين:
- WebGL
- يمكنك أيضًا الاطّلاع على WebGPU، وهو الإصدار الأحدث من WebGL.
- WebAssembly
- WebRTC
- المقالة الأصلية باللغة اليابانية