Hướng dẫn kỹ thuật

Cơ chế kết xuất raymarching trong WebGL 2.0

Tìm hiểu vòng lặp raymarching, trường khoảng cách có dấu SDF, kết xuất thể tích và các kỹ thuật giảm tải tính toán GPU trong WebGL 2.0.

🚀 volumeshadertest
3 thg 12
10 phút đọc
Cơ chế kết xuất raymarching trong WebGL 2.0

WebGL 2.0 cho phép thực hiện các kỹ thuật kết xuất bằng GPU ngay trên trình duyệt. Raymarching dùng phép toán để mô tả và kết xuất cảnh, với rất ít hình học truyền thống.

Bài viết này trình bày vòng lặp raymarching, cách trường khoảng cách có dấu (SDF) mô tả bề mặt, kết xuất thể tích và những kỹ thuật giảm chi phí tính toán.

1. Raymarching là gì?

Thay vì chiếu hình học lên màn hình, raymarching phát một tia từ camera cho mỗi điểm ảnh, di chuyển tia từng bước qua cảnh ảo và tính khoảng cách cho đến khi tia chạm bề mặt hoặc đạt giới hạn khoảng cách.

1.1 Sơ đồ hoạt động

Camera
  |
  | tia
  v
  +--------------------------------- Điểm ảnh màn hình
  |
  | bước 1      (khoảng cách d1)
  |
  |---------> bước 2      (khoảng cách d2)
  |
  |-----------------------> bước 3 ...
  |
Chạm bề mặt khi khoảng cách < ε

Về cơ bản, raymarching là một vòng lặp:

for (int i = 0; i < MAX_STEPS; i++) {
    float dist = sdf(currentPos);
    if (dist < EPSILON) hitSurface();
    currentPos += rayDir * dist;
}

Hàm SDF cung cấp khoảng cách cần dùng ở mỗi bước. Đây là mã minh họa; hàm xử lý va chạm và điều kiện dừng cần được xác định trong bộ kết xuất hoàn chỉnh.

2. Trường khoảng cách có dấu (SDF) là gì?

SDF là hàm trả về khoảng cách có dấu đến bề mặt vật thể:

> 0  điểm ở bên ngoài vật thể
= 0  điểm nằm đúng trên bề mặt
< 0  điểm ở bên trong vật thể

2.1 Sơ đồ SDF

           bên ngoài
        +------------+
        |   d = 0,5  |
        |            |
bên     |     SDF    | bề mặt d = 0
trong   |            |
d < 0   |   d = -0,3 |
        +------------+

2.2 Ví dụ SDF hình cầu

float sdSphere(vec3 p, float r) {
    return length(p) - r;
}

Kết hợp các hình cơ bản bằng phép hợp, hiệu và giao cho phép tạo cảnh phức tạp mà không cần lưới đa giác cho từng vật thể.

3. Vì sao dùng raymarching SDF trong WebGL 2.0?

3.1 Đặc điểm

  • Không cần lưới cho các vật thể SDF: hình dạng được tính trong fragment shader; bộ kết xuất vẫn có thể dùng hình học đơn giản để phủ màn hình.
  • Bề mặt liên tục: SDF mô tả bề mặt bằng hàm toán học thay vì độ chia nhỏ của lưới.
  • Vật thể động và biến dạng: thay đổi hình dạng bằng phép toán.
  • Mô tả cảnh gọn: một cảnh 3D có thể được xác định bằng một số hàm GLSL.

3.2 Các tính năng WebGL 2.0 liên quan

  • Số thực dấu phẩy động độ chính xác cao.
  • Shader GLSL ES 3.0.
  • Bộ đệm uniform (UBO).
  • Texture dấu phẩy động.
  • Vòng lặp và rẽ nhánh linh hoạt hơn.

Những tính năng này hỗ trợ việc triển khai raymarching. Hiệu năng thực tế vẫn phụ thuộc vào shader, GPU và trình duyệt; số phiên bản WebGL không tự bảo đảm FPS cao hơn.

