JOKER
Dành cho buổi họp có biên bảnTrả lại lá Joker
Bài viết kỹ thuật

200.000 hạt trên điện thoại tầm trung200.000 hạt trên điện thoại tầm trung

Lá Joker ở trang chủ được vẽ bằng WebGL2 viết tay, không thư viện 3D. Đây là cách nó chạy mượt mà máy chủ không phải làm gì.

17/10/20264 phút đọcWebGL2Hiệu năngShader

Vì sao không dùng thư viện 3D

Một thư viện 3D phổ biến nặng hàng trăm KB. Lá bài chỉ cần một loại vật thể duy nhất: điểm sáng. Viết thẳng bằng WebGL2, toàn bộ cảnh chỉ còn khoảng 5 KB sau nén, tải sau khi chữ trên trang đã hiện.

Hạt không được tải về, hạt được sinh ra

Hình lá bài được vẽ lên một canvas 2D ẩn, rồi lấy mẫu ngẫu nhiên các điểm ảnh có màu. Mỗi hạt chỉ mang vài số: toạ độ trên lá bài, số thứ tự lá nhỏ mà nó thuộc về, và bốn số ngẫu nhiên. Không có tệp toạ độ nào đi qua mạng.

js
const img = cx.getImageData(0, 0, CW, CH).data;
const strong = [], soft = [];
for (let i = 0; i < CW * CH; i++) {
  const a = img[i * 4 + 3];
  if (a > 200) strong.push(i);   // nét đậm: lấy nhiều hạt
  else if (a > 40) soft.push(i); // phần tô nhạt: lấy ít hơn
}

Mọi chuyển động nằm trong vertex shader

Mỗi khung hình, JavaScript chỉ gửi vài con số: thời gian, tiến độ cuộn, vị trí chuột. Vertex shader tự tính vị trí từng hạt: lá lớn, đám mây tung ra, rồi xoè thành tay bài. GPU xử lý song song hàng trăm nghìn hạt, CPU gần như rảnh.

glsl
float p  = clamp(uProg * 1.25 - aR.x * .25, 0., 1.); // mỗi hạt trễ một chút
float e1 = smoothstep(0., .55, p);   // lá lớn → đám mây
float e2 = smoothstep(.5, 1., p);    // đám mây → tay bài
vec3 pos = mix(mix(big, cloud, e1), fan, e2);

Tự điều tốc theo máy

Trang đo số khung hình mỗi nửa giây bằng thời gian thực, không bằng thời gian đã bị chặn trần. Dưới 50 khung thì giảm số hạt và độ phân giải; dưới 30 thì giảm nhanh hơn. Máy quá yếu thì đóng băng hoạt cảnh, chỉ vẽ lại khi người dùng cuộn.

js
low = fps < 30 ? low + 1 : fps < 50 ? low + 0.5 : 0;
if (low >= 2 && count > 8000) {
  count = Math.max(8000, Math.floor(count * 0.65));
  maxDpr = Math.max(1, maxDpr - 0.25); resize();
}

Máy không có GPU thật thì sao

Một số máy, và hầu hết công cụ đo hiệu năng, vẽ WebGL bằng phần mềm. Chỉ việc biên dịch shader ở đó đã tốn hàng trăm mili giây. Trang hỏi tên GPU trước; nếu là bộ vẽ phần mềm thì dừng ngay và giữ lá bài SVG tĩnh, vẽ từ cùng hình gốc.

Chỉ chạy khi có người thật

Công cụ đo và bot thường tự cuộn trang, khiến cảnh 3D khởi động dù không ai xem. Cảnh chỉ bắt đầu sau cử chỉ đầu tiên của người thật: chạm, rê chuột, lăn chuột hoặc bấm phím.

js
const EVS = ['pointermove', 'pointerdown', 'touchstart', 'wheel', 'keydown'];
export const engaged = new Promise((resolve) => {
  const go = () => { EVS.forEach((e) => removeEventListener(e, go)); resolve(); };
  EVS.forEach((e) => addEventListener(e, go, { passive: true, once: true }));
});

Chạy thử điều bài viết nói

02Đồ hoạ GPU và kiến trúc tối ưu

Trang này

Trang này là thí nghiệm đầu tiên.Hiệu ứng ngang studio quốc tế, chạy trên điện thoại tầm trung, trong khi máy chủ gần như không làm gì.

Chạy thí nghiệm