Tạo hiệu ứng vòng tròn khuếch tán bằng Shader trong CesiumJS

Trong phát triển ứng dụng GIS với CesiumJS, việc tạo ra các hiệu ứng hình ảnh động như vòng tròn khuếch tán (diffusion circle) giúp tăng tính trực quan cho các điểm cảnh báo hoặc khu vực mục tiêu. Bài viết này sẽ hướng dẫn cách can thiệp vào shader thông qua Fabric Material để tạo hiệu ứng này.

Nguyên lý cơ bản

Để tạo một vòng tròn có hiệu ứng mờ dần từ tâm ra ngoài, chúng ta dựa trên tọa độ kết cấu (ST coordinates - tương tự UV). Khoảng cách từ một điểm bất kỳ trên bề mặt đến tâm (0.5, 0.5) sẽ quyết định độ trong suốt của điểm đó.

// Ý tưởng shader cơ bản
float radialDist = distance(st, vec2(0.5, 0.5)); 
opacity = 1.0 - radialDist; // Càng xa tâm càng mờ

Để tạo chuyển động khuếch tán, chúng ta sử dụng biến thời gian (trong Cesium là czm_frameNumber) kết hợp với hàm fract để tạo ra một chu kỳ lặp lại từ 0 đến 1. Khi giá trị này tăng dần, chúng ta mở rộng bán kính hiển thị của vòng tròn.

Triển khai trong CesiumJS

1. Khởi tạo Geometry

Đầu tiên, chúng ta cần một thực thể hình học để áp dụng chất liệu. Ở đây sử dụng EllipseGeometry để tạo hình tròn phẳng trên mặt đất.

const circleInstance = new Cesium.GeometryInstance({
    geometry: new Cesium.EllipseGeometry({
        center: Cesium.Cartesian3.fromDegrees(108.225, 16.067, 0), // Tọa độ ví dụ
        semiMinorAxis: 1000.0,
        semiMajorAxis: 1000.0,
    }),
    attributes: {
        color: Cesium.ColorGeometryInstanceAttribute.fromColor(Cesium.Color.RED)
    }
});

2. Định nghĩa Material với Custom Shader

Sử dụng MaterialAppearance để định nghĩa shader tùy chỉnh. Trong phần source, chúng ta sẽ tính toán ba lớp sóng khuếch tán đan xen để hiệu ứng mượt mà hơn.

const customAppearance = new Cesium.MaterialAppearance({
    material: new Cesium.Material({
        fabric: {
            uniforms: {
                diffuseColor: new Cesium.Color(0.0, 1.0, 1.0, 0.7),
                speedRate: 1.5
            },
            source: `
                czm_material czm_getMaterial(czm_materialInput materialInput) {
                    czm_material material = czm_getDefaultMaterial(materialInput);
                    vec2 st = materialInput.st;
                    float dist = distance(st, vec2(0.5, 0.5));
                    
                    // Tính toán thời gian dựa trên frame của Cesium
                    float time = czm_frameNumber * 0.01 * speedRate;
                    
                    // Tạo 3 lớp sóng lệch pha nhau
                    float wave1 = fract(time);
                    float wave2 = fract(time + 0.33);
                    float wave3 = fract(time + 0.66);
                    
                    // Hàm tính toán độ mờ cho từng lớp sóng
                    float alpha1 = step(dist, wave1 * 0.5) * (1.0 - wave1) * (dist * 2.0);
                    float alpha2 = step(dist, wave2 * 0.5) * (1.0 - wave2) * (dist * 2.0);
                    float alpha3 = step(dist, wave3 * 0.5) * (1.0 - wave3) * (dist * 2.0);
                    
                    // Tổng hợp các lớp sóng
                    float finalAlpha = max(max(alpha1, alpha2), alpha3);
                    
                    material.diffuse = diffuseColor.rgb * 1.5;
                    material.alpha = finalAlpha * diffuseColor.a;
                    return material;
                }
            `
        }
    })
});

3. Thêm Primitive vào Scene

Cuối cùng, kết hợp Geometry và Appearance vào một Primitive để hiển thị lên bản đồ.

const diffusionPrimitive = new Cesium.Primitive({
    geometryInstances: circleInstance,
    appearance: customAppearance,
    asynchronous: false
});

viewer.scene.primitives.add(diffusionPrimitive);

Giải thích mã Shader

  • czm_frameNumber: Biến hệ thống của Cesium cung cấp số thứ tự khung hình hiện tại, dùng để tạo biến thiên theo thời gian.
  • fract(x): Trả về phần thập phân của x, giúp tạo ra vòng lặp từ 0.0 đến 1.0.
  • step(edge, x): Nếu x < edge trả về 0, ngược lại trả về 1. Ở đây dùng để giới hạn phạm vi vẽ của vòng tròn theo thời gian.
  • materialInput.st: Tọa độ bề mặt của hình học, giúp xác định vị trí tương đối để tính toán khoảng cách từ tâm.

Bằng cách điều chỉnh speedRatediffuseColor, bạn có thể dễ dàng thay đổi tốc độ khuếch tán và màu sắc của hiệu ứng để phù hợp với yêu cầu của dự án.

Thẻ: CesiumJS webgl glsl shader GIS

Đăng vào ngày 23 tháng 7 lúc 07:50