4. Xây dựng bộ kết xuất raymarching WebGL 2.0

4.1 Thiết lập tia trong cảnh

Tính một tia cho mỗi điểm ảnh:

vec3 rayOrigin = cameraPos;
vec3 rayDir = normalize(uv.x * camRight + uv.y * camUp + camForward);

4.2 Vòng lặp dò tia

float marchRay(vec3 ro, vec3 rd) {
    float totalDist = 0.0;

    for (int i = 0; i < MAX_STEPS; i++) {
        vec3 p = ro + rd * totalDist;
        float d = map(p);     // hàm SDF của cảnh

        if (d < EPSILON) break;    // chạm bề mặt
        if (totalDist > MAX_DISTANCE) break;  // vượt phạm vi

        totalDist += d;
    }
    return totalDist;
}

4.3 Định nghĩa cảnh SDF

float map(vec3 p) {
    float s = sdSphere(p, 1.0);
    float box = sdBox(p - vec3(2.0, 0.0, 0.0), vec3(0.7));
    return min(s, box); // phép hợp
}

4.4 Tính pháp tuyến

Dùng xấp xỉ gradient:

vec3 calcNormal(vec3 p) {
    const vec2 e = vec2(0.001, 0.0);
    return normalize(vec3(
        map(p + e.xyy) - map(p - e.xyy),
        map(p + e.yxy) - map(p - e.yxy),
        map(p + e.yyx) - map(p - e.yyx)
    ));
}

4.5 Mô hình chiếu sáng

Thành phần ánh sáng khuếch tán đơn giản:

float diffuse = max(dot(normal, lightDir), 0.0);

5. Kết xuất thể tích: sương mù, mây và khói

Có thể mở rộng raymarching sang kết xuất thể tích: mỗi bước đóng góp màu và mật độ thay vì chỉ dừng tại bề mặt.

5.1 Sơ đồ lấy mẫu thể tích

Tia →
[mật độ thấp] [vừa] [dày] [không xuyên qua]
   +---------+-----+-----+----------------+
     mẫu 1   mẫu 2 mẫu 3      mẫu 4

5.2 Mô hình tích lũy màu

vec3 color = vec3(0);
float absorption = 1.0;

for (int i = 0; i < STEPS; i++) {
    float density = computeDensity(p);
    vec3 sampleColor = density * vec3(0.6, 0.7, 1.0);

    color += sampleColor * absorption;
    absorption *= (1.0 - density * 0.1);

    p += rd * STEP_SIZE;
}

Kết xuất thể tích cần nhiều phép tính hơn. Với số mẫu, độ phân giải và mô hình mật độ phù hợp với GPU, có thể dùng kỹ thuật này để tạo mây, sương và tinh vân theo thời gian thực.

6. Các kỹ thuật tối ưu hiệu năng

Raymarching có thể tạo tải lớn. Các cách giảm công việc cần tính gồm:

  1. Bỏ qua vùng trống theo khoảng cách: dùng khoảng cách SDF chính xác để tiến qua vùng không có vật thể.
  2. Thể tích bao: kiểm tra tia có đi vào vùng chứa SDF trước khi tính chi tiết.
  3. Kích thước bước thích ứng: bước nhỏ gần bề mặt, bước lớn trong không gian trống.
  4. Dừng sớm: ngừng dò ngay khi chạm bề mặt.
  5. Giảm MAX_STEPS: khoảng 64–128 là một điểm khởi đầu cho nhiều cảnh thời gian thực; điều chỉnh theo chất lượng và tải thực tế.
  6. Dùng uniform và bộ đệm hiệu quả: đưa các giá trị không thay đổi ra ngoài vòng lặp.

7. Cấu trúc mã cho dự án WebGL 2.0

Ví dụ thư mục:

shader/
  raymarch.vert
  raymarch.frag
src/
  webgl2-context.js
  camera.js
  renderer.js
