Unity-热力图效果的实现

文章目录[x]
  1. 0.1:一、前言
  2. 0.2:二、编写shader
  3. 0.3:三、温度控制点脚本
  4. 0.4:四、使用方法
  5. 0.5:五、代码下载

一、前言

在做工业可视化的项目中我们经常会遇到显示设备、环境温度的需求。下面我们来讲解一下实现的原理和最终的效果。

这里我实现了三种不同的效果(两种是抄来的:地址)。

缺点:无法实现中间有障碍物对温度传播的阻挡,目前都是根据距离衰减的。如果要实现障碍物或者立体空间的热力图那就属于另外一门学科了,在Unity中做出这样的仿真效果是比较难的。

二、编写shader

1.上图中间效果的shader

Shader "UChart/HeatMap/Simple"
{
    Properties
    {
        _HeatMapTex("Texture",2D) = "white"{}
        _Alpha("Alpha",range(0,1)) = 0.8 
    }

    SubShader
    {
        Tags {"RenderType"="Overlay" "Queue"="Transparent" } 
        Blend SrcAlpha OneMinusSrcAlpha
        ZTest [unity_GUIZTestMode]
        ZWrite On
        // Cull off

        Pass
        {
            CGPROGRAM
            
            #pragma vertex vert
            #pragma fragment frag

            sampler2D _HeatMapTex;
            half _Alpha;
            uniform int _FactorCount = 0;
            uniform float3 _Factors[100];
            uniform float3 _FactorsProperties[100];

            struct a2v
            {
                float4 pos : POSITION;
            };

            struct v2f
            {
                float4 pos : POSITION;
                fixed3 worldPos : TEXCOORD1;
            };

            v2f vert(a2v input)
            {
                v2f o;
                o.pos = UnityObjectToClipPos(input.pos);
                o.worldPos = mul(unity_ObjectToWorld,input.pos).xyz;
                return o;
            }

            half4 frag(v2f input):COLOR
            {
                half heat = 0;
                for( int i = 0 ; i < _FactorCount;i++ )
                {
                    half dis = distance(input.worldPos,_Factors[i].xyz);
                    float radius = _FactorsProperties[i].x;
                    float intensity = _FactorsProperties[i].y;
                    float temperatureFacator = _FactorsProperties[i].z;
                    half ratio = 1 - saturate(dis/radius);
                    heat += intensity * ratio * temperatureFacator;
                    heat = clamp(heat,0.05,0.95);
                }
                half4 color = tex2D(_HeatMapTex,fixed2(heat,0.5));
                color.a = _Alpha;
                return color;
            }

            ENDCG
        }
    }

     FallBack "Diffuse"
}

2.上图最右边的shader

Shader "UChart/HeatMap/Peak"
{
    Properties
    {
        _HeatMapTex("HeatMapTex",2D) = "white"{}
        _Alpha("Alpha",range(0,1)) = 0.8
    }

    SubShader
    {
        Tags{"Queue"="Transparent"}
        Blend SrcAlpha OneMinusSrcAlpha

        Pass
        {
            CGPROGRAM

            #pragma vertex vert
            #pragma fragment frag
            #include "unitycg.cginc"

            sampler2D _HeatMapTex;
            half _Alpha;
            uniform int _FactorCount;
            uniform float4 _Factors[100];
            uniform float2 _FactorsProperties[100];

            struct a2v
            {
                float4 pos : POSITION;
            };

            struct v2f
            {
                float4 pos : POSITION;
                fixed3 worldPos : TEXCOORD1;
            };

            v2f vert(a2v input)
            {
                v2f o;
                half3 worldPos = mul(unity_ObjectToWorld,input.pos).xyz;
                half heat = 0;
                for( int i = 0 ; i < _FactorCount;i++ )
                {
                    half dis = distance(worldPos,_Factors[i].xyz);
                    float radius = _FactorsProperties[i].x;
                    float intensity = _FactorsProperties[i].y;
                    half ratio = 1 - saturate(dis/radius);
                    heat += intensity * ratio;
                }	
                o.pos = UnityObjectToClipPos(input.pos + half3(0,heat*3,0));
                o.worldPos = mul(unity_ObjectToWorld,input.pos).xyz;
                return o;
            }

            half4 frag(v2f input):COLOR
            {
                half heat = 0;
                for( int i = 0 ; i < _FactorCount;i++ )
                {
                    half dis = distance(input.worldPos,_Factors[i].xyz);
                    float radius = _FactorsProperties[i].x;
                    float intensity = _FactorsProperties[i].y;
                    half ratio = 1 - saturate(dis/radius);
                    heat += intensity * ratio;
                    heat = clamp(heat,0.05,0.95);
                }
                half4 color = tex2D(_HeatMapTex,fixed2(heat,0.5));
                color.a = _Alpha;
                return color;
            }

            ENDCG
        }
    }

    Fallback "Diffuse"
}

3.上图最左边的shader

