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ếux < edgetrả 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 speedRate và diffuseColor, 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.