index.html

Các phần thường có trong fragment shader:

// 1. Thiết lập camera và tia
// 2. Định nghĩa SDF
// 3. Hàm vật liệu và chiếu sáng
// 4. Vòng lặp dò tia
// 5. Xuất màu cuối cùng

8. Khi nào nên dùng raymarching?

Kỹ thuật này phù hợp với:

  • Bề mặt trơn được định nghĩa bằng hàm.
  • Hình phân dạng.
  • Cảnh khoa học viễn tưởng.
  • Bóng mềm dùng trường khoảng cách có dấu.
  • Hình học sinh bằng thuật toán.
  • Hiệu ứng thể tích như mây, sương và tia sáng.

Nó thường không phải lựa chọn phù hợp khi cần nhiều lưới phức tạp, nhân vật có khung xương chân thực hoặc cảnh động lớn với hàng nghìn vật thể. Raymarching đặc biệt hữu ích cho cảnh sinh bằng thuật toán và hiệu ứng hình ảnh có phong cách riêng.

9. Mối liên hệ giữa mô hình toán học và chi phí GPU

WebGL 2.0 raymarching dùng GLSL và SDF để mô tả cảnh 3D trên trình duyệt. Việc không cần lưới chi tiết cho từng vật thể chuyển phần lớn công việc sang fragment shader. Vì vậy, số điểm ảnh, số bước dò tia và độ phức tạp hàm khoảng cách quyết định phần đáng kể của chi phí kết xuất.

Liên hệ với Volume Shader BM Test

Các nguyên lý trên giải thích tác vụ của Volume Shader BM Test.

Vì sao bài kiểm tra có thể nặng hơn một bản demo 3D thông thường?

Một bản demo WebGL có thể chủ yếu vẽ đa giác. Bài kiểm tra này chạy vòng lặp raymarching cho từng điểm ảnh, với giới hạn hơn 1.000 bước ở các mức tải cao. GPU chạy trò chơi mượt vẫn có thể đạt FPS thấp trong tác vụ shader này; hai loại kết quả không tương đương.

Vì sao FPS trên điện thoại giảm?

Khi SoC nóng lên, điện thoại có thể giảm xung GPU. Kết quả đầu lần chạy vì thế có thể cao hơn cuối lần chạy. Giữ điều kiện nguồn điện giống nhau, dùng tải nhẹ hoặc trung bình và xem độ ổn định cùng FPS cao nhất. Đọc hướng dẫn kiểm tra GPU điện thoại.

Vì sao lần đầu có thể chậm hơn?

Lần tải đầu biên dịch GLSL thành chương trình phù hợp với trình điều khiển. Bước này có thể mất hàng trăm mili giây và gây giật ở đầu lần chạy. Dùng lần đầu để khởi động và lấy kết quả lần thứ hai để so sánh.

Cách so sánh công bằng

Dùng cùng bộ kết xuất, mức tải, độ phân giải và nguồn điện; giữ tab ở phía trước. Không so sánh trực tiếp tải nhẹ, trung bình và nặng vì chúng chạy lượng công việc shader khác nhau. Xem cách đọc kết quả FPS.

Nếu trình duyệt dùng GPU tích hợp thì sao?

Trên laptop có cả GPU tích hợp và GPU rời, Chrome hoặc Edge có thể mặc định dùng GPU tích hợp. Mở chrome://gpu hoặc edge://gpu và kiểm tra GL_RENDERER. Nếu muốn đo GPU rời, chọn GPU đó cho trình duyệt trong thiết lập đồ họa hệ điều hành. Hướng dẫn xử lý giật trình bày từng bước.

Chạy bài kiểm tra

Thử tác vụ raymarching thực tế với bài kiểm tra tải trung bình 45 giây.

Bắt đầu Volume Shader BM Test →

Hướng dẫn liên quan

Thẻ
WebGLRaymarchingKết xuất thể tíchHiệu năng GPU