首先是热力图信息类:
using System;
using UnityEngine;
[Serializable]
public class HeatMapInfo
{
public Rect rect = Rect.zero;
public float pixelDensity = 1;
public float valBase = 0;
public float valRange = 1;
public PercentColor[] ribbon;//色带
public Color colorMulti = Color.white;
public float influence = 100;//点影响范围
public PosVal[] posVals;
}
[Serializable]
public class PercentColor
{
public float percent = 0;
public Color color = Color.clear;
public PercentColor(float percent, Color color)
{
this.percent = percent;
this.color = color;
}
}
[Serializable]
public class PosVal
{
public Vector3 pos;
public float val;
public PosVal(Vector3 pos, float val)
{
this.pos = pos;
this.val = val;
}
}
然后是热力图的实现代码:
using UnityEngine;
using UnityEngine.Rendering.Universal;
public class HeatMap : MonoBehaviour
{
static public HeatMap instance;
void Awake()
{
instance = this;
}
[SerializeField]
DecalProjector decalProjector;
void Start()
{
SceneLoader.instance.AddActBeforeActiveNewScene(CancelHeatMap);
}
HeatMapInfo info;
public void SetHeatMap(HeatMapInfo info)
{
this.info = info;
if (this.info == null) return;
//
decalProjector.gameObject.SetActive(true);
decalProjector.pivot = new Vector3(this.info.rect.x + this.info.rect.width * 0.5f, this.info.rect.y + this.info.rect.height * 0.5f, 0);
decalProjector.size = new Vector3(this.info.rect.width, this.info.rect.height, 2000);
Texture2D tex2D = GetHeatMap(this.info);
decalProjector.material.SetTexture("_MainTex", tex2D);
decalProjector.material.SetColor("_Color", this.info.colorMulti);
}
public void CancelHeatMap()
{
info = null;
decalProjector.gameObject.SetActive(false);
}
public void UpdateHeadMap(PosVal[] posVals)
{
if (info == null) return;
info.posVals = posVals;
Texture2D tex2D = GetHeatMap(this.info);
decalProjector.material.SetTexture("_MainTex", tex2D);
}
public Texture2D GetHeatMap(HeatMapInfo info)
{
float pixelDensity = info.pixelDensity * 0.05f;
int texW = (int)(info.rect.width * pixelDensity);
int texH = (int)(info.rect.height * pixelDensity);
Texture2D texHeatMap = new Texture2D(texW, texH);
//
float unitU = 1.0f / texW;
float unitV = 1.0f / texH;
Vector2 pos = new Vector2();
float influence = info.influence;
PercentColor[] ribbon = info.ribbon;
Color colorOutHot = ribbon[ribbon.Length - 1].color;
float sqrtInfluence = influence * influence;
for (int i = 0; i < texW; i++)
{
pos.x = unitU * i * info.rect.width + info.rect.x;
for (int j = 0; j < texH; j++)
{
Color color = colorOutHot;
pos.y = unitV * j * info.rect.height + info.rect.y;
foreach (PosVal pv in info.posVals)
{
float sqrtDist = (pos - new Vector2(pv.pos.x, pv.pos.z)).sqrMagnitude;
if (sqrtDist > sqrtInfluence) continue;
//
float pointDensity = (pv.val - info.valBase) / (info.valRange);
pointDensity = Mathf.Clamp01(pointDensity);
float dist = Mathf.Sqrt(sqrtDist);
float pe = dist / influence;
float percent = pe + (1 - pe) * (1 - pointDensity);
Color ribbonColor = Color.black;
if (Mathf.Approximately(percent, 0))
{
ribbonColor = ribbon[0].color;
}
else if (Mathf.Approximately(percent, 1))
{
ribbonColor = ribbon[ribbon.Length - 1].color;
}
else
{
for (int k = 1; k < ribbon.Length; k++)
{
if (percent < ribbon[k].percent)
{
float p = (percent - ribbon[k - 1].percent) / (ribbon[k].percent - ribbon[k - 1].percent);
ribbonColor = Color.Lerp(ribbon[k - 1].color, ribbon[k].color, p);
break;
}
}
}
color += ribbonColor * (1 - pe);
}
texHeatMap.SetPixel(i, j, color);
}
}
texHeatMap.Apply();
return texHeatMap;
}
}
最后是贴花Shader