পিক্সিভ হল চিত্রকর এবং চিত্রাঙ্কন উৎসাহীদের জন্য একটি অনলাইন কমিউনিটি পরিষেবা যা তাদের কন্টেন্টের মাধ্যমে একে অপরের সাথে যোগাযোগ করতে সাহায্য করে। এটি মানুষকে তাদের নিজস্ব চিত্রাঙ্কন পোস্ট করতে দেয়। বিশ্বজুড়ে তাদের ৮৪ মিলিয়নেরও বেশি ব্যবহারকারী রয়েছে এবং ২০২৩ সালের মে পর্যন্ত ১২ কোটিরও বেশি শিল্পকর্ম পোস্ট করা হয়েছে।
পিক্সিভ স্কেচ হল পিক্সিভের প্রদত্ত পরিষেবাগুলির মধ্যে একটি। এটি আঙুল বা স্টাইলাস ব্যবহার করে ওয়েবসাইটে শিল্পকর্ম আঁকার জন্য ব্যবহৃত হয়। এটি বিভিন্ন ধরণের ব্রাশ, স্তর এবং বাকেট পেইন্টিং সহ আশ্চর্যজনক চিত্র আঁকার জন্য বিভিন্ন বৈশিষ্ট্য সমর্থন করে এবং লোকেদের তাদের অঙ্কন প্রক্রিয়া লাইভস্ট্রিম করার অনুমতি দেয়।
এই কেস স্টাডিতে, আমরা দেখব কিভাবে pixiv Sketch WebGL, WebAssembly এবং WebRTC এর মতো কিছু নতুন ওয়েব প্ল্যাটফর্ম বৈশিষ্ট্য ব্যবহার করে তাদের ওয়েব অ্যাপের কর্মক্ষমতা এবং মান উন্নত করেছে।
কেন ওয়েবে একটি স্কেচিং অ্যাপ তৈরি করবেন?
পিক্সিভ স্কেচ প্রথম ওয়েব এবং iOS-এ ২০১৫ সালে প্রকাশিত হয়েছিল। ওয়েব সংস্করণের জন্য তাদের লক্ষ্য দর্শক ছিল মূলত ডেস্কটপ, যা এখনও চিত্র সম্প্রদায়ের দ্বারা ব্যবহৃত সবচেয়ে বড় প্ল্যাটফর্ম।
ডেস্কটপ অ্যাপের পরিবর্তে ওয়েব সংস্করণ তৈরি করার জন্য পিক্সিভের দুটি প্রধান কারণ এখানে দেওয়া হল:
- উইন্ডোজ, ম্যাক, লিনাক্স এবং আরও অনেক কিছুর জন্য অ্যাপ তৈরি করা খুবই ব্যয়বহুল। ওয়েব ডেস্কটপের যেকোনো ব্রাউজারে পৌঁছায়।
- ওয়েব প্ল্যাটফর্ম জুড়ে সবচেয়ে বেশি ব্যবহারযোগ্য। ওয়েব ডেস্কটপ, মোবাইল এবং প্রতিটি অপারেটিং সিস্টেমে উপলব্ধ।
প্রযুক্তি
পিক্সিভ স্কেচে ব্যবহারকারীদের জন্য বিভিন্ন ধরণের ব্রাশ রয়েছে যা থেকে তারা বেছে নিতে পারে। 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);
}
একাধিক অঙ্কন স্তর
ডিজিটাল অঙ্কনের ক্ষেত্রে লেয়ার হল সবচেয়ে অনন্য ধারণাগুলির মধ্যে একটি। এটি ব্যবহারকারীদের একে অপরের উপরে বিভিন্ন ধরণের চিত্র আঁকতে দেয় এবং স্তরে স্তরে সম্পাদনা করার অনুমতি দেয়। পিক্সিভ স্কেচ অন্যান্য ডিজিটাল অঙ্কন অ্যাপের মতোই লেয়ার ফাংশন প্রদান করে।
প্রচলিতভাবে, drawImage() এবং কম্পোজিটিং অপারেশন সহ বিভিন্ন <canvas> উপাদান ব্যবহার করে স্তরগুলি বাস্তবায়ন করা সম্ভব। তবে এটি সমস্যাযুক্ত কারণ 2D ক্যানভাস প্রসঙ্গে, CanvasRenderingContext2D.globalCompositeOperation কম্পোজিশন মোড ব্যবহার করা ছাড়া অন্য কোনও বিকল্প নেই, যা পূর্বনির্ধারিত এবং মূলত স্কেলেবিলিটি সীমিত করে। WebGL ব্যবহার করে এবং শেডার লেখার মাধ্যমে, এটি ডেভেলপারদের এমন কম্পোজিশন মোড ব্যবহার করতে দেয় যা API দ্বারা পূর্বনির্ধারিত নয়। ভবিষ্যতে, 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);
}
}
বাকেট ফাংশন সহ বৃহৎ এলাকা রঙ করা
পিক্সিভ স্কেচ আইওএস এবং অ্যান্ড্রয়েড অ্যাপগুলিতে ইতিমধ্যেই বাকেট ফিচারটি দেওয়া ছিল, কিন্তু ওয়েব ভার্সনে তা দেওয়া হয়নি। বাকেট ফাংশনের অ্যাপ ভার্সনটি সি++ এ বাস্তবায়িত করা হয়েছিল।
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 তুলনা করলে, asm.js ব্যবহার করে এক্সিকিউশন সময় 67% কমে যায়। WASM ব্যবহার করলে এটি আরও ভালো হবে বলে আশা করা হচ্ছে।
পরীক্ষার বিবরণ:
- কীভাবে: বাকেট ফাংশন ব্যবহার করে ১১৮০x৮০০px এরিয়া রঙ করুন
- পরীক্ষামূলক ডিভাইস: ম্যাকবুক প্রো (এম১ ম্যাক্স)
কার্যকর করার সময়:
- বিশুদ্ধ জাভাস্ক্রিপ্ট: ২১৩.৮ মিলিসেকেন্ড
- asm.js: ৭০.৩ মিলিসেকেন্ড
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 এর মতো নতুন API গুলির শক্তি ব্যবহার করে, আপনি ওয়েব প্ল্যাটফর্মে একটি জটিল অ্যাপ তৈরি করতে পারেন এবং যেকোনো ডিভাইস জুড়ে এটিকে স্কেল করতে পারেন। এই কেস স্টাডিতে প্রবর্তিত প্রযুক্তি সম্পর্কে আপনি নিম্নলিখিত লিঙ্কগুলিতে আরও জানতে পারেন:
- ওয়েবজিএল
- এছাড়াও WebGL এর উত্তরসূরী WebGPU দেখুন।
- ওয়েব অ্যাসেম্বলি
- WebRTC সম্পর্কে
- জাপানি ভাষায় মূল প্রবন্ধ