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

前言

在工业可视化的项目中,我们还会经常遇到一个需求,就是显示某个设备的温度信息,或者是显示一间房间的温度信息,这里我们就需要用到一个组件叫温度/热力图。需要实现一个温度图又一次需要用到我们的网格和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框架解读系列

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

猜你喜欢

转载自blog.csdn.net/qq_29579137/article/details/77854504
今日推荐