尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
Cesium动态绘制点线面技术解析与实践
1. Cesium动态绘制点线面技术解析Cesium作为当前最强大的开源WebGIS引擎之一其动态绘制能力在实际项目中具有广泛应用价值。动态绘制不同于静态数据加载它允许用户通过交互方式实时创建和修改地理要素这种能力在城市规划、应急指挥等场景中尤为重要。1.1 核心实现原理动态绘制的核心技术在于Cesium的CallbackProperty机制。与常规属性赋值不同CallbackProperty通过回调函数实现属性的动态计算。当场景需要渲染时系统会自动调用回调函数获取最新属性值从而实现要素的实时更新。以绘制多边形为例传统静态绘制的坐标是固定值positions: [x1,y1,z1, x2,y2,z2, ...]而动态绘制则采用hierarchy: new Cesium.CallbackProperty(function() { return dynamicPositionsArray }, false)关键参数说明第二个参数false表示不进行深度比较优化确保每次渲染都重新计算。在需要高频更新的场景应设为false性能敏感但更新频率低的场景可设为true。1.2 完整绘制流程实现一个完整的动态绘制流程包含三个阶段初始化阶段this.handler new Cesium.ScreenSpaceEventHandler(viewer.canvas); this.positions []; this.polygon null;交互处理阶段核心事件绑定// 左键点击添加控制点 handler.setInputAction(LEFT_CLICK, function(evt) { const cartesian getCartesian3FromPX(evt.position); positions.push(cartesian); }); // 鼠标移动实时更新 handler.setInputAction(MOUSE_MOVE, function(evt) { if(positions.length 2) return; const lastPos positions[positions.length-1]; positions[positions.length-1] getCartesian3FromPX(evt.endPosition); }); // 右键结束绘制 handler.setInputAction(RIGHT_CLICK, function(evt) { handler.destroy(); commitPolygon(); });地形适配处理function getCartesian3FromPX(px) { // 优先尝试从3D模型获取精确坐标 if(scene.pickPositionSupported) { const cartesian scene.pickPosition(px); if(Cesium.defined(cartesian)) return cartesian; } // 普通地形采样 const ray camera.getPickRay(px); return scene.globe.pick(ray, scene); }2. 高级功能实现与优化2.1 编辑功能实现基础绘制只是开始完整的业务场景还需要编辑功能。实现要素编辑需要解决三个技术难点要素拾取viewer.screenSpaceEventHandler.setInputAction( LEFT_CLICK, function(evt) { const picked viewer.scene.pick(evt.position); if(picked picked.id.editMode) { enterEditMode(picked.id); } } );控制点交互function createControlPoint(position, index) { return viewer.entities.add({ position: position, point: { pixelSize: 12, color: Cesium.Color.RED }, dragHandler: new Cesium.CallbackProperty(function() { return updatePositionOnDrag(index); }) }); }拓扑维护function updatePolygonHierarchy() { const movingIndex getDraggingIndex(); positions[movingIndex] controlPoints[movingIndex].position.getValue(); polygon.polygon.hierarchy new Cesium.CallbackProperty(() { return new Cesium.PolygonHierarchy(positions); }, false); }2.2 性能优化方案当处理大规模动态要素时性能优化至关重要实例化渲染const instances []; entities.forEach(entity { instances.push(new Cesium.GeometryInstance({ geometry: new Cesium.PolygonGeometry({ hierarchy: entity.hierarchy }), attributes: { color: Cesium.ColorGeometryInstanceAttribute.fromColor( entity.color ) } })); }); viewer.scene.primitives.add(new Cesium.Primitive({ geometryInstances: instances, appearance: new Cesium.PerInstanceColorAppearance() }));LOD控制策略function computeLodLevel(distance) { if(distance 10000) return { updateRate: 2, detail: 0.5 }; if(distance 5000) return { updateRate: 1, detail: 0.8 }; return { updateRate: 0.5, detail: 1.0 }; } viewer.scene.preUpdate.addEventListener(function() { const cameraPos viewer.camera.position; entities.forEach(entity { const distance Cesium.Cartesian3.distance( cameraPos, entity.position.getValue() ); const lod computeLodLevel(distance); entity.updateRate lod.updateRate; entity.detailLevel lod.detail; }); });WebWorker异步计算// 主线程 const worker new Worker(drawWorker.js); worker.postMessage({ type: init, positions: initialPositions }); // Worker线程 onmessage function(e) { if(e.data.type update) { const newPositions computePositions(e.data.input); postMessage({ positions: newPositions }); } };3. 典型应用场景剖析3.1 智慧城市应用在城市数字孪生系统中动态绘制技术支撑着多种业务场景规划方案比选实时绘制多个规划方案轮廓自动计算各方案的关键指标占地面积、容积率等支持方案叠加对比分析function calculatePlotRatio(polygon) { const area Cesium.PolygonGeometry.computeArea( polygon.hierarchy.getValue().positions ); const height getAverageHeight(polygon); return (height * buildingFootprint) / area; }应急演练系统动态划定危险区域实时规划疏散路线模拟灾害影响范围扩散function simulateHazardSpread(center, radius, duration) { const startTime Cesium.JulianDate.now(); const property new Cesium.CallbackProperty(function(time) { const elapsed Cesium.JulianDate.secondsDifference(time, startTime); const currentRadius radius * (elapsed / duration); return createCirclePositions(center, currentRadius); }, false); return viewer.entities.add({ polygon: { hierarchy: property, material: new Cesium.ColorMaterialProperty( new Cesium.CallbackProperty(function() { const alpha 0.7 * (1 - elapsed/duration); return Cesium.Color.RED.withAlpha(alpha); }, false) ) } }); }3.2 军事指挥系统军事领域对动态绘制有更严苛的要求战场态势标绘支持NATO标准军事符号体系实时更新部队行进路线动态显示战场警戒区域class MilitarySymbol { constructor(type, position) { this.entity viewer.entities.add({ position: position, billboard: { image: symbols/${type}.png, width: 32, height: 32 }, path: { resolution: 1, material: new Cesium.PolylineGlowMaterialProperty({ glowPower: 0.2, color: Cesium.Color.YELLOW }), width: 10 } }); } updatePath(positions) { this.entity.path.show true; this.entity.path.positions new Cesium.CallbackProperty(() { return interpolatePositions(positions); }, false); } }三维电子沙盘动态划定演习区域实时标注战术要点支持多层级指挥标绘function createTacticalLayer() { const tacticalGroup new Cesium.EntityCluster({ enabled: true, pixelRange: 100, minimumClusterSize: 5, clusterBillboards: true, clusterLabels: true, clusterPoints: true }); viewer.dataSources.add(new Cesium.CustomDataSource({ name: Tactical, clustering: tacticalGroup, entities: tacticalEntities })); }4. 常见问题解决方案4.1 绘制精度问题问题现象地形起伏区域绘制偏移模型表面绘制不准确移动端触控精度不足解决方案高精度拾取算法function highPrecisionPick(px) { const scene viewer.scene; const maxSample 5; let bestCartesian; for(let i0; imaxSample; i) { const offsetPx new Cesium.Cartesian2( px.x Math.random()*2-1, px.y Math.random()*2-1 ); const cartesian scene.pickPosition(offsetPx); if(cartesian (!bestCartesian || cartesian.z bestCartesian.z)) { bestCartesian cartesian; } } return bestCartesian; }触摸屏优化策略function setupTouchDrawing() { let lastPos; const touchHandler new Cesium.ScreenSpaceEventHandler(viewer.canvas); touchHandler.setInputAction(Cesium.ScreenSpaceEventType.TOUCH_MOVE, function(evt) { const currentPos getCartesian3FromPX(evt.endPosition); if(lastPos Cesium.Cartesian3.distance(lastPos, currentPos) 50) { interpolatePoints(lastPos, currentPos).forEach(pos { positions.push(pos); }); } lastPos currentPos; } ); }4.2 性能优化方案内存泄漏预防class DrawingManager { constructor() { this._entities new Cesium.EntityCollection(); this._eventHandlers []; this._properties []; } destroy() { this._eventHandlers.forEach(h h.destroy()); viewer.entities.remove(this._entities); this._properties.forEach(p p.destroy()); } createProperty() { const prop new Cesium.CallbackProperty(() {...}, false); this._properties.push(prop); return prop; } }渲染性能监控function monitorPerformance() { const stats new Cesium.PerformanceDisplay({ scene: viewer.scene }); viewer.scene.postRender.addEventListener(function() { const frameState viewer.scene.frameState; console.log(Primitives: ${frameState.primitivesLength}, Commands: ${frameState.commandsLength}); }); }4.3 坐标系转换问题常用转换工具集class CoordinateConverter { static toCartographic(cartesian) { const carto Cesium.Cartographic.fromCartesian(cartesian); return { longitude: Cesium.Math.toDegrees(carto.longitude), latitude: Cesium.Math.toDegrees(carto.latitude), height: carto.height }; } static toCartesian3(lon, lat, height) { return Cesium.Cartesian3.fromDegrees(lon, lat, height); } static transformCRS(sourcePos, sourceCRS, targetCRS) { const transformMatrix Cesium.Matrix4.fromArray( getCRSTransformParams(sourceCRS, targetCRS) ); return Cesium.Matrix4.multiplyByPoint( transformMatrix, sourcePos, new Cesium.Cartesian3() ); } }WGS84与局部坐标转换function setupLocalCoordinateSystem(origin) { const fixedFrame Cesium.Transforms.eastNorthUpToFixedFrame(origin); const inverseTransform Cesium.Matrix4.inverse( fixedFrame, new Cesium.Matrix4() ); return { toLocal: function(cartesian) { return Cesium.Matrix4.multiplyByPoint( inverseTransform, cartesian, new Cesium.Cartesian3() ); }, toGlobal: function(localPos) { return Cesium.Matrix4.multiplyByPoint( fixedFrame, localPos, new Cesium.Cartesian3() ); } }; }5. 前沿技术融合5.1 与WebGL深度集成通过直接访问WebGL上下文可以实现更高效的渲染class CustomPrimitive { constructor(options) { this._command new Cesium.DrawCommand({ boundingVolume: options.bbox, modelMatrix: options.modelMatrix, primitiveType: Cesium.PrimitiveType.TRIANGLES, vertexArray: createVertexArray(options), shaderProgram: createShaderProgram(options), uniformMap: createUniformMap(options) }); } update(frameState) { frameState.commandList.push(this._command); } } viewer.scene.primitives.add(new Cesium.Primitive({ geometryInstances: instances, appearance: new Cesium.Appearance({ vertexShaderSource: customVS, fragmentShaderSource: customFS, renderState: { depthTest: { enabled: true }, blending: Cesium.BlendingState.ALPHA_BLEND } }) }));5.2 机器学习辅助绘制集成TensorFlow.js实现智能绘制async function setupSmartDrawing() { const model await tf.loadLayersModel(path/to/model.json); const canvas document.createElement(canvas); viewer.screenSpaceEventHandler.setInputAction( Cesium.ScreenSpaceEventType.MOUSE_MOVE, async function(evt) { const screenshot viewer.scene.canvas.toDataURL(); const img await loadImage(screenshot); canvas.getContext(2d).drawImage(img, 0, 0, 64, 64); const tensor tf.browser.fromPixels(canvas) .resizeNearestNeighbor([64, 64]) .toFloat() .expandDims(); const prediction model.predict(tensor); const suggestedPositions await processPrediction(prediction); updateGuideLines(suggestedPositions); } ); }5.3 三维空间分析扩展基于动态绘制实现高级空间分析function createVisibilityAnalysis(startPoint) { const viewer this.viewer; const handler new Cesium.ScreenSpaceEventHandler(viewer.canvas); let endPoint; handler.setInputAction(Cesium.ScreenSpaceEventType.LEFT_CLICK, function(evt) { endPoint getCartesian3FromPX(evt.position); const visibility computeVisibility(startPoint, endPoint); viewer.entities.add({ polyline: { positions: [startPoint, endPoint], width: 3, material: new Cesium.PolylineDashMaterialProperty({ color: visibility.visible ? Cesium.Color.GREEN : Cesium.Color.RED }) }, label: { text: 可视性: ${visibility.visible ? 通视 : 遮挡}, distanceDisplayCondition: new Cesium.DistanceDisplayCondition(0, 1000) } }); }, Cesium.ScreenSpaceEventType.LEFT_CLICK); } function computeVisibility(start, end) { const scene viewer.scene; const samples 100; let visible true; for(let i0; isamples; i) { const t i/samples; const samplePos Cesium.Cartesian3.lerp( start, end, t, new Cesium.Cartesian3() ); const terrainPos scene.globe.pick( new Cesium.Ray(samplePos, Cesium.Cartesian3.UNIT_Z), scene ); if(terrainPos Cesium.Cartesian3.distance(samplePos, terrainPos) 10) { visible false; break; } } return { visible: visible, obstructionPos: visible ? null : terrainPos }; }6. 工程化实践建议6.1 模块化设计推荐采用分层架构设计src/ ├── core/ │ ├── DrawingManager.js # 核心绘制逻辑 │ ├── CoordinateService.js # 坐标转换服务 │ └── EventDispatcher.js # 事件管理 ├── modules/ │ ├── PolygonTool/ # 多边形绘制模块 │ ├── PolylineTool/ # 折线绘制模块 │ └── MeasurementTool/ # 测量工具模块 ├── utils/ │ ├── GeometryUtils.js # 几何计算工具 │ ├── StylePresets.js # 样式预设 │ └── WorkerPool.js # WebWorker管理 └── plugins/ ├── SnappingPlugin.js # 吸附功能插件 └── UndoRedoPlugin.js # 撤销重做插件6.2 状态管理方案复杂交互场景建议采用状态机模式class DrawingStateMachine { constructor() { this._state IDLE; this._states { IDLE: { onLeftClick: ADD_POINT, onRightClick: null }, ADD_POINT: { onLeftClick: ADD_POINT, onRightClick: COMPLETE, onMouseMove: PREVIEW }, PREVIEW: { onLeftClick: ADD_POINT, onRightClick: COMPLETE } }; } transition(eventType) { const currentState this._states[this._state]; const newState currentState[on${eventType}]; if(newState) { this._state newState; this._emitStateChange(); } } _emitStateChange() { // 触发状态变更事件 } }6.3 质量保障体系建议建立完整的测试方案describe(DrawingManager, () { let viewer; beforeAll(() { viewer new Cesium.Viewer(testContainer); }); it(should correctly add points, () { const manager new DrawingManager(viewer); manager.handleLeftClick({position: {x: 100, y: 100}}); expect(manager.points.length).toBe(1); }); it(should maintain topology when editing, () { const polygon createTestPolygon(); const editor new PolygonEditor(polygon); editor.moveControlPoint(0, newPosition); expect(editor.getArea()).toBeCloseTo(expectedArea, 2); }); }); // WebWorker测试方案 test(Position calculation in worker, async () { const worker new Worker(drawWorker.js); const testData {type: calc, points: [...]}; worker.postMessage(testData); const result await new Promise(resolve { worker.onmessage e resolve(e.data); }); expect(result.positions).toHaveLength(expectedCount); });7. 跨平台适配方案7.1 移动端优化策略针对触控设备的特殊处理function setupTouchDrawing() { const touchHandler new Cesium.ScreenSpaceEventHandler(viewer.canvas); let lastTapTime 0; // 双击结束绘制 touchHandler.setInputAction(Cesium.ScreenSpaceEventType.LEFT_DOUBLE_CLICK, () { finishDrawing(); }); // 长按删除点 touchHandler.setInputAction(Cesium.ScreenSpaceEventType.LEFT_DOWN, (evt) { this._tapTimer setTimeout(() { deletePointUnderPosition(evt.position); }, 800); }); touchHandler.setInputAction(Cesium.ScreenSpaceEventType.LEFT_UP, () { clearTimeout(this._tapTimer); }); // 双指平移 let lastDistance; touchHandler.setInputAction(Cesium.ScreenSpaceEventType.PINCH_START, (evt) { lastDistance calculateFingerDistance(evt); }); touchHandler.setInputAction(Cesium.ScreenSpaceEventType.PINCH_MOVE, (evt) { const currentDistance calculateFingerDistance(evt); const delta currentDistance - lastDistance; viewer.camera.zoomIn(delta * 10); lastDistance currentDistance; }); } function calculateFingerDistance(pinchEvent) { const pos1 pinchEvent.position1; const pos2 pinchEvent.position2; return Math.sqrt( Math.pow(pos2.x - pos1.x, 2) Math.pow(pos2.y - pos1.y, 2) ); }7.2 离线环境支持实现完整的离线解决方案资源缓存方案class AssetCache { constructor() { this._database new PouchDB(cesium_assets); this._attachments new PouchDB(cesium_attachments); } async cacheTerrain(tile) { const key ${tile.level}_${tile.x}_${tile.y}; const terrainData await fetchTerrainData(tile); await this._database.put({ _id: key, type: terrain, timestamp: Date.now(), data: terrainData }); } async getCachedTerrain(tile) { const key ${tile.level}_${tile.x}_${tile.y}; try { const doc await this._database.get(key); return doc.data; } catch { return null; } } }离线绘制数据同步class OfflineSyncManager { constructor() { this._localDB new PouchDB(drawings); this._remoteDB new PouchDB(http://remote/db); this._syncHandler null; } startSync() { this._syncHandler this._localDB.sync(this._remoteDB, { live: true, retry: true }).on(change, function(change) { console.log(Sync event:, change); }).on(error, function(err) { console.error(Sync error:, err); }); } async saveDrawing(drawing) { const doc { _id: drawing_${Date.now()}, type: feature, geometry: drawing.toGeoJSON(), properties: drawing.attributes, createdAt: new Date().toISOString() }; await this._localDB.put(doc); return doc._id; } }8. 安全与权限控制8.1 要素级权限管理class SecurityManager { constructor(aclRules) { this._rules aclRules; this._currentRole guest; } setRole(role) { this._currentRole role; } filterEntities(entities) { return entities.filter(entity { const requiredLevel this._rules[entity.type] || 0; return this._currentRole.level requiredLevel; }); } checkEditPermission(entity) { const owner entity.properties.owner; const requiredLevel this._rules[entity.type] || 0; return this._currentRole.level requiredLevel (owner this._currentRole.userId || this._currentRole.isAdmin); } }8.2 数据加密方案class CryptoHandler { constructor(key) { this._key crypto.getRandomValues(new Uint8Array(32)); } async encryptGeometry(positions) { const iv crypto.getRandomValues(new Uint8Array(12)); const encoded new TextEncoder().encode( JSON.stringify(positions) ); const ciphertext await crypto.subtle.encrypt( { name: AES-GCM, iv }, this._key, encoded ); return { iv: Array.from(iv), data: Array.from(new Uint8Array(ciphertext)) }; } async decryptGeometry(encrypted) { const iv new Uint8Array(encrypted.iv); const data new Uint8Array(encrypted.data); const decrypted await crypto.subtle.decrypt( { name: AES-GCM, iv }, this._key, data ); return JSON.parse( new TextDecoder().decode(decrypted) ); } }9. 性能监控与调优9.1 实时性能指标class PerformanceMonitor { constructor() { this._stats { fps: 0, primitiveCount: 0, drawCommands: 0, memoryUsage: 0 }; this._samples []; this._maxSamples 60; } update(frameState) { // 计算FPS const now performance.now(); this._samples.push(now); if(this._samples.length this._maxSamples) { this._samples.shift(); } if(this._samples.length 1) { const duration (now - this._samples[0]) / 1000; this._stats.fps (this._samples.length / duration).toFixed(1); } // 获取场景指标 this._stats.primitiveCount frameState.primitivesLength; this._stats.drawCommands frameState.commandsLength; // 内存监测 if(window.performance performance.memory) { this._stats.memoryUsage performance.memory.usedJSHeapSize / 1048576; } return this._stats; } getThresholds() { return { fps: { warn: 30, critical: 15 }, primitives: { warn: 5000, critical: 10000 }, memory: { warn: 500, critical: 1000 } // MB }; } }9.2 自适应降级策略class AdaptiveRenderer { constructor(viewer) { this._viewer viewer; this._currentQuality high; this._qualityLevels { high: { terrainDetail: 1.0, shadowQuality: 1.0, fxaa: true, msaa: 4 }, medium: { terrainDetail: 0.7, shadowQuality: 0.5, fxaa: true, msaa: 2 }, low: { terrainDetail: 0.5, shadowQuality: 0.3, fxaa: false, msaa: 0 } }; } adjustQualityBasedOnFPS(fps) { if(fps 15 this._currentQuality ! low) { this.setQuality(low); } else if(fps 25 this._currentQuality high) { this.setQuality(medium); } else if(fps 40 this._currentQuality ! high) { this.setQuality(high); } } setQuality(level) { const config this._qualityLevels[level]; this._viewer.scene.globe.detail config.terrainDetail; this._viewer.scene.shadowMap.maximumDistance 5000 * config.shadowQuality; this._viewer.scene.postProcessStages.fxaa.enabled config.fxaa; this._viewer.scene.msaaSamples config.msaa; this._currentQuality level; } }10. 项目实战经验10.1 复杂多边形处理处理带岛洞的多边形时需要特殊处理function createComplexPolygon(outerRing, innerRings) { const hierarchy new Cesium.PolygonHierarchy(outerRing); innerRings.forEach(ring { hierarchy.holes.push(new Cesium.PolygonHierarchy(ring)); }); return viewer.entities.add({ polygon: { hierarchy: hierarchy, material: Cesium.Color.GREEN.withAlpha(0.5), classificationType: Cesium.ClassificationType.TERRAIN } }); } // 动态更新示例 function updateComplexPolygon(entity, newOuter, newHoles) { entity.polygon.hierarchy new Cesium.CallbackProperty(() { const hierarchy new Cesium.PolygonHierarchy(newOuter); newHoles.forEach(hole { hierarchy.holes.push(new Cesium.PolygonHierarchy(hole)); }); return hierarchy; }, false); }10.2 海量数据绘制优化当需要处理10万要素时的优化方案空间索引构建class QuadTreeIndex { constructor(bounds, maxDepth 4) { this.root new QuadNode(bounds, 0, maxDepth); } insert(position, data) { this.root.insert(position, data); } queryRange(range) { const results []; this.root.queryRange(range, results); return results; } } class QuadNode { constructor(bounds, depth, maxDepth) { this.bounds bounds; this.children null; this.items []; this.depth depth; this.maxDepth maxDepth; } insert(position, data) { if(!this.bounds.contains(position)) return false; if(this.depth this.maxDepth || this.items.length 10) { this.items.push({position, data}); return true; } if(!this.children) this._subdivide(); return this.children.some(child child.insert(position, data)); } _subdivide() { const { west, south, east, north } this.bounds; const centerX (west east) / 2; const centerY (south north) / 2; this.children [ new QuadNode(new Cesium.Rectangle(west, south, centerX, centerY), this.depth 1, this.maxDepth), new QuadNode(new Cesium.Rectangle(centerX, south, east, centerY), this.depth 1, this.maxDepth), new QuadNode(new Cesium.Rectangle(west, centerY, centerX, north), this.depth 1, this.maxDepth), new QuadNode(new Cesium.Rectangle(centerX, centerY, east, north), this.depth 1, this.maxDepth) ]; // 重新分配现有项 const oldItems this.items; this.items []; oldItems.forEach(item { this.insert(item.position, item.data); }); } }视域裁剪优化function isInView(cartesian) { const camera viewer.camera; const position camera.position; const direction camera.direction; // 粗略视锥剔除 if(!camera.viewRectangle.contains( Cesium.Cartographic.fromCartesian(cartesian) )) return false; // 精确距离检测 const distance Cesium.Cartesian3.distance(position, cartesian); if(distance camera.frustum.far) return false; // 方向检测 const toPoint Cesium.Cartesian3.subtract( cartesian, position, new Cesium.Cartesian3() ); Cesium.Cartesian3.normalize(toPoint, toPoint); const dot Cesium.Cartesian3.dot(direction, toPoint); const fov camera.frustum.fov; return dot Math.cos(fov / 2); }10.3 跨平台渲染一致性问题确保不同设备上渲染效果一致class RenderConsistency { static checkWebGLFeatures() { const canvas document.createElement(canvas); const gl canvas.getContext(webgl) || canvas.getContext(experimental-webgl); if(!gl) { throw new Error(WebGL not supported); } const requiredExtensions [ OES_texture_float, WEBGL_depth_texture, EXT_frag_depth ]; const missing requiredExtensions.filter(ext !gl.getExtension(ext) ); if(missing.length 0) { console.warn(Missing extensions:, missing); return false; } return true; } static applyFallbacks() { const scene viewer.scene; // 浮点纹理回退 if(!scene.context.floatingPointTexture) { console.log(Using RGBA8 precision fallback); scene.highDynamicRange false; } // 抗锯齿回退 if(scene.context.antialias false) { console.log(Using FXAA instead of MSAA); scene.postProcessStages.fxaa.enabled true; } } }
RELATED

