当前位置: 首页 > ai >正文

第七章 Cesium 3D 粒子烟花效果案例解析:从原理到完整代码

效果图

本文详细介绍如何使用 Cesium.js 创建逼真的 3D 粒子烟花效果,包括核心原理、实现步骤和完整代码,并解决开发过程中常见的 "只读对象修改" 错误。

效果展示与技术原理

我们将在 Cesium 地球表面创建一组动态烟花效果,每个烟花由数百个粒子组成,具有随机的位置、颜色和爆炸范围,最终形成绚丽的 3D 视觉效果。

核心技术原理:

  • 利用 Cesium 的粒子系统 (ParticleSystem) 创建和管理粒子
  • 通过坐标系转换实现粒子在 3D 空间中的精确定位
  • 运用向量运算控制粒子运动轨迹
  • 使用生命周期管理实现粒子的产生、运动和消失

开发准备

环境依赖

  • Cesium.js 1.95(粒子系统 API 稳定版本)
  • 现代浏览器(支持 WebGL)

基础配置

<!-- 引入Cesium核心库 -->
<script src="https://cesium.com/downloads/cesiumjs/releases/1.95/Build/Cesium/Cesium.js"></script>
<!-- 引入Cesium样式表 -->
<link href="https://cesium.com/downloads/cesiumjs/releases/1.95/Build/Cesium/Widgets/widgets.css" rel="stylesheet">

实现步骤详解

1. 初始化 Cesium 场景

首先创建基础的 3D 地球场景,并配置卫星影像图层:

// 创建Cesium Viewer实例
const viewer = new Cesium.Viewer("cesiumContainer", {shouldAnimate: true,  // 启用自动动画,确保粒子系统实时更新baseLayerPicker: false // 关闭图层选择器
});// 清除默认图层,添加ArcGIS卫星影像
viewer.imageryLayers.removeAll();
viewer.imageryLayers.addImageryProvider(new Cesium.ArcGisMapServerImageryProvider({url: 'https://services.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer'})
);// 获取场景对象
const scene = viewer.scene;
// 显示帧率信息(调试用)
scene.debugShowFramesPerSecond = true;

2. 配置核心参数

将所有可调整的参数集中管理,便于后续维护和扩展:

const CONFIG = {// 粒子发射的经纬度位置(美国费城附近)emissionCoord: Cesium.Cartesian3.fromDegrees(-75.59777, 40.03883),// 发射器初始高度(地面以上100米)initialHeight: 100.0,// 粒子在屏幕上的大小particleSize: new Cesium.Cartesian2(7.0, 7.0),// 每次粒子爆发的数量burstCount: 400,// 单个粒子系统的生命周期systemLifetime: 10.0,// 总烟花数量totalFireworks: 20,// 烟花爆炸范围explosionRange: { min: 30.0, max: 100.0 },// 烟花位置的随机偏移范围offsetRange: { x: [-100, 100], y: [-80, 100], z: [-50, 50] },// 烟花颜色方案colorSchemes: [{ minRed: 0.75, green: 0.0, minBlue: 0.8, alpha: 1.0 }, // 粉紫色系{ red: 0.0, minGreen: 0.75, minBlue: 0.8, alpha: 1.0 },  // 青绿色系{ red: 0.0, green: 0.0, minBlue: 0.8, alpha: 1.0 },      // 蓝色系{ minRed: 0.75, minGreen: 0.75, blue: 0.0, alpha: 1.0 }  // 黄色系]
};

3. 创建粒子纹理

定义粒子的外观,使用 canvas 创建白色圆形作为粒子基础纹理:

// 粒子纹理缓存
let particleTexture;/*** 创建粒子纹理(白色圆形)* @returns {HTMLCanvasElement} 粒子纹理画布*/
const getParticleTexture = () => {// 如果纹理已创建,直接返回缓存的纹理if (Cesium.defined(particleTexture)) return particleTexture;// 创建20x20的画布const canvas = document.createElement("canvas");canvas.width = canvas.height = 20;const ctx = canvas.getContext("2d");// 绘制白色圆形ctx.arc(8, 8, 8, 0, Cesium.Math.TWO_PI, true);ctx.fillStyle = "#fff";ctx.fill();// 缓存纹理并返回particleTexture = canvas;return canvas;
};

