文章目录[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、运行、查看效果。