Unity 2022实战用Mesh和Shader打造可交互的3D人群热力图附完整C#源码在数字孪生和智慧城市建设的浪潮中人群动态可视化成为城市规划、商业分析和游戏设计的关键技术。传统二维热力图难以表现立体空间中的密度分布而基于Unity的三维热力图技术则能直观呈现商场、地铁站等复杂场景中的人群流动。本文将手把手教你实现一套实时动态更新的交互式热力图系统核心代码可直接用于你的下一个城市模拟或MMO游戏项目。1. 三维热力图的底层架构设计三维热力图本质上是由Mesh网格和Shader着色器构成的视觉化系统。与传统静态网格不同动态热力图需要解决三个核心问题实时顶点计算如何根据NPC位置动态更新网格顶点高度高效着色如何通过Shader实现平滑的颜色过渡性能优化如何支持大规模人群场景的实时渲染我们采用CPU计算密度GPU渲染的混合架构// 系统架构伪代码 void Update() { if (frameCount % updateInterval 0) { Parallel.ForEach(npcList, npc { UpdateHeatValue(npc.position); }); UpdateMeshVertices(); } }这种设计将耗时的密度计算分散到多帧执行保证主线程流畅。下面我们拆解具体实现。2. 动态网格生成与更新2.1 基础网格创建首先创建可变形的基础网格这段代码生成一个XZ平面的矩形网格void CreateBaseMesh(int width, int depth) { Vector3[] vertices new Vector3[(width 1) * (depth 1)]; Vector2[] uv new Vector2[vertices.Length]; for (int z 0, i 0; z depth; z) { for (int x 0; x width; x, i) { vertices[i] new Vector3(x, 0, z); uv[i] new Vector2((float)x/width, (float)z/depth); } } // 三角形索引计算... mesh.vertices vertices; mesh.uv uv; mesh.RecalculateNormals(); }提示UV坐标要预先计算好后续Shader中会用它做热力值插值2.2 实时顶点更新算法采用高斯衰减模型计算每个顶点受NPC影响的程度void UpdateHeatValues(Vector3 npcPosition) { for (int i 0; i vertices.Length; i) { float distance Vector3.Distance(vertices[i], npcPosition); float influence Mathf.Exp(-distance * distance / (2 * radius * radius)); heatValues[i] Mathf.Max(heatValues[i], influence * intensity); } }关键参数说明参数名类型说明典型值radiusfloat影响半径3.0-5.0intensityfloat影响强度0.5-1.5decayRatefloat衰减速度0.9-0.993. Shader着色器实现3.1 热力颜色映射在片段着色器中实现五段式颜色渐变fixed4 frag (v2f i) : SV_Target { float heat tex2D(_HeatMap, i.uv).r; fixed4 color _CoolColor; color lerp(color, _WarmColor, smoothstep(0.2, 0.4, heat)); color lerp(color, _HotColor, smoothstep(0.4, 0.6, heat)); color lerp(color, _VeryHotColor, smoothstep(0.6, 0.8, heat)); color lerp(color, _ExtremeColor, smoothstep(0.8, 1.0, heat)); return color * _Intensity; }3.2 顶点动画增强在顶点着色器中添加高度位移v2f vert (appdata v) { v2f o; float heat tex2Dlod(_HeatMap, float4(v.uv,0,0)).r; v.vertex.y heat * _HeightScale; o.vertex UnityObjectToClipPos(v.vertex); o.uv v.uv; return o; }4. 性能优化实战技巧4.1 计算分帧策略将密集的NPC影响计算分散到多帧IEnumerator UpdateHeatMapCoroutine() { while (true) { for (int i 0; i batchSize; i) { int index (currentIndex i) % npcList.Count; UpdateSingleNPC(npcList[index]); } currentIndex batchSize; UpdateMesh(); yield return null; } }4.2 多级LOD优化根据摄像机距离切换网格精度距离网格密度更新频率10m高 (1m)每帧10-30m中 (2m)每3帧30m低 (5m)每5帧5. 商场人流模拟案例以商场热力图为例我们需要导入商场3D模型并设置导航网格配置不同店铺的吸引力参数实现游客行为树40%概率随机游走30%概率前往热门店铺20%概率休息10%概率去洗手间关键店铺配置示例[System.Serializable] public class ShopConfig { public string name; [Range(0,2)] public float attraction; public Vector3[] waitingSpots; } // 在UpdateHeatValues中增加店铺吸引力 heatValues[i] shopConfig.attraction * shopProximity;6. 进阶交互功能6.1 点击查询热力值通过射线检测实现交互void HandleClick() { Ray ray Camera.main.ScreenPointToRay(Input.mousePosition); if (Physics.Raycast(ray, out RaycastHit hit)) { Vector2 uv hit.textureCoord; float heat GetHeatValueAtUV(uv); ShowTooltip(hit.point, heat); } }6.2 数据导出分析将热力数据导出为CSVvoid ExportHeatData() { StringBuilder sb new StringBuilder(X,Y,Heat\n); for (int z 0; z depth; z) { for (int x 0; x width; x) { float heat heatValues[z * (width1) x]; sb.AppendLine(${x},{z},{heat:F2}); } } System.IO.File.WriteAllText(HeatMapData.csv, sb.ToString()); }在最近的地铁站项目中使用这套系统时我们发现将更新间隔设置为0.3秒、影响半径设为4米时在2000个NPC的场景下仍能保持60fps。但要注意避免频繁的Mesh更新建议使用Mesh.SetVertices而非直接赋值vertices数组。