精华内容
下载资源
问答
  • Unity3D 温度图、热点图

    热门讨论 2017-09-07 17:04:14
    基于Unity3D利用Mesh和Shader开发的温度图,在工业可视化的项目中,我们还会经常遇到一个需求,就是显示某个设备的温度信息,或者是显示一间房间的温度信息,这里我们就需要用到一个组件叫温度/热力图。需要实现一个...
  • Unity3D 3D温度图

    2017-09-25 09:12:28
    最近在论坛中碰到不少同学需要将这个温度图改为三维的,其实改为三维的并不难,因为上一篇中HeatMap是用Mesh绘制的,因此我们只要给网格一个高度值就可以变成三维的温度图,但是为了效果我们准备增加一些单位和网格...
  • 因一个任务要完成如何在Unity上面实现热力的效果,所以百度了很久,发现资料很少,现在就把我总结的如何在Unity上面基于Canvas实现热力效果的实现过程分享出来, 此前转载了一篇主要讲的是如何根据数据值,在...
  • Unity网格编程篇(三) 温度图、热力图

    万次阅读 多人点赞 2017-09-05 18:06:44
    在工业可视化的项目中,我们还会经常遇到一个需求,就是显示某个设备的温度信息,或者是显示一间房间的温度信息,这里我们就需要用到一个组件叫温度图/热力图。需要实现一个温度图又一次需要用到我们的网格和shader...

    前言

    在工业可视化的项目中,我们还会经常遇到一个需求,就是显示某个设备的温度信息,或者是显示一间房间的温度信息,这里我们就需要用到一个组件叫温度/热力图。需要实现一个温度图又一次需要用到我们的网格和shader,下面我们来讲解一下实现的原理和最终的效果。

    实现效果

    以前大概区分我三个版本,版本之前没有本质区别,只是更改Shader已达到不同的使用场景和最佳的运行效果。

    • 不支持透明的单面可见版
      这里写图片描述
    • 不支持透明的双面可见版
      这里写图片描述

    • 支持透明且双面可见版
      这里写图片描述

    主要内容

    • 绘制网格
    • 编写Shader
    • 赋颜色值

    详细设计

    绘制网格

    网格的绘制详细教程参见我的另一篇文章,这里不再详细讲解。

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

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

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

    编写Shader

    • 基础版
    Shader "HeatMap/HeatMap Easy"
    {
        SubShader 
        {
            Tags { "RenderType"="Opaque" }
            LOD 200
    
            Pass
            {
                CGPROGRAM
    
                #pragma vertex vert
                #pragma fragment frag
                #include "UnityCG.cginc"
    
                struct a2v
                {
                    float4 pos : POSITION;
                    fixed4 color : COLOR;
                };
    
                struct v2f
                {
                    float4 vertex : SV_POSITION;
                    fixed4 color : COLOR;
                };
    
                v2f vert( a2v i )
                {
                    v2f o;
                    o.vertex = mul(UNITY_MATRIX_MVP,i.pos);
                    o.color = i.color;
                    return o;
                }
    
                fixed4 frag( v2f i ) : COLOR
                {
                    return i.color;
                }
    
                ENDCG
            }
        }
    }
    
    • 双面不透明版

    将基础版的改为双面不透明,只需要将渲染通道中Shader默认的剔除属性更改为关闭剔除即可Cull OFF

    Pass
    {
        Cull OFF
        CGPROGRAM
        //和基础版相同
        ENDCG
    }
    • 双面透明版

    再讲双面不透明版更改为支持透明版本,只需要将渲染类型设置为Transparent和渲染队列设置为Transparent,在渲染通道中开启透明混合属性即可Blend SrcAlpha OneMinusSrcAlpha。

    Properties
    {
        _Alpha("Alpha",Range(0,1)) = 0.8
    }
    
    SubShader 
    {
        Tags { "RenderType"="Transparent" "Opaque"="Transparent" }
        Pass
        {
            Cull OFF
            Blend SrcAlpha OneMinusSrcAlpha
            //和基础版相同
            CGPROGRAM
        }
    }
    

    赋颜色值

    本组件预留的接口是导入一个二维的温度数组,所以如果你有真实的温度数据,你可以使用我提供的二维数组的接口,也可以自己再添加一个新的接口,在接口中将数据赋值到对应的顶点的color属性即可。

    由于没有真实的温度数据,我们利用算法模拟一些数据导入,以下是模拟的算法。

    • 赋值颜色大致步骤:
      • 初始化二维数组
      • 抽取随机点,产生高温值,并产生递减趋势,并生成的温度值覆盖二维数组
      • 给顶点的color属性赋值
      • 生成网格,添加使用以上编写shader的材质球
    初始化二维数组

    根据我们录入的数据精度,产生二维数组,我们假定生成一个长宽唯独均为100的二维数组

    // 初始化算法
    private float[,] InitTemperatures()  
    {
        // vertical 和 horizontal 均为100
        float[,] temperature = new float[vertical , horizontal];
        for (int i = 0; i < vertical ; i++)
            for ( int j = 0 ; j < horizontal ; j++ )
                // 假定正常温度为50度低于50度均为正常温度
                temperature[i , j] = 50;
        return temperature;
    }
    产生随机高温点

    将外界产生的初始化温度通过接口Inject方法传入我们的组件HeatMap中。因为没有真实的温度数据,所以我们需要调用HeatMap中的随机高温点方法来产生一些温度数据。如下:

    [RequireComponent(typeof(MeshFilter), typeof(MeshRenderer))]
    public class HeatMap : MonoBehaviour
    {
        public void Inject( float[,] temperature )
        {
            this.temperature = temperature;
            this.horizontal = temperature.GetLength(1);
            this.vertical = temperature.GetLength(0);
            // 利用随机产生高温点的算法添加6个高温点
            RandomTeamperature(99 , 50 , 0 , 45 , ref this.temperature);
            RandomTeamperature(89 , 50 , 0 , 25 , ref this.temperature);
            RandomTeamperature(79 , 50 , 0 , 30 , ref this.temperature);
            RandomTeamperature(69 , 50 , 0 , 33 , ref this.temperature);
            RandomTeamperature(89 , 50 , 0 , 50 , ref this.temperature);
            RandomTeamperature(79 , 50 , 0 , 22 , ref this.temperature);
            meshFilter.mesh = DrawHeatMap();
            // 赋值颜色
            AddVertexColor();
        }
    
        // 随机在某个点产生高温数据,并按minD和maxD温度范围来生成模拟数据
        // minD和maxD为以当前高温点为中心的生效范围
        // from为最高温度,to为最低温度
        private void RandomTeamperature( float from , float to, int minD , int maxD , ref float[,] temperatures ) 
        {
            // 产生随机点
            int randomX = Random.Range(3 , horizontal);
            int randomY = Random.Range(3 , vertical);
    
            float maxTweenDis = maxD - minD;
            float offset = to - from;
            for ( int i = randomX - maxD ; i < randomX + maxD ; i++ )
            {
                for ( int j = randomY + maxD ; j > randomY - maxD ; j-- )
                {
                    if ( i < 0 || i >= horizontal )
                        continue;
                    if ( j < 0 || j >= vertical )
                        continue;  
                    float distance = Mathf.Sqrt(Mathf.Pow(randomX - i , 2) + Mathf.Pow(randomY - j , 2));
                    if ( distance <= maxD && distance >= minD )
                    {
                        float offsetDis = distance - minD;
                        float ratio = offsetDis / maxTweenDis;
                        float temp = from + ratio * offset;
                        // 只有比当前点温度高才选择覆盖
                        if ( temp > temperatures[i , j] )
                            temperatures[i , j] = temp;
                    }
                }
            }
        }
    }
    给顶点color属性赋值
    private void AddVertexColor( )
    {
        Color[] colors = new Color[meshFilter.mesh.colors.Length];
        for ( int j = 0 ; j < vertical ; j++ )
        {
            for ( int i = 0 ; i < horizontal ; i++ )
                colors[horizontal * j + i] = CalcColor(this.temperature[j , i]);
        }
        meshFilter.mesh.colors = colors;
    }
    生成网格赋予材质球

    这里写图片描述

    后续拓展

    • 1.以上讲解了简单温度图的实现原理和代码编写,但是我们在项目不单单会使用到这类简单的功能,除了2D的温度图,我们可能会使用到三维的温度云图,温度云图又是如何实现的呢,我们在以后的文章再详细讲解吧。
    • 2.温度图我们也可以拓展为UGUI组件,可只用在UGUI UI界面进行展示,到底该怎么编写,看过UGUI自定义组件文章的应该都很熟悉了,如果不懂,那你就点击下面的UGUI组件系列的链接去了解一下吧!

    UGUI组件系列

    Unity框架解读系列

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

    展开全文
  • 因一个任务要完成如何在Unity上面实现热力的效果,所以百度了很久,发现资料很少,现在就把我总结的如何在Unity上面基于Canvas实现热力效果的实现过程分享出来, 此前转载了一篇主要讲的是如何根据数据值,在...

    一、前言

    因一个任务要完成如何在Unity上面实现热力图的效果,所以百度了很久,发现资料很少,现在就把我总结的如何在Unity上面基于Canvas实现热力图效果的实现过程分享出来, 此前转载了一篇主要讲的是如何根据数据值,在Canvas上重新绘制RGBA的值,完成热力图的绘制,不过用的是H5写的,我修改了一下,用C#重写的

    效果图:
    在这里插入图片描述

    项目源文件已经上传CSDN:
    https://download.csdn.net/download/q764424567/13109963

    二、参考资料

    1.基于Canvas的热力图绘制方法【http://www.blueidea.com/tech/web/2010/7933.asp 】
    2.Unity(OpenGL)实现“阴阳师画符”、划线功能【https://blog.csdn.net/yuanhandsome/article/details/78366250】

    三、正文

    实现过程:
    1.首先从文档中解析数据,获取需要的数据(当然,这一步也可以在网上获取数据,然后再解析)
    2.将获取的数据进行分析
    3.保存贴图到Texture,将保存的贴图赋值给Image的Sprite(保存的贴图也可以赋值给物理对象的贴图)
    4.清除数据

    第一步、解析数据

    就以下面这一组数据为例
    在这里插入图片描述

    第一行数据,代表是的模拟网格 160160
    第二行数据,模拟半径 -80km至80km范围;东西方向(x轴)区间范围,西→东
    第三行数据,剂量值(z轴),即160
    160网格范围内的最小值和最大值
    第6-164,表示各个网格点的浓度值

    就是说有160*160个数据,可以用二维数组去接收数据
    在这里插入图片描述
    然后用一个List数组去接收解析完的数据
    在这里插入图片描述

    完整代码

    using System.Collections.Generic;
    using UnityEngine;
    using System.IO;
    using UnityEngine.UI;
    using UnityEngine.EventSystems;
    
    public class Draw : MonoBehaviour
    {
        //文件路径名
        private string fileName;
        //读取数据
        private string str;
        //保存数据
        private List<int> numberList = new List<int>();
        string tempData_Space;
        string[] tempData_Array;
        int[] tempData_IntArray;
        //保存解析完的数据
        private int[,] m_Sum = new int[159, 159];
    
        void Start()
        {
            ReadFile();
        }
    
        //读指定目录下的文件
        public void ReadFile()
        {
            //指定目录下的文件
            fileName = "X:/2015_m01_d01_0000(utc+0800)_l00_csi_1hr_conc.grd";
            StreamReader sr = File.OpenText(fileName);
            while (true)
            {
                //按行读取,str保存的是一行的数据
                str = sr.ReadLine();
                int lineLength = 0;
                if (str != null)
                {
                    //得到获取到的这行数据的长度
                    lineLength = str.Length;
                    //过滤掉前几行和后几行的数据
                    if (lineLength >= 1000)
                    {
                        //SPlit函数只能分割单个空格,所以两个空格替换成一个空格
                        tempData_Space = str.Replace("  ", " ");
                        //分割字符串
                        tempData_Array = tempData_Space.Split(' ');
                        //保存获取到的数据
                        tempData_IntArray = new int[tempData_Array.Length];
                        //取后两位数据
                        for (int i = 0; i < tempData_Array.Length; i++)
                        {
                            tempData_IntArray[i] = int.Parse(tempData_Array[i].Substring(8, 2));
                        }
                        //添加到List数组中
                        for (int i = 0; i < tempData_IntArray.Length; i++)
                        {
                            numberList.Add(tempData_IntArray[i]);
                        }
                    }
                }
                if (str == null)
                {
                    break;
                }
            }
            //将List数组中保存的数据赋值给二维数组
            for (int x = 0; x < m_Width; x++)
            {
                for (int y = 0; y < m_Height; y++)
                {
                    int i = y + x * m_Width;
                    m_Sum[x, y] = numberList[i];
                }
            }
            //关闭数据流
            sr.Close();
        }
    

    第二步、分析数据

    数据保存到m_Sum二维数组中,剩下的就是从二维数组中读取数据,然后进行分析
    在这里插入图片描述
    读取到的数据是最后两位的数据,然后将数据转成int类型进行对比

    实现代码

    //对比数据
        public void DataCompare()
        {
            //因为二维数组的数量是159*159所以就在这个范围内读取
            for (int i = 0; i < 159; i++)
            {
                for (int j = 0; j < 159; j++)
                {
                    //数据对比
                    int temp = m_Sum[i, j];
                    if (temp <= 6)
                    {
                        Debug.Log("透明");
                    }
                    else if (temp > 6 && temp <= 7)
                    {
                        Debug.Log("红色");
                    }
                    else if (temp > 7 && temp <= 8)
                    {
                        Debug.Log("橘色");
                    }
                    else if (temp > 8 && temp <= 9)
                    {
                        Debug.Log("黄色");
                    }
                    else if (temp > 9 && temp <= 10)
                    {
                        Debug.Log("绿色");
                    }
                    else if (temp > 10 && temp <= 11)
                    {
                        Debug.Log("蓝色");
                    }
                    else if (temp > 11 && temp <= 12)
                    {
                        Debug.Log("靛蓝色");
                    }
                    else if (temp > 12)
                    {
                        Debug.Log("紫色");
                    }
                }
            }
        }
    

    第三步、保存贴图

    最重要的一步,如何将分析过的数据生成贴图,并且赋值给Image呢

    在这里插入图片描述
    主要就是这两行的代码

    完成代码

    //保存贴图变量
        private Texture2D texture;
        private Image targetMaterial;
        
    //创建设置贴图文件
        void CreatTexture()
        {
            texture = new Texture2D(m_Width, m_Height);
            int targetWidth = m_Width;
            int targetHeight = m_Height;
            for (int i = 0; i < targetWidth; i++)
            {
                for (int j = 0; j < targetHeight; j++)
                {
                    int temp = m_Sum[i, j];
                    if (temp <= 6)
                    {
                        texture.SetPixel(i, j, m_ColorTransparency);
                    }
                    else if (temp > 6 && temp <= 7)
                    {
                        texture.SetPixel(i, j, m_ColorRed);
                    }
                    else if (temp > 7 && temp <= 8)
                    {
                        texture.SetPixel(i, j, m_ColorOrange);
                    }
                    else if (temp > 8 && temp <= 9)
                    {
                        texture.SetPixel(i, j, m_ColorYellow);
                    }
                    else if (temp > 9 && temp <= 10)
                    {
                        texture.SetPixel(i, j, m_ColorGreen);
                    }
                    else if (temp > 10 && temp <= 11)
                    {
                        texture.SetPixel(i, j, m_ColorBlue);
                    }
                    else if (temp > 11 && temp <= 12)
                    {
                        texture.SetPixel(i, j, m_ColorIndigo);
                    }
                    else if (temp > 12)
                    {
                        texture.SetPixel(i, j, m_ColorPurple);
                    }
                }
            }
            //应用贴图
            texture.Apply();
            //将贴图数据赋值给Image的sprite
            targetMaterial.sprite = Sprite.Create(texture, new Rect(0, 0, m_Width, m_Height), Vector2.zero); ;
        }
    

    第四步、清除数据

    清除数组中的数据,供下次读取数据

    //清理数组中的数据
        public void ClearList()
        {
            numberList.Clear();
        }
    

    整体代码

    using System.Collections.Generic;
    using UnityEngine;
    using System.IO;
    using UnityEngine.UI;
    using UnityEngine.EventSystems;
    
    public class Draw : MonoBehaviour
    {
        //颜色设置
        public Color m_ColorTransparency;
        public Color m_ColorRed;
        public Color m_ColorOrange;
        public Color m_ColorYellow;
        public Color m_ColorGreen;
        public Color m_ColorBlue;
        public Color m_ColorIndigo;
        public Color m_ColorPurple;
        //保存贴图
        private Texture2D texture;
        private Image targetMaterial;
        //文件路径名
        private string fileName;
        //读取数据
        private string str;
        //保存数据
        private List<int> numberList = new List<int>();
        string tempData_Space;
        string[] tempData_Array;
        int[] tempData_IntArray;
        //保存解析完的数据
        private int[,] m_Sum = new int[159, 159];
        //长宽设置
        private int m_Width = 159;
        private int m_Height = 159;
    
        void Start()
        {
            targetMaterial = gameObject.GetComponent<Image>();
            targetMaterial.color = Color.white;
            ReadFile();
            CreatTexture();
            ClearList();
        }
    
        //清理数组中的数据
        public void ClearList()
        {
            numberList.Clear();
        }
    
        //读指定目录下的文件
        public void ReadFile()
        {
            //指定目录下的文件
            fileName = "X:/2015_m01_d01_0000(utc+0800)_l00_csi_1hr_conc.grd";
            StreamReader sr = File.OpenText(fileName);
            while (true)
            {
                //按行读取,str保存的是一行的数据
                str = sr.ReadLine();
                int lineLength = 0;
                if (str != null)
                {
                    //得到获取到的这行数据的长度
                    lineLength = str.Length;
                    //过滤掉前几行和后几行的数据
                    if (lineLength >= 1000)
                    {
                        //SPlit函数只能分割单个空格,所以两个空格替换成一个空格
                        tempData_Space = str.Replace("  ", " ");
                        //分割字符串
                        tempData_Array = tempData_Space.Split(' ');
                        //保存获取到的数据
                        tempData_IntArray = new int[tempData_Array.Length];
                        //取后两位数据
                        for (int i = 0; i < tempData_Array.Length; i++)
                        {
                            tempData_IntArray[i] = int.Parse(tempData_Array[i].Substring(8, 2));
                        }
                        //添加到List数组中
                        for (int i = 0; i < tempData_IntArray.Length; i++)
                        {
                            numberList.Add(tempData_IntArray[i]);
                        }
                    }
                }
                if (str == null)
                {
                    break;
                }
            }
            //将List数组中保存的数据赋值给二维数组
            for (int x = 0; x < m_Width; x++)
            {
                for (int y = 0; y < m_Height; y++)
                {
                    int i = y + x * m_Width;
                    m_Sum[x, y] = numberList[i];
                }
            }
            //关闭数据流
            sr.Close();
        }
    
        //创建设置贴图文件
        void CreatTexture()
        {
            texture = new Texture2D(m_Width, m_Height);
            int targetWidth = m_Width;
            int targetHeight = m_Height;
            for (int i = 0; i < targetWidth; i++)
            {
                for (int j = 0; j < targetHeight; j++)
                {
                    int temp = m_Sum[i, j];
                    if (temp <= 6)
                    {
                        texture.SetPixel(i, j, m_ColorTransparency);
                    }
                    else if (temp > 6 && temp <= 7)
                    {
                        texture.SetPixel(i, j, m_ColorRed);
                    }
                    else if (temp > 7 && temp <= 8)
                    {
                        texture.SetPixel(i, j, m_ColorOrange);
                    }
                    else if (temp > 8 && temp <= 9)
                    {
                        texture.SetPixel(i, j, m_ColorYellow);
                    }
                    else if (temp > 9 && temp <= 10)
                    {
                        texture.SetPixel(i, j, m_ColorGreen);
                    }
                    else if (temp > 10 && temp <= 11)
                    {
                        texture.SetPixel(i, j, m_ColorBlue);
                    }
                    else if (temp > 11 && temp <= 12)
                    {
                        texture.SetPixel(i, j, m_ColorIndigo);
                    }
                    else if (temp > 12)
                    {
                        texture.SetPixel(i, j, m_ColorPurple);
                    }
                }
            }
            //应用贴图
            texture.Apply();
            //将贴图数据赋值给Image的sprite
            targetMaterial.sprite = Sprite.Create(texture, new Rect(0, 0, m_Width, m_Height), Vector2.zero); ;
        }
    }
    

    四、工程文件

    链接:https://pan.baidu.com/s/1akdyDVrwHjQJm9RvD4Rh8w
    提取码:y4be

    Github地址:
    https://github.com/764424567/Demo_DrawImg

    Gitee地址(嫌弃Github地址下载太慢可以用码云):
    https://gitee.com/itMonon/Demo_DrawImg

    展开全文
  • 上一篇博客中我门实现了一个平面的温度图,但是最近在论坛中碰到不少同学需要将这个温度图改为三维的,其实改为三维的并不难,因为上一篇中HeatMap是用Mesh绘制的,因此我们只要给网格一个高度值就可以变成三维的...

    前言

    上一篇博客中我门实现了一个平面的温度图,但是最近在论坛中碰到不少同学需要将这个温度图改为三维的,其实改为三维的并不难,因为上一篇中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),可以用于绘制实线和虚线,这种线条宽度比例随相机移动保持相同比例,而且使用起来也极其简单。

    了解了上述内容之后,相信大家已经明白改如何调用官方的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);
            // color
            Color[] 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);
            // color
            Color[] 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 200
    
            Pass
            {
                Cull Front
                Blend SrcAlpha OneMinusSrcAlpha
                CGPROGRAM
    
                #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框架解读系列

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

    展开全文
  • unity温度云图实现

    千次阅读 2016-04-22 12:16:57
    最近在做一个展示项目 需要展示空间内的温度云图 在网络搜索一番 得到一枚shader 但是并不能满足需求  所以借鉴其算法 进行程序方面的实现 达到预期效果 PS:并非此shader不好,而是因为不能动态传入动态长度...
    最近在做一个展示项目 需要展示空间内的温度云图 在网络搜索一番 得到一枚shader 但是并不能满足需求 
    
    所以借鉴其算法 进行程序方面的实现 达到预期效果
    PS:并非此shader不好,而是因为不能动态传入动态长度的数据  只能展示固定的几个点 无法满足本项目需求

    其实思路很简单 在需要展示温度数据的时候 根据实时传入的数据进行贴图生成 对颜色进行插值运算 达到展示效果
    效果如图


    代码附上:

    [code]csharpcode:

    using UnityEngine;
    using System.Collections;
    using System;
    /// <summary>
    /// Ace 
    /// </summary>
    public class Temperature : MonoBehaviour {
    
        float row=128;//行数
        float col=128;//列数
    	void Start () {
            //宽度为列数 高度为行数
            Texture2D t = new Texture2D((int)col, (int)row);
            Color[] colors = new Color[(int)row * (int)col];
            for (int i = 0; i < colors.Length; i++)
            {
                float[] uv = new float[2];
                uv[0] = ((i % col)+1) / col;
                uv[1] = ((i / col)+1) / row;
                float temp = computerTemperature(uv);
                
                if (temp >= 60)
                {
                    colors[i] = new Color((temp - 60) / 40 + 1 - (temp - 60) / 40, 1 - (temp - 60) / 40, 0);
                }
                else if (temp >= 30)
                {
                    colors[i] = new Color((temp - 30) / 30, (temp - 30) / 30 + 1 - (temp - 30) / 30, 0);
                }
                else
                {
                    colors[i] = new Color(0, (temp) / 30, 1 - (temp) / 30);
                }
            }
            t.SetPixels(colors);
            t.Apply();
            GetComponent<Renderer>().material.mainTexture = t;
        }
        [SerializeField]
        PData[] datas = new PData[] { };
        float computerTemperature(float[] uv)
        {
            
            float d1 = Mathf.Sqrt(uv[0] * uv[0] + uv[1] * uv[1]);
            float d2 = Mathf.Sqrt((1 - uv[0]) * (1 - uv[0]) + (1 - uv[1]) * (1 - uv[1]));
            float d3 = Mathf.Sqrt(uv[0] * uv[0] + (1 - uv[1]) * (1 - uv[1]));
            float d4 = Mathf.Sqrt((1 - uv[0]) * (1 - uv[0]) + uv[1] * uv[1]);
            float m = 1 / d1 + 1 / d2 + 1 / d3 + 1 / d4;
            float n = 1 / d1 * 30 + 1 / d2 * 30 + 1 / d3 * 30 + 1 / d4 * 30;
            for (int i = 0; i < datas.Length; i++)
            {
                float dp = Mathf.Sqrt((uv[0] - datas[i].x) * (uv[0] - datas[i].x) + (uv[1] - datas[i].y) * (uv[1] - datas[i].y));
                m = m + 1 / dp;
                n = n + 1 / dp * datas[i].t;
            }
            return n / m;
        }
    }
    [Serializable]
    public class PData
    {
        public float x;
        public float y;
        public float t;
    }


    其中 PData对象为温度数据点相关数据  xy坐标 及点的温度值

    显示时对温度点颜色进行插值计算 生成贴图


    使用方法:创建一个quad 或者plane 放上脚本 即可

    展开全文
  • 原文地址:https://catlikecoding.com/unity/tutorials/hex-map/part-26/ 机翻+个人润色 河流是否起源于高湿单元? 创建一个简单的温度模型。 对单元使用生物群落模型,然后调整它。 这是六边形地图系列教程的第...
  • Unity&Shader案例篇—地图上热图分布

    千次阅读 2018-07-09 18:29:06
    可以通过切换不同的贴图表示不同的信息,如所示表示的是该地区的降水量的变化如所示为表示该地区的温度变化分布二、实现 在贴图的的某个位置处绘制指定半径的圆圈,然后调整圆圈的强度也即透明度。...
  • Unity在移动设备上发热严重问题

    万次阅读 2016-08-28 20:07:15
    (在运行时,Unity产生的计算量非常大,造成移动设备发热严重。) (根据简单温度测试,我的测试手机10min,温度上升20℃。) 解决办法: 修改帧速率: 先修改限制刷新速率如: 设置为Don`t Sync之后,就是...
  • 检测显卡温度的软件

    2019-07-03 09:54:27
    sensor是一个以图形形式显示硬件温度的监视器,同时还支持UbuntuUnity Indicator。 它可以监控: 主板和CPU传感器的温度(用lm-sensors) 显卡GPU的温度(用XNVCtrl) 硬盘的温度(用hddtemp) 风扇旋转速度...

空空如也

空空如也

1 2
收藏数 27
精华内容 10
关键字:

unity温度图