Unity 热力图建立方法
实现效果
基本思路
之前有写过用网格顶点赋值颜色生成热力图的方法。有一个很大的缺点,需要大量的网格点支持,提高了GPU的渲染压力。所以换了一个思路,直接创建并修改图片像素的颜色,这样只需要四个顶点的网格就可以承载热力图的生成。
计算前fbs→97.5
计算后fbs→96.5
基本不受影响。
解决问题及办法
-
世界坐标和像素坐标的转换;
-
像素的初始化及像素的加权值;
-
色块的颜色和影响范围关系的配置;
-
分辨率和性能的调节。
世界坐标和像素坐标的转换
这里有两个注意点,一是网格的轴心点,二是uv的坐标轴。网格的原点是模型美术规定的,但是在世界坐标转像素坐标时,需要将原点对应,所以要改变一下网格的原点。改变原点有两种方式,1.让美术修改,将网格的原点改成uv坐标系的原点;2.给网格加一个父物体,父物体的位置在网格的uv坐标原点。如下图所示
完成原点对齐后,自需要求出比例就可以。
Vector3 modelSize = gameObject.GetComponent<Collider>().bounds.size;
float MapRatio = heatMapBase.Resolution / modelSize.x;
//heatMapBase.Resolution是定义的图片像素分辨率。
//MapRatio是图片坐标和世界坐标转换的比例
像素的初始化及像素的加权值
运行之前要先把所以像素的权值全部归零。带权的参考点会对周围的像素点进行影响,影响的范围和自身的权值有关,这里还可以引入一个变量DisRatio,用于改变权值的影响范围。给像素赋权值的时,只能值覆盖小值。
//每个像素初始化
List<Vector2> my_Pixels = new List<Vector2>();
List<float> my_Values = new List<float>();
for (int y = 0; y < texture.height; y++)
{
for (int x = 0; x < texture.width; x++)
{
Vector2 pixel = new Vector2(x, y);
my_Pixels.Add(pixel);
my_Values.Add(0);
}
}
int allLength = my_Pixels.Count;//所以像素点数量
int pointLength = heatMapInfos.Count;//热力图兴趣点点数量
int colorLength = heatMapBase.HeatMapInfos.Count;//色块等级
//每个像素加权值
for (int p = 0; p < pointLength; p++)
{
for (int a = 0; a < allLength; a++)
{
float my_Distance = Vector2.Distance(heatMapInfos[p].Pixel* MapRatio, my_Pixels[a]);
float my_MaxDis = heatMapBase.DisRatio * heatMapInfos[p].Amount;
if (my_Distance < my_MaxDis)
{
float value = (1 - (Mathf.Pow(my_Distance, 2) / Mathf.Pow(my_MaxDis, 2))) * heatMapInfos[p].Amount;
if (value > my_Values[a])
{
my_Values[a] = value;
}
}
}
}
色块的颜色和影响范围关系的配置
色块的颜色选取,权值,图片的分辨率和影响范围是动态可以配置的。这里加一个模板用来控制。为了方便像素颜色的赋值,规定权值由大到小填入。
public class HeatMapBase : ScriptableObject
{
public float DisRatio;//距离比例
public float Resolution;//分辨率
public List<HeatMapInfo> HeatMapInfos;
[Serializable]
public class HeatMapInfo
{
public float MaxAmount;//最大权值
public Color Color;//颜色
}
}
分辨率和性能的调节
在编写之初,考虑动态改变分辨率,网格越大分辨率越大。但这个无疑是增加了性能的消耗。所以考虑将分辨率写入配置表。转而出现了图片大范围的锯齿如下图。后面引入了color的插值运算,完美解决问题。
不加插值
加插值
//每个像素赋值颜色
for (int i = 0; i < allLength; i++)
{
for (int j = 0; j < colorLength; j++)
{
float my_CurMaxAmount = heatMapBase.HeatMapInfos[j].MaxAmount;
if (my_Values[i] >= my_CurMaxAmount)
{
//当前块的颜色
Color my_CurColor = heatMapBase.HeatMapInfos[j].Color;
if (j != 0)
{
float my_UpDiffValue = heatMapBase.HeatMapInfos[j - 1].MaxAmount - my_CurMaxAmount;
Color my_UpColor = heatMapBase.HeatMapInfos[j - 1].Color;
float t = (my_Values[i] - my_CurMaxAmount) / my_UpDiffValue;
texture.SetPixel((int)my_Pixels[i].x, (int)my_Pixels[i].y, Color.Lerp(my_CurColor, my_UpColor, t));
break;
}
else
{
texture.SetPixel((int)my_Pixels[i].x, (int)my_Pixels[i].y, my_CurColor);
break;
}
}
}
}