Shader "Unlit/MetallBallHeatMapShader"
{
    Properties
    {
        _HeatMapTex("Texture",2D) = "white"{}
        _Alpha("Alpha",range(0,1)) = 0.8
    }
    SubShader
    {
        Tags {"RenderType" = "Overlay" "Queue" = "Transparent" }
        Blend SrcAlpha OneMinusSrcAlpha
        ZTest[unity_GUIZTestMode]
        ZWrite On
        LOD 100

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"

            sampler2D _HeatMapTex;
            half _Alpha;
            uniform int _FactorCount = 0;
            uniform float3 _Factors[100];//控制点的数量不够的话可以重新指定数组长度,但是数量越多效率越低
            uniform float3 _FactorsProperties[100];

            struct appdata
            {
                float4 vertex : POSITION;
            };

            struct v2f
            {
                float4 vertex : SV_POSITION;
                float3 worldVertex : TEXCOORD1;
            };

            //hsv颜色转rgb颜色
            float3 hsv2rgb(float3 c) 
            {
                c = float3(c.x, clamp(c.yz, 0.0, 1.0));
                float4 K = float4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);
                float3 p = abs(frac(c.xxx + K.xyz) * 6.0 - K.www);
                return c.z * lerp(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);
            }

            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.worldVertex = mul(unity_ObjectToWorld, v.vertex).xyz;
                return o;
            }

            fixed4 frag(v2f input) : SV_Target
            {
                half heat = 0;
                //逐像素遍历各个控制点效率低
                for (int i = 0; i < _FactorCount; i++)
                {
                    float len = length(input.worldVertex.xyz - _Factors[i].xyz);
                    float value;
                    if (len < _FactorsProperties[i].x)//在半径之内的统一为红色
                        value = 1;
                    else
                        value = _FactorsProperties[i].y / (len - _FactorsProperties[i].x);//范围之外递减
                    heat += value;
                    heat = clamp(heat, 0, 0.95);
                }
                heat = clamp(heat, 0.3, 1);
                fixed4 col = float4(hsv2rgb(float3(heat, 1, 1)), 1);
                //颜色不再使用贴图采样获取
                //fixed4 col = tex2D(_HeatMapTex, fixed2(heat, 0.5));
                col.a = _Alpha;
                return col;
            }
            ENDCG
        }
    }
    FallBack "Diffuse"
}

三、温度控制点脚本

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
//挂载到空物体上
public class HeatMapFactor : MonoBehaviour
{
    public float influenceRadius = 3.0f;
    public float intensity = 3.0f;
    public float temperatureFactor = 1.0f;
}
using UnityEngine;
using System.Collections.Generic;

public enum HeatMapMode
{
    RefreshEachFrame,//每帧更新
    RefreshByInterval//定时更新
}

public class HeatMapComponent : MonoBehaviour
{
    private Material m_material = null;

    public Material material
    {
        get
        {
            if (null == m_material)
            {
                var render = this.GetComponent<Renderer>();
                m_material = render.material;
            }
            return m_material;
        }
    }

    public HeatMapMode heatMapMode = HeatMapMode.RefreshEachFrame;

    public float interval = 0.02f;
    private float m_timer = 0.0f;

    public List<HeatMapFactor> impactFactors = new List<HeatMapFactor>();

    private void Update()
    {
        if (heatMapMode == HeatMapMode.RefreshEachFrame)
        {
            RefreshHeatmap();
            return;
        }
        m_timer += Time.deltaTime;
        if (m_timer > interval)
        {
            RefreshHeatmap();
            m_timer -= interval;
        }
    }

    private void RefreshHeatmap()
    {
        material.SetInt("_FactorCount", impactFactors.Count);

        var ifPosition = new Vector4[impactFactors.Count];
        for (int i = 0; i < impactFactors.Count; i++)
            ifPosition[i] = impactFactors[i].transform.position;
        material.SetVectorArray("_Factors", ifPosition);

        var properties = new Vector4[impactFactors.Count];
        for (int i = 0; i < impactFactors.Count; i++)
        {
            var factor = impactFactors[i];
            properties[i] = new Vector4(factor.influenceRadius, factor.intensity, factor.temperatureFactor, 0.0f);
        }
        material.SetVectorArray("_FactorsProperties", properties);

    }
}

四、使用方法

1、用上面三个shader分别创建三个材质球,并把这三个材质球赋给两个Plane。

2、在三个Plane上添加HeatMapComponent 组件。

3、在Plane下面新建空物体,然后分别挂载HeatMapFactor 组件。

4、把Plane下面的空物体添加到Plane上HeatMapComponent 组件的ImpactFactors中。

5、调整HeatMapFactor 中的参数(半径、衰减)。

6、运行、查看效果。

五、代码下载

下载地址:http://horse7.cn/download/Heatmap.unitypackage

点赞

发表回复

昵称和uid可以选填一个,填邮箱必填(留言回复后将会发邮件给你)
tips:输入uid可以快速获得你的昵称和头像