相关推荐

为什么 JWT 明明主打“无状态、不用查库”,实际业务咋还得存个 Redis?

为什么 JWT 明明主打“无状态、不用查库”,实际业务咋还得存个 Redis?

做业务开发有时就是有些矛盾的地方,比如 JWT 都吹它无状态,又不用查数据库性能还好,结果实际开发转头 token 就存 Redis 里了。JWT(JSON Web Token)自带用户信息和签名,服务端不需要存任何东西,…

📅 2026/9/11 22:51:29
Medium读者行为分析:从阅读时长到标题词数的数据驱动洞察

Medium读者行为分析:从阅读时长到标题词数的数据驱动洞察

1. 项目概述:为什么一篇 Medium 文章能“说话”?你有没有想过,当你在 Medium 上点开一篇文章,读完、点赞、甚至转发——这一连串动作背后,其实藏着一整套可被量化的读者行为逻辑?而作者写标题时多加一个形容…

📅 2026/7/21 7:33:27
OpenAI智能体框架实战:构建企业级对话系统的架构解析

OpenAI智能体框架实战:构建企业级对话系统的架构解析

如果你正在构建一个需要处理海量用户对话的系统,可能会面临这样的困境:传统客服系统响应慢、成本高,而简单的聊天机器人又无法理解复杂意图。Cars24 作为印度最大的二手车交易平台,每月需要处理超过100万分钟的客户对话&#xff0…

