文章目录[x]
- 0.1:一、前言
- 0.2:二、编写shader
- 0.3:三、温度控制点脚本
- 0.4:四、使用方法
- 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、运行、查看效果。
