前言

上一篇博客中我门实现了一个平面的温度图,但是最近在论坛中碰到不少同学需要将这个温度图改为三维的,其实改为三维的并不难,因为上一篇中HeatMap是用Mesh绘制的,因此我们只要给网格一个高度值就可以变成三维的温度图,但是为了效果我们准备增加一些单位和网格作为基础,接下来我们看看如何实现吧。(这里的网格和之前UGUI自定义组件中网格并不一样,因为之前的是纯2D的网格绘制,而这里我们改为三维网格绘制,其实也很简单)

实现效果

主要内容

  • 更改HeatMap组件,以适应3D
  • 绘制基础网格
  • 网格光滑的问题

详细设计

更改HeatMap组件,以适应3D

关于温度图的具体详细绘制,请查看另一篇博文。

Unity网格编程篇(三) 温度图、热力图

如果你不明白如何利用代码来绘制网格,参见我的其他博文,非常详细的讲解了网格绘制中的主要内容点。

Unity网格编程篇(二) 非常详细的Mesh编程入门文章

Unity Shader(一) Lowpoly动态低多边形 (QQ登录界面低边动画)

因为HeatMap是在被绕x轴旋转90度后绘制的,所以当我们要给温度图添加高度时,只需要在计算顶点位置时,根据高低温度限制,计算出Z轴的值就可以了。

