pixiv adalah layanan komunitas online bagi ilustrator dan penggemar ilustrasi untuk berkomunikasi satu sama lain melalui konten mereka. Fitur ini memungkinkan pengguna memposting ilustrasi mereka sendiri. Mereka memiliki lebih dari 84 juta pengguna di seluruh dunia, dan lebih dari 120 juta karya seni yang diposting hingga Mei 2023.
pixiv Sketch adalah salah satu layanan yang disediakan oleh pixiv. Digunakan untuk menggambar karya seni di situs, menggunakan jari atau stylus. Aplikasi ini mendukung berbagai fitur untuk menggambar ilustrasi yang menakjubkan, termasuk berbagai jenis kuas, lapisan, dan lukisan bucket, serta memungkinkan orang melakukan livestream proses menggambar mereka.
Dalam studi kasus ini, kita akan melihat bagaimana pixiv Sketch meningkatkan performa dan kualitas aplikasi web mereka dengan menggunakan beberapa fitur platform web baru seperti WebGL, WebAssembly, dan WebRTC.
Mengapa mengembangkan aplikasi sketsa di web?
pixiv Sketch pertama kali dirilis di web dan di iOS pada tahun 2015. Target audiens untuk versi web mereka terutama adalah desktop, yang masih menjadi platform utama yang digunakan oleh komunitas ilustrasi.
Berikut dua alasan utama pixiv memilih mengembangkan versi web daripada aplikasi desktop:
- Membuat aplikasi untuk Windows, Mac, Linux, dan lainnya sangat mahal. Web dapat diakses di browser mana pun di desktop.
- Web memiliki jangkauan terbaik di seluruh platform. Web tersedia di desktop dan perangkat seluler, serta di setiap sistem operasi.
Teknologi
pixiv Sketch memiliki sejumlah kuas berbeda yang dapat dipilih pengguna. Sebelum mengadopsi WebGL, hanya ada satu jenis kuas karena kanvas 2D terlalu terbatas untuk menggambarkan tekstur kompleks dari berbagai kuas, seperti tepi kasar pensil dan perbedaan lebar serta intensitas warna yang berubah pada tekanan sketsa.
Jenis kuas kreatif menggunakan WebGL
Namun, dengan penerapan WebGL, mereka dapat menambahkan lebih banyak variasi dalam detail kuas dan meningkatkan jumlah kuas yang tersedia menjadi tujuh.

Dengan menggunakan konteks kanvas 2D, hanya garis yang memiliki tekstur sederhana dengan lebar yang didistribusikan secara merata yang dapat digambar, seperti screenshot berikut:

Garis ini digambar dengan membuat jalur dan menggambar goresan, tetapi WebGL mereproduksinya menggunakan sprite titik dan shader, yang ditunjukkan dalam contoh kode berikut
Contoh berikut menunjukkan shader vertex.
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;
}
Contoh berikut menunjukkan contoh kode untuk shader fragmen.
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);
}
Penggunaan sprite titik memudahkan untuk memvariasikan ketebalan dan shading sebagai respons terhadap tekanan gambar, sehingga memungkinkan garis tebal dan tipis berikut ditampilkan, seperti ini:


Selain itu, penerapan yang menggunakan sprite titik kini dapat melampirkan tekstur dengan menggunakan shader terpisah, sehingga memungkinkan representasi kuas yang efisien dengan tekstur seperti pensil dan pena ujung runcing.
Dukungan stilus di browser
Penggunaan stylus digital telah menjadi sangat populer di kalangan seniman digital. Browser
modern mendukung
PointerEvent API yang
memungkinkan pengguna menggunakan stilus di perangkat mereka: Gunakan PointerEvent.pressure untuk
mengukur tekanan pena, dan gunakan PointerEvent.tiltX, PointerEvent.tiltY untuk
mengukur sudut pena terhadap perangkat.
Untuk melakukan sapuan kuas dengan sprite titik, PointerEvent harus diinterpolasi dan dikonversi menjadi urutan peristiwa yang lebih terperinci. Di
PointerEvent, orientasi stylus dapat diperoleh dalam bentuk koordinat
polar, tetapi pixiv Sketch mengonversinya menjadi vektor yang merepresentasikan
orientasi stylus sebelum menggunakannya.
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);
}
Beberapa lapisan gambar
Lapisan adalah salah satu konsep paling unik dalam gambar digital. Fitur ini memungkinkan pengguna menggambar berbagai bagian ilustrasi di atas satu sama lain, dan mengedit lapisan demi lapisan. pixiv Sketch menyediakan fungsi lapisan seperti aplikasi menggambar digital lainnya.
Secara konvensional, lapisan dapat diimplementasikan menggunakan beberapa elemen <canvas> dengan drawImage() dan operasi komposit. Namun, hal ini
bermasalah karena dengan konteks kanvas 2D, tidak ada pilihan lain selain menggunakan
mode komposisi CanvasRenderingContext2D.globalCompositeOperation, yang telah ditentukan sebelumnya dan sangat membatasi skalabilitas. Dengan
menggunakan WebGL dan menulis shader, developer dapat menggunakan mode komposisi
yang tidak ditentukan sebelumnya oleh API. Pada masa mendatang, pixiv Sketch akan menerapkan fitur lapisan menggunakan WebGL untuk skalabilitas dan fleksibilitas yang lebih baik.
Berikut adalah contoh kode untuk komposisi lapisan:
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);
}
}
Mewarnai area besar dengan fungsi ember
Aplikasi iOS dan Android pixiv Sketch sudah menyediakan fitur bucket, tetapi versi web belum. Versi aplikasi fungsi bucket diimplementasikan di C++.
Dengan codebase yang sudah tersedia di C++, pixiv Sketch menggunakan Emscripten dan asm.js untuk menerapkan fungsi bucket ke versi web.
bfsQueue.push(startPoint);
while (!bfsQueue.empty()) {
Point point = bfsQueue.front();
bfsQueue.pop();
/* ... */
bfsQueue.push(anotherPoint);
}
Penggunaan asm.js memungkinkan solusi berperforma tinggi. Dengan membandingkan waktu eksekusi JavaScript murni dengan asm.js, waktu eksekusi menggunakan asm.js dipersingkat sebesar 67%. Performa ini diperkirakan akan lebih baik lagi saat menggunakan WASM.
Detail pengujian:
- Cara: Warnai area 1180x800 px dengan fungsi bucket
- Perangkat pengujian: MacBook Pro (M1 Max)
Waktu eksekusi:
- JavaScript Murni: 213,8 md
- asm.js: 70,3 md
Dengan menggunakan Emscripten dan asm.js, pixiv Sketch berhasil merilis fitur bucket dengan menggunakan kembali codebase dari versi aplikasi khusus platform.
Live streaming sambil menggambar
pixiv Sketch menawarkan fitur untuk melakukan live stream saat menggambar, melalui aplikasi web pixiv Sketch LIVE. Fitur ini menggunakan WebRTC API, menggabungkan trek audio mikrofon yang diperoleh dari getUserMedia() dan trek video MediaStream yang diambil dari elemen <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());
Kesimpulan
Dengan kecanggihan API baru seperti WebGL, WebAssembly, dan WebRTC, Anda dapat membuat aplikasi kompleks di platform web dan menskalakannya di perangkat apa pun. Anda dapat mempelajari lebih lanjut teknologi yang diperkenalkan dalam studi kasus ini di link berikut:
- WebGL
- Lihat juga WebGPU, penerus WebGL
- WebAssembly
- WebRTC
- Artikel asli dalam bahasa Jepang