📅 2026/7/21 0:44:09
MORE NEWS

更多资讯

📰

Unity消消乐开发:菜单界面与游戏界面场景解耦实战

简介:这是一份基于Unity 2020.2.23开发的方块消消乐完整项目,面向Unity初学者以及需要完成虚拟现实课程期末作业的学生,资源同时包含菜单界面与游戏界面,实现了基本消除、界面动画、手势识别、消除判定、连续消除判定、分数统计与…

📰

yolov5垃圾分类识别检测:数据、训练到部署全流程实战

简介:这份课程设计资源以YOLOv5为基础,实现垃圾分类识别检测,面向需要完成高质量课程设计或期末大作业的高校学生,也适合入门目标检测的开发者参考。项目已获导师指导并通过,属于97分的高分作品,附带的源码…

📰

玩Steam游戏用什么开黑语音软件?2026兼容全平台的语音工具盘点

Steam 玩家的语音需求,和手游党不太一样。今天玩 CS2 要 5 人语音,明天开坑永劫无间要 3 人小队,后天又回 Apex 拉个四人车——游戏换来换去,语音软件最好一个就够。再加上 Steam 好友分布散,有人用 YY、有人用 QQ 语音…

📰

硕士论文高效写作四步法:从选题到终稿全流程解析

1. 论文写作痛点与破局思路第一次面对3000字硕士论文写作时,我和大多数同学一样陷入焦虑:选题方向模糊、文献梳理耗时、写作效率低下、格式反复修改。直到研二时导师分享的"四步法"彻底改变了我的学术写作方式——这个方法帮助我在两周内完成了…

📰

Python机器学习股市情感分析全流程:从词向量到量化因子

简介:面向金融数据分析与机器学习学习者,该资源提供了一套完整的股市情感分析实践方案。项目以股评文本和上证指数为数据源,通过Python完成情感分析、情绪指标构建,并借助机器学习模型揭示看涨情绪与股市走势之间的关系&#xff0…

📰

CopilotKit + A2A + A2UI 实战:用 AG-UI 协议构建可动态渲染 UI 的餐厅预订 Agent

CopilotKit A2A A2UI 实战:用 AG-UI 协议构建可动态渲染 UI 的餐厅预订 Agent 【免费下载链接】CopilotKit The Frontend Stack for Agents & Generative UI. React, Angular, Mobile, Slack, and more. Makers of the AG-UI Protocol 项目地址: https://gi…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