for ( int j = 0; j < vertical; j++)
{for (int i = 0; i < horizontal; i++){                     float temperature = this.temperature[j, i];// 利用温度值计算顶点颜色值colors[horizontal * j + i] = CalcColor(temperature);Vector3 vertex = origin + new Vector3(i * perWidth, j * perHeight,// 只要利用GetHeightByTemperature方法计算出z轴高度值GetHeightByTemperature(temperature));vertices[horizontal * j + i] = vertex;uvs[horizontal * j + i] = new Vector2(0 , 1) + new Vector2(1 / horizontal * i , 1 / vertical * j);}
}

我的案例中以20度为温度下限,50度为上限,这里可根据实际需求在Inspector配置或直接修改代码

private float GetHeightByTemperature( float temperature )
{return  (0.5f -(temperature - MinTemperature) / (MaxTemperature - MinTemperature) );
}

同样我们要根据温度值计算出顶点的颜色

private Color CalcColor( float temperature )
{int count = (int)temperature / 10;float temp = ( temperature % 10 ) / 10;Color[] colors = GetColors(count);Color from = colors[0];Color to = colors[1];Color offset = to - from;return from + offset * temp;
}// TemperatureColors 实在Inspector面板中配置的颜色区间private Color[] GetColors( int index )
{Color startColor = Color.blue, endColor = Color.blue;startColor = TemperatureColors[index];endColor = TemperatureColors[index+1];return new Color[] { startColor , endColor };
}

如此我们已经解决了温度图三维化的问题,接下来我们处理网格的绘制

绘制基础网格

实现网格绘制

通常我们使用LineRenderer绘制线条,但是LineRenderer绘制的线有宽度的问题和远近距离宽度无法自适应的问题,那我们该如何处理呢,其实UnityEngine.Mesh已经为我们处理了这个问题,Mesh中提供了新的接口SetIndices(int[] indices,MeshTopology,int sumMeshCount),可以用于绘制实线和虚线,这种线条宽度比例随相机移动保持相同比例,而且使用起来也极其简单。

  • 什么是Mesh?

  • SetIndices是什么方法?

  • MeshTopology网格拓扑是什么鬼?

  • 这里也提醒大家,要经常去Unity官网查看官方提供的API,你总会有新的发现。

了解了上述内容之后,相信大家已经明白改如何调用官方的API了,我们把API再封装一下,就可以很方便的用于绘制实线和虚线了。

public partial class EDraw
{public void Draw3DLine( Vector3 start, Vector3 end ,Color color , Material material = null ){ if (null == material)material = new Material(Shader.Find("HeatMap/HeatMap TwoSide"));GameObject line = new GameObject();line.name = "3DLine";line.hideFlags = HideFlags.HideInHierarchy;MeshFilter lineMesh = line.AddComponent<MeshFilter>();MeshRenderer lineRenderer = line.AddComponent<MeshRenderer>();Mesh mesh = new Mesh();mesh.name = "Line";// verticles // 绘制实现就只需要将顶点计算出来,然后设置顶点序列并制定为LineStrip即可Vector3[] verticles = new Vector3[2];verticles[0] = start;verticles[1] = end;mesh.vertices = verticles;int[] indices = new int[2];indices[0] = 0;indices[1] = 1;mesh.SetIndices(indices,MeshTopology.LineStrip,0);// colorColor[] colors = new Color[verticles.Length];for (int i = 0; i < colors.Length; i++)colors[i] = color;mesh.colors = colors;lineMesh.sharedMesh = mesh;lineRenderer.sharedMaterial = material;}public void Draw3DDottedLine( Vector3 start , Vector3 end , Color color ,float interval = 0.01f, Material material = null ){if (null == material)material = new Material(Shader.Find("HeatMap/HeatMap TwoSide"));GameObject line = new GameObject();line.name = "3DDottedLine";line.hideFlags = HideFlags.HideInHierarchy;MeshFilter lineMesh = line.AddComponent<MeshFilter>();MeshRenderer lineRenderer = line.AddComponent<MeshRenderer>();Mesh mesh = new Mesh();mesh.name = "DottedLine";float distance = Vector3.Distance(start,end);int count = (int)(distance / interval) + 1 ;// verticles Vector3[] verticles = new Vector3[count];int[] indices = new int[count];Vector3 dir = (end - start).normalized;for (int i = 0; i < count; i++){var pos = start + dir * i * interval;if (i.Equals(count - 1))pos = end;verticles[i] = pos;indices[i] = i;}mesh.vertices = verticles;// 绘制虚线同实线原理想吐,只需将拓扑结构改为lines即可.mesh.SetIndices(indices,MeshTopology.Lines,0);// colorColor[] colors = new Color[verticles.Length];for (int i = 0; i < colors.Length; i++)colors[i] = color;mesh.colors = colors;mesh.MarkDynamic();lineMesh.sharedMesh = mesh;lineRenderer.sharedMaterial = material;}// 这个方法用于绘制TextMesh,我们也是稍微做了一个封装public Transform Draw3DText( string text , Vector3 pos , Color fontColor ,Transform parent,Material material = null, int fontSize = 40 , FontStyle fontStyle = FontStyle.Normal,TextAnchor textAnchor = TextAnchor.MiddleLeft){GameObject text3D = new GameObject();text3D.name = "3DText";text3D.hideFlags = HideFlags.HideInHierarchy;MeshRenderer renderer = text3D.AddComponent<MeshRenderer>();TextMesh textMesh = text3D.AddComponent<TextMesh>();renderer.sharedMaterial = textMesh.font.material;textMesh.text = text;textMesh.color = fontColor;textMesh.fontSize = fontSize;textMesh.fontStyle = fontStyle;textMesh.anchor = textAnchor;text3D.transform.SetParent(parent);text3D.transform.localScale = new Vector3(0.01f,0.01f,0.01f);text3D.transform.localPosition = pos;return text3D.transform;}
}
虚线网格绘制

见Draw3D类中的Draw3DDottedLine方法,基本原理与实线的绘制相同

温度图网格的绘制

有了上述线条绘制的方法,在三维空间中绘制网格,只需要你计算出条线两端的顶点即可,详细代码见项目包,这里就不再写了。下载地址会在文章末尾给出。

如何实现只有三面显示的网格

这里我仅给出一个粗糙的实现方案,但是它会出现一些因角度旋转而显示别的面的问题。
这是为什么呢,因为我使用的是剔除正面,所以我们就会看到box的背面,这个你可以通过shader计算法线和视角角度来实现。剔除正面shader如下:

Shader "HeatMap/Advanced/HeatmapBox"
{Properties{_Color ("Base Color(RGBA)",COLOR) = (0.8,0.8,0.8,1)}SubShader{Tags{ "RenderType"="Transparent" "Queue"="Transparent" }LOD 200Pass{Cull FrontBlend SrcAlpha OneMinusSrcAlphaCGPROGRAM#pragma vertex vert#pragma fragment frag#include "UnityCG.cginc"#include "UnityLightingCommon.cginc"struct a2v{float4 pos : POSITION;float3 normal : NORMAL;};struct v2f{float4 vertex : SV_POSITION;float3 normal : NORMAL;float3 worldPos : Texcoord0;};float4 _Color;v2f vert( a2v i ){v2f o;o.vertex = UnityObjectToClipPos(i.pos);o.normal = mul(float4(i.normal,0),unity_WorldToObject).xyz;o.worldPos = mul(unity_WorldToObject,i.pos);return o;}fixed4 frag ( v2f i ) : COLOR{float3 N = normalize(i.normal);float3 L = normalize(_WorldSpaceLightPos0.xyz - i.worldPos.xyz);float NdotL = 1 - saturate(dot(N,L));float3 diffuseColor = NdotL * _Color.rgb;return fixed4(1,1,1,_Color.a);}ENDCG}}
}
Inspector

其中的配置就比较简单了,具体细节见项目源码

网格光滑的问题

  • 为什么示例图中的网格看起来棱角特别的分明,没有一个弧度的过度呢?

相信大家也明白在三维世界中的一切都是由三角面组成的,而且也没有正真绝对的弧线,而弧线都是由大量的短小的直线拼接出出来的,同样圆滑的弧面也是由大量的细分三角面来构成的,所以示例中的菱角特别分明是因为网格面数太低,实例中的网格面数应该只有64个三角面。

  • 还有一点3dMax中的光滑组是什么意思呢?

加光滑组其实是将相邻的两个面的颜色值设置的更为接近,相邻面颜色越接近,那么弧面也将越显光滑,所以在温度图中顶点的颜色值计算也是较为重要的。

通过上述两个问题,我们应该清楚如何提升面的光滑程度,一,更多的三角面,二,面与面之间的颜色过度要合理。对应到我们的温度图组件中就是需要足够多的温度的点信息。

后续拓展

后续我们会将线条的绘制,网格的绘制进一步封装,同时与SpringGUI合并,封装出一个可绘制3D和2D图形的组件。

其实这个组件中还有较多的点需要去优化,比如每条线都会占用一个drawcall,我们需要将实例化的线条mesh合并,还有为了面的光滑,应该添加一个插入算法,让两个点之间通过贝塞尔或者别的算法有一个合理的过度。

UGUI组件系列

  • Unity自定义UI组件(十二) 条形图篇
  • Unity自定义UI组件(十一) 雷达图、属性图
  • Unity自定义UI组件(十) 折线图
  • Unity自定义UI组件(九) 颜色拾取器(下)
  • Unity自定义UI组件(八) 颜色拾取器(上)
  • Unity自定义UI组件(七)渐变工具、渐变色图片、渐变遮罩
  • Unity自定义UI组件(六)日历、日期拾取器
  • Unity自定义组件之(五) 目录树 UITree
  • Unity自定义UI组件(四)双击按钮、长按按钮
  • Unity自定义UI组件(三)饼图篇
  • Unity自定义UI组件(二)函数图篇(下)
  • Unity自定义UI组件(一)函数图篇(上)

Unity框架解读系列

  • [Unity]PureMVC框架解读(下)
  • [Unity]PureMVC框架解读(上)

分享地址(置顶目录包含所有组件的最新下载地址)

  • Github :https://github.com/ll4080333/UnityCodes
  • CSDN : http://blog.csdn.net/qq_29579137
  • 博客专栏 : http://blog.csdn.net/column/details/16329.html
  • 温度图组件下载地址 : http://download.csdn.net/download/qq_29579137/9968723
  • QQ群 : 593906968 有什么不懂的可以加群咨询互相学习
    如果你想了解UGUI的更多拓展组件,欢迎关注我的博客,我会持续更新,支持一下我这个博客新手,你的关注也会给予我更多的动力。如果以上文章对你有帮助,点个赞,让更多的人看到这篇文章,我们一起学习。如果有什么指点的地方欢迎在评论区留言,秒回复。

Unity网格编程篇(四) 三维温度图、热力图相关推荐

  1. 【Unity】Mesh网格编程(四)麦比乌斯环

    前言 事隔四个多月,第二篇网格编程原创,本次献给大家的是麦比乌斯环. 其实这个早就想做了,还是轻松下来的时候思绪转的快. 不废话,先看效果: 博文首发:http://blog.csdn.net/duz ...

  2. unity---Mesh网格编程(四)

    目录 1.绘制一个圆角正方体 2.代码 unity---Mesh网格编程(三)这里说了Mesh绘制一个正方体或立方体 1.绘制一个圆角正方体 2.代码 //X.Y.Z轴正方体数量足够多的偶数 半径取X ...

  3. Unity可编程渲染管线系列(四)聚光灯阴影(阴影贴图)

    目录 1 一个带有阴影的聚光灯 1.1 阴影贴图 1.2 阴影命令缓冲区 1.3 设置 渲染目标 1.4 配置视图和投影矩阵 1.5 渲染阴影投射器 2 阴影投射器通道 2.1 阴影包含文件 2.2 ...

  4. Unity数据可视化 温度图效果(一)

    既然是使用Unity做可视化三维,那么基本的数据可视化效果还有要有的,之后几篇文章会讲解几种温度图效果的实现方案. 之前文章我们也讲了URP的好处,因为我们的所有效果都是在通用渲染管线下使用的. 效果 ...

  5. DirectX5.0最新游戏编程指南 DirectDraw篇 四、DirectDraw高级特性 (转)

    DirectX5.0最新游戏编程指南 DirectDraw篇 四.DirectDraw高级特性 (转)[@more@] 四.DirectDraw高级特性 1.直接内存访问DMA   有些显示设备能够在 ...

  6. Unity Editor 基础篇(四):Handles

    本文参自: 克森http://mp.weixin.qq.com/s/qxsKDPjJS30S9OXeQ8WKTw 本文为本人学习上链接的笔记微有改动,请点击以上链接查看原文,尊重楼主知识产权. Uni ...

  7. Unity网格篇Mesh(一)

    程序代码实现网格 这里根据CSDNSpring5211的一篇文章,进行网格学习为网格材质合并作为一个基础 本文的目标 创建网格坐标 使用携程计算他们位置 利用三角形确定一个面 自动生成法线 添加纹理坐 ...

  8. [Unity 学习] - 进阶篇 - Mesh基础系列1:生成网格

    [Unity 学习] - 进阶篇 - Mesh基础系列1:生成网格 本文并非原创,只是本人的学习记录,原文是由放牛的星星老师翻译Catlike系列教程 链接: https://mp.weixin.qq ...

  9. python画三维温度散点图-Python 绘制酷炫的三维图步骤详解

    通常我们用 Python 绘制的都是二维平面图,但有时也需要绘制三维场景图,比如像下面这样的: 这些图怎么做出来呢?今天就来分享下如何一步步绘制出三维矢量(SVG)图. 八面体 我们先以下面这个八面体 ...

  10. linux 脚本编写 -eq,关于shell脚本基础编程第四篇

    shell脚本基础编程第四篇 本章主要内容:函数 函数 function: function 名称 { 命令 ; } 或 name () { 命令 ; } 定义 shell 函数. 创建一个以 NAM ...

最新文章

  1. ts引入公共方法_vue3+TS+ant2.0 实践指南
  2. MVC框架浅析(基于PHP)
  3. 学习Git的最佳资料
  4. 邓侃:深度强化学习“深”在哪里?
  5. JavaWeb学习总结(十二)——Session
  6. 【Servlet】request对象获取请求头数据和用户数据
  7. 【Flink】Flink Failed to push metrics to PushGateway Connect refuse
  8. TCP协议 状态解析和状态统计
  9. cadence SPB17.4 allegro + CAM350 10.5 / 10.7 / 14.6 出拼板
  10. 如何修改hosts文件
  11. 二、列表(java)
  12. python dlib caffe人脸相似度_人脸检测学习笔记(数据集-DLIB人脸检测原理-DLIBOpenCV人脸检测方法及对比)...
  13. 跨国公司怎样面试应聘者
  14. moto edge s root与开启nfc门禁卡模拟
  15. elementUI组件el-table实现分页、勾选、勾选回显功能
  16. Linux 网络编程: daytime Service
  17. 【Python,迄今为止讲解的最详细的一篇
  18. Linux安装unrar
  19. Vue中使用tailwindcss
  20. 使用Xgboost自带的读取格式DMatrix()

热门文章

  1. Windows利用WTS API获取锁屏状态
  2. 龙芯源码编译mysql_龙芯服务器安装总结
  3. 基于STM32单片机设计的红外测温仪(带人脸检测)
  4. NPOI Word 换行 【XWPFDocument】
  5. 什么是MBR/DPT/DBR/BPB?
  6. 交易偏见--《别做正常的傻瓜》摘记2
  7. 一个人流浪,不必去远方
  8. android休眠状态,【Android休眠】之Android休眠机制
  9. 安卓手机作为文件共享服务器,安卓手机的文件共享应该怎么操作?
  10. windows server 2008安装配置web服务器