4. 实现粒子系统核心逻辑

创建单个烟花的核心函数,控制粒子的产生、运动和消失:

/*** 创建单个烟花粒子系统* @param {Cesium.Cartesian3} offset - 烟花相对于初始位置的偏移量* @param {Cesium.Color} color - 烟花的颜色* @param {Cesium.ParticleBurst[]} bursts - 粒子爆发的时间配置*/
const createFirework = (offset, color, bursts) => {// 计算烟花的实际位置 = 初始位置 + 偏移量const fireworkPos = Cesium.Cartesian3.add(initialPos, offset, new Cesium.Cartesian3());// 创建发射器矩阵:将发射器定位到计算出的烟花位置const emitterMatrix = Cesium.Matrix4.fromTranslation(fireworkPos);// 计算坐标系转换矩阵const localToWorld = Cesium.Matrix4.multiply(emissionMatrix, emitterMatrix, new Cesium.Matrix4());const worldToLocal = Cesium.Matrix4.inverseTransformation(localToWorld, localToWorld);// 随机生成当前烟花的爆炸范围const explosionSize = Cesium.Math.randomBetween(CONFIG.explosionRange.min,CONFIG.explosionRange.max);/*** 粒子受力回调函数* 控制粒子运动,超出爆炸范围时停止*/const particleForce = (particle) => {const localPos = Cesium.Matrix4.multiplyByPoint(worldToLocal, particle.position, new Cesium.Cartesian3());// 超出爆炸范围时停止粒子运动if (Cesium.Cartesian3.magnitudeSquared(localPos) >= explosionSize **2) {// 关键修复:创建新的Cartesian3实例而非使用Cesium.Cartesian3.ZERO// 原因:Cesium.Cartesian3.ZERO是只读对象,直接赋值会导致错误particle.velocity = new Cesium.Cartesian3(0, 0, 0);}};// 计算粒子生命周期(与爆炸范围相关)const normalizedSize = (explosionSize - CONFIG.explosionRange.min) / (CONFIG.explosionRange.max - CONFIG.explosionRange.min);const particleLife = 0.3 + normalizedSize * 0.7;// 创建并添加粒子系统scene.primitives.add(new Cesium.ParticleSystem({image: getParticleTexture(),       // 粒子纹理startColor: color,                 // 初始颜色endColor: color.withAlpha(0.0),    // 结束颜色(透明,实现淡出)particleLife: particleLife,        // 粒子生命周期speed: 100.0,                      // 初始速度imageSize: CONFIG.particleSize,    // 粒子大小emissionRate: 0,                   // 不持续发射(仅通过burst)emitter: new Cesium.SphereEmitter(0.1),  // 发射器形状bursts: bursts,                    // 爆发配置lifetime: CONFIG.systemLifetime,   // 粒子系统生命周期updateCallback: particleForce,     // 粒子运动回调modelMatrix: emissionMatrix,       // 基础坐标系矩阵emitterModelMatrix: emitterMatrix  // 发射器位置矩阵}));
};

5. 批量创建烟花效果

循环创建多个烟花,实现多样化的视觉效果:

/*** 批量创建所有烟花*/
const createAllFireworks = () => {for (let i = 0; i < CONFIG.totalFireworks; i++) {// 生成随机位置偏移const offset = new Cesium.Cartesian3(Cesium.Math.randomBetween(...CONFIG.offsetRange.x),Cesium.Math.randomBetween(...CONFIG.offsetRange.y),Cesium.Math.randomBetween(...CONFIG.offsetRange.z));// 选择烟花颜色(循环使用颜色方案)const colorScheme = CONFIG.colorSchemes[i % CONFIG.colorSchemes.length];const color = Cesium.Color.fromRandom({minimumRed: colorScheme.minRed,red: colorScheme.red,minimumGreen: colorScheme.minGreen,green: colorScheme.green,minimumBlue: colorScheme.minBlue,blue: colorScheme.blue,alpha: colorScheme.alpha});// 配置粒子爆发(3次随机爆发)const bursts = Array.from({ length: 3 }, () => new Cesium.ParticleBurst({time: Cesium.Math.nextRandomNumber() * CONFIG.systemLifetime,minimum: CONFIG.burstCount,maximum: CONFIG.burstCount}));// 创建单个烟花createFirework(offset, color, bursts);}
};

6. 配置相机视角

调整相机位置和朝向,确保烟花效果在最佳视角展示:

/*** 配置相机视角,聚焦烟花区域*/
const setupCamera = () => {const camera = scene.camera;// 相机相对于发射器的偏移位置const cameraOffset = new Cesium.Cartesian3(-300.0, 0.0, 0.0);// 相机定位camera.lookAtTransform(emissionMatrix, cameraOffset);camera.lookAtTransform(Cesium.Matrix4.IDENTITY);// 计算相机看向烟花的方向向量// 关键修复:使用新实例存储结果,避免修改只读对象const lookDir = Cesium.Cartesian3.subtract(initialPos, cameraOffset, new Cesium.Cartesian3());// 归一化方向向量// 关键修复:使用新实例存储归一化结果const normalizedDir = Cesium.Cartesian3.normalize(lookDir, new Cesium.Cartesian3());// 计算并设置相机仰角const pitchAngle = Cesium.Math.PI_OVER_TWO - Math.acos(Cesium.Cartesian3.dot(normalizedDir, Cesium.Cartesian3.UNIT_Z));camera.lookUp(pitchAngle);
};

7. 初始化执行

// 批量创建所有烟花
createAllFireworks();
// 配置相机视角
setupCamera();

常见错误及解决方案

在开发过程中,最容易遇到的是 "Cannot assign to read only property 'x' of object" 错误,这是由于 Cesium 中的一些静态对象(如Cesium.Cartesian3.ZERO)是只读的。

错误原因:直接修改 Cesium 的只读对象

// 错误示例
particle.velocity = Cesium.Cartesian3.ZERO;

解决方案:创建新的对象实例

// 正确示例
particle.velocity = new Cesium.Cartesian3(0, 0, 0);

同样,在处理向量运算时,也要注意不要直接修改原对象:

// 错误示例 - 可能修改原对象
Cesium.Cartesian3.normalize(lookDir, lookDir);// 正确示例 - 使用新实例存储结果
const normalizedDir = Cesium.Cartesian3.normalize(lookDir, new Cesium.Cartesian3());

完整代码

<!DOCTYPE html>
<html lang="zh-CN"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Cesium粒子火花效果 - 修复版</title><!-- 引入Cesium核心库:提供3D地球和粒子系统支持 --><script src="https://cesium.com/downloads/cesiumjs/releases/1.95/Build/Cesium/Cesium.js"></script><!-- 引入Cesium样式表:确保控件正常显示 --><link href="https://cesium.com/downloads/cesiumjs/releases/1.95/Build/Cesium/Widgets/widgets.css" rel="stylesheet"><style>* {margin: 0;padding: 0;box-sizing: border-box;}/* 确保场景占满整个屏幕 */body,#cesiumContainer {width: 100vw;height: 100vh;overflow: hidden;}</style>
</head><body><!-- Cesium场景容器:所有3D内容将渲染到这里 --><div id="cesiumContainer"></div><script>/******************************************************************************* 1. 初始化Cesium核心环境* 作用:创建3D视图,配置地图图层,为粒子效果提供基础渲染环境******************************************************************************/// 创建Cesium Viewer实例(核心控制器)// shouldAnimate: true → 启用自动动画,确保粒子系统能实时更新// baseLayerPicker: false → 关闭图层选择器(我们将手动配置地图)const viewer = new Cesium.Viewer("cesiumContainer", {shouldAnimate: true,baseLayerPicker: false});// 清除默认图层(Cesium默认带的地图图层)viewer.imageryLayers.removeAll();// 添加ArcGIS卫星影像图层(高清卫星地图)viewer.imageryLayers.addImageryProvider(new Cesium.ArcGisMapServerImageryProvider({url: 'https://services.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer'}));// 获取场景对象(所有渲染和粒子系统的基础)const scene = viewer.scene;// 显示帧率信息(左下角,用于调试性能)scene.debugShowFramesPerSecond = true;/******************************************************************************* 2. 全局配置与工具函数* 作用:集中管理所有常量参数和复用性工具函数******************************************************************************/// 配置常量:所有可调整的参数集中在这里,便于维护const CONFIG = {// 粒子发射的经纬度位置(美国费城附近)emissionCoord: Cesium.Cartesian3.fromDegrees(-75.59777, 40.03883),// 发射器初始高度(地面以上100米)initialHeight: 100.0,// 粒子在屏幕上的大小(宽7像素,高7像素)particleSize: new Cesium.Cartesian2(7.0, 7.0),// 每次粒子爆发的数量burstCount: 400,// 单个粒子系统的生命周期(10秒后自动消失)systemLifetime: 10.0,// 总烟花数量totalFireworks: 20,// 烟花爆炸范围的最小值和最大值(单位:米)explosionRange: { min: 30.0, max: 100.0 },// 烟花位置的随机偏移范围(单位:米)offsetRange: { x: [-100, 100], y: [-80, 100], z: [-50, 50] },// 烟花颜色方案(4种色系循环使用)colorSchemes: [{ minRed: 0.75, green: 0.0, minBlue: 0.8, alpha: 1.0 }, // 粉紫色系{ red: 0.0, minGreen: 0.75, minBlue: 0.8, alpha: 1.0 },  // 青绿色系{ red: 0.0, green: 0.0, minBlue: 0.8, alpha: 1.0 },      // 蓝色系{ minRed: 0.75, minGreen: 0.75, blue: 0.0, alpha: 1.0 }  // 黄色系]};// 发射坐标系矩阵:将东-北-上坐标系(局部坐标系)转换到世界坐标系// 基于CONFIG.emissionCoord指定的经纬度创建const emissionMatrix = Cesium.Transforms.eastNorthUpToFixedFrame(CONFIG.emissionCoord);// 发射器初始位置(在局部坐标系中的位置:x=0, y=0, z=100米)const initialPos = new Cesium.Cartesian3(0.0, 0.0, CONFIG.initialHeight);// 设置随机数种子:确保每次运行的随机效果一致,便于调试Cesium.Math.setRandomNumberSeed(315);// 粒子纹理缓存:避免重复创建canvas元素,提升性能let particleTexture;/*** 创建粒子纹理(白色圆形)* 作用:定义粒子的外观,返回一个20x20的白色圆形画布* @returns {HTMLCanvasElement} 粒子纹理画布*/const getParticleTexture = () => {// 如果纹理已创建,直接返回缓存的纹理if (Cesium.defined(particleTexture)) return particleTexture;// 创建20x20的画布const canvas = document.createElement("canvas");canvas.width = canvas.height = 20;const ctx = canvas.getContext("2d");// 绘制白色圆形(粒子形状)ctx.arc(8, 8, 8, 0, Cesium.Math.TWO_PI, true); // 圆心(8,8),半径8ctx.fillStyle = "#fff"; // 白色填充ctx.fill();// 缓存纹理并返回particleTexture = canvas;return canvas;};/******************************************************************************* 3. 粒子系统核心逻辑(创建单个烟花)* 作用:根据位置偏移、颜色和爆发配置,创建一个完整的烟花粒子效果* @param {Cesium.Cartesian3} offset - 烟花相对于初始位置的偏移量* @param {Cesium.Color} color - 烟花的颜色* @param {Cesium.ParticleBurst[]} bursts - 粒子爆发的时间配置******************************************************************************/const createFirework = (offset, color, bursts) => {// 计算烟花的实际位置 = 初始位置 + 偏移量const fireworkPos = Cesium.Cartesian3.add(initialPos, offset, new Cesium.Cartesian3());// 创建发射器矩阵:将发射器定位到计算出的烟花位置const emitterMatrix = Cesium.Matrix4.fromTranslation(fireworkPos);// 计算坐标系转换矩阵:// localToWorld → 将粒子的局部坐标转换为世界坐标// worldToLocal → 将世界坐标转换为粒子的局部坐标(用于计算粒子受力)const localToWorld = Cesium.Matrix4.multiply(emissionMatrix, emitterMatrix, new Cesium.Matrix4());const worldToLocal = Cesium.Matrix4.inverseTransformation(localToWorld, localToWorld);// 随机生成当前烟花的爆炸范围(在配置的最小值和最大值之间)const explosionSize = Cesium.Math.randomBetween(CONFIG.explosionRange.min,CONFIG.explosionRange.max);/*** 粒子受力回调函数* 作用:控制粒子的运动,当粒子超出爆炸范围时停止运动* @param {Cesium.Particle} particle - 单个粒子对象*/const particleForce = (particle) => {// 将粒子的世界位置转换为局部位置(相对于发射器)const localPos = Cesium.Matrix4.multiplyByPoint(worldToLocal, particle.position, new Cesium.Cartesian3());// 判断粒子是否超出爆炸范围(使用平方距离比较,避免开方运算,提升性能)if (Cesium.Cartesian3.magnitudeSquared(localPos) >= explosionSize ** 2) {// 【关键修复】:创建新的Cartesian3实例,而非使用Cesium.Cartesian3.ZERO// 原因:Cesium.Cartesian3.ZERO是只读对象,直接赋值会导致"Cannot assign to read only property"错误particle.velocity = new Cesium.Cartesian3(0, 0, 0);}};// 计算粒子的生命周期:爆炸范围越大,粒子生命周期越长(0.3~1.0秒)// 归一化爆炸范围(将范围值转换为0~1之间的比例)const normalizedSize = (explosionSize - CONFIG.explosionRange.min) / (CONFIG.explosionRange.max - CONFIG.explosionRange.min);const particleLife = 0.3 + normalizedSize * 0.7;// 创建粒子系统并添加到场景scene.primitives.add(new Cesium.ParticleSystem({image: getParticleTexture(),       // 粒子纹理(白色圆形)startColor: color,                 // 粒子初始颜色endColor: color.withAlpha(0.0),    // 粒子结束颜色(透明,实现淡出效果)particleLife: particleLife,        // 单个粒子的生命周期(秒)speed: 100.0,                      // 粒子初始速度(米/秒)imageSize: CONFIG.particleSize,    // 粒子在屏幕上的大小emissionRate: 0,                   // 不持续发射粒子(仅通过burst爆发)emitter: new Cesium.SphereEmitter(0.1),  // 发射器形状(半径0.1米的球体)bursts: bursts,                    // 粒子爆发的时间和数量配置lifetime: CONFIG.systemLifetime,   // 整个粒子系统的生命周期(秒)updateCallback: particleForce,     // 粒子运动的更新回调(控制受力)modelMatrix: emissionMatrix,       // 基础坐标系矩阵emitterModelMatrix: emitterMatrix  // 发射器的位置矩阵}));};/******************************************************************************* 4. 批量创建烟花* 作用:循环创建指定数量的烟花,每个烟花有随机位置、颜色和爆发时间******************************************************************************/const createAllFireworks = () => {// 循环创建CONFIG.totalFireworks个烟花for (let i = 0; i < CONFIG.totalFireworks; i++) {// 1. 生成随机位置偏移(在配置的范围内)const offset = new Cesium.Cartesian3(Cesium.Math.randomBetween(...CONFIG.offsetRange.x),  // x轴偏移Cesium.Math.randomBetween(...CONFIG.offsetRange.y),  // y轴偏移Cesium.Math.randomBetween(...CONFIG.offsetRange.z)   // z轴偏移);// 2. 选择烟花颜色(循环使用配置的颜色方案)const colorScheme = CONFIG.colorSchemes[i % CONFIG.colorSchemes.length];const color = Cesium.Color.fromRandom({minimumRed: colorScheme.minRed,red: colorScheme.red,minimumGreen: colorScheme.minGreen,green: colorScheme.green,minimumBlue: colorScheme.minBlue,blue: colorScheme.blue,alpha: colorScheme.alpha});// 3. 配置粒子爆发(每个烟花爆发3次,时间在生命周期内随机)const bursts = Array.from({ length: 3 }, () =>new Cesium.ParticleBurst({time: Cesium.Math.nextRandomNumber() * CONFIG.systemLifetime, // 随机爆发时间minimum: CONFIG.burstCount,  // 每次爆发的最小粒子数maximum: CONFIG.burstCount   // 每次爆发的最大粒子数(固定值)}));// 4. 创建单个烟花createFirework(offset, color, bursts);}};/******************************************************************************* 5. 相机视角配置* 作用:调整相机位置和朝向,确保烟花效果在视野中居中显示******************************************************************************/const setupCamera = () => {const camera = scene.camera;// 相机相对于发射器的偏移位置(在烟花区域前方300米)const cameraOffset = new Cesium.Cartesian3(-1000.0, 0.0, 0.0);// 相机定位:基于发射坐标系的偏移位置camera.lookAtTransform(emissionMatrix, cameraOffset);// 重置相机坐标系(切换回世界坐标系)camera.lookAtTransform(Cesium.Matrix4.IDENTITY);// 计算相机看向烟花的方向向量// 【关键修复】:使用new Cesium.Cartesian3()创建新实例存储结果// 原因:避免修改原对象,防止只读对象错误const lookDir = Cesium.Cartesian3.subtract(initialPos, cameraOffset, new Cesium.Cartesian3());// 归一化方向向量(将向量长度转换为1)// 【关键修复】:使用新实例存储归一化结果,不修改原向量const normalizedDir = Cesium.Cartesian3.normalize(lookDir, new Cesium.Cartesian3());// 计算相机仰角:让相机朝上看向烟花区域const pitchAngle = Cesium.Math.PI_OVER_TWO - Math.acos(Cesium.Cartesian3.dot(normalizedDir, Cesium.Cartesian3.UNIT_Z));camera.lookUp(pitchAngle);};/******************************************************************************* 6. 初始化执行* 作用:启动整个粒子效果流程******************************************************************************/createAllFireworks();  // 批量创建所有烟花setupCamera();         // 配置相机视角,聚焦烟花区域</script>
</body></html>

扩展与优化建议

  1. 交互扩展:添加鼠标点击事件,允许用户在地球表面任意位置触发烟花效果
  2. 性能优化
    • 限制同时存在的粒子数量
    • 根据设备性能动态调整粒子数量和大小
  3. 效果增强
    • 添加粒子尾迹效果
    • 实现烟花上升阶段动画
    • 增加声音效果同步
  4. 参数控制:添加 UI 控件,允许用户实时调整粒子大小、颜色、爆炸范围等参数

通过本文介绍的方法,可以在 Cesium 中创建出各种炫酷的粒子效果,不仅限于烟花,还可以实现火焰、烟雾、喷泉等多种视觉效果。

http://www.xdnf.cn/news/20060.html

相关文章:

  • CSS Position 属性
  • Pspice仿真电路:(三十六)变压器仿真
  • 本科论文抽检档案整理:Python批量文件查找、打包、改名
  • 【uniapp】打包为h5在保留头部标题的同时配置网站标题不跟随页面路由更新
  • CVPR 2025|无类别词汇的视觉-语言模型少样本学习
  • RikkaHub:安卓原生AI聊天新体验
  • 【设计模式】UML 基础教程总结(软件设计师考试重点)
  • 十一、标准化和软件知识产权基础知识
  • 认识 Flutter
  • 告别 OpenAI SDK:如何使用 Python requests 库调用大模型 API(例如百度的ernie-4.5-turbo)
  • 【Qt开发】按钮类控件(三)-> QCheckBox
  • 【完整源码+数据集+部署教程】手袋类型检测系统源码和数据集:改进yolo11-AFPN-P345
  • 前端开发,同源策略
  • 【Linux】Linux进程状态和僵尸进程:一篇看懂“进程在忙啥”
  • 基于OpenGL封装摄像机类:视图矩阵与透视矩阵的实现
  • 如何下载B站视频,去水印,翻译字幕
  • .Net程序员就业现状以及学习路线图(四)
  • 创建线程有哪几种方式
  • 【数字孪生核心技术】数字孪生有哪些核心技术?
  • Kubernetes(四):Service
  • HyperWorks许可服务器设置
  • 企业微信AI怎么用?食品集团靠它砍掉50%低效操作,答案就是选对企业微信服务商
  • ZeroMQ 编译 项目使用流程文档
  • Android 生命周期函数调用原理
  • 《计算机网络安全》实验报告一 现代网络安全挑战 拒绝服务与分布式拒绝服务攻击的演变与防御策略(3)
  • 2025年数学建模国赛参考论文发布
  • 从碎片化到一体化:Java分布式缓存的“三级跳”实战
  • Spring Security 深度学习(六): RESTful API 安全与 JWT
  • 服务器IP暴露被攻击了怎么办?
  • 微算法科技 (NASDAQ:MLGO)利用量子密钥分发QKD技术,增强区块链系统的抗攻击能力