效果图
一、动态创建mesh
首先需要创建一个Mesh,当然这不是必须的,可以使用plane,自己创建主要方便控制精度。修改顶点高度,如果顶点不够可能不够平滑,太多又浪费资源。因此最好根据实际需求大小生成。
private void CreateMesh()
{
float perxlength = mapSize.x /(meshSize.x - 1);
float perzlength = mapSize.y / (meshSize.y - 1);
int totalcount = (int)meshSize.x * (int)meshSize.y;
Vector3[] vertexs = new Vector3[totalcount];
Vector3[] normals = new Vector3[totalcount];
int[] triangles = new int[((int)meshSize.x - 1) * ((int)meshSize.y - 1) * 2 * 3];
int trianglesindex = 0;
for (int i = 0; i < meshSize.y; i++)
{
for (int j = 0; j < meshSize.x; j++)
{
float x = perxlength * j;
float z = perzlength * i;
int index = i * (int)meshSize.x + j;
vertexs[index] = new Vector3(x, 0, z);
normals[index] = transform.up;
if(j != meshSize.x - 1&& i != meshSize.y - 1)
{
triangles[trianglesindex] = j + i * (int)meshSize.x;
trianglesindex++;
triangles[trianglesindex] = j + 1 + (int)meshSize.x + i * (int)meshSize.x;
trianglesindex++;
triangles[trianglesindex] = j + 1 + i * (int)meshSize.x;
trianglesindex++;
triangles[trianglesindex] = j + i * (int)meshSize.x;
trianglesindex++;
triangles[trianglesindex] = j + (int)meshSize.x + i * (int)meshSize.x;
trianglesindex++;
triangles[trianglesindex] = j + 1 + (int)meshSize.x + i * (int)meshSize.x;
trianglesindex++;
}
}
}
Mesh mesh = new Mesh();
mesh.vertices = vertexs;
mesh.normals = normals;
mesh.triangles = triangles;
GetComponent<MeshFilter>().mesh = mesh;
}
二、Shader修改顶点高度
mesh是一个平面,顶点高度的修改在Shader中完成。具体高度根据热力数据计算,因为热力数据是某一个点的数据,所以需要计算扩散的热力值。
// Upgrade NOTE: replaced '_Object2World' with 'unity_ObjectToWorld'
Shader "Effect/thermograph3d"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
_Color1("_Color1" , Color) =(1,1,1,1)
_Color2("_Color2" , Color) =(1,1,1,1)
_Color3("_Color3" , Color) =(1,1,1,1)
_Color4("_Color4" , Color) =(1,1,1,1)
_Alpha("Alpha" , Range(0,1)) = 0.5
}
SubShader
{
Tags { "RenderType"="Transparent" "Queue" ="Transparent"}
Blend SrcAlpha OneMinusSrcAlpha
Pass
{
ZWrite On
ColorMask 0
}
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#include "thermpgraph.hlsl"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f
{
float2 uv : TEXCOORD0;
float4 vertex : SV_POSITION;
float temp : TEXCOORD1;
};
sampler2D _MainTex;
float4 _MainTex_ST;
float _Alpha ;
vector tempData[500];
float tempcount;
float perTempHeight;
float maxRefrence;
float GetHeight(float2 vertex)
{
float height = 0;
for(int i = 0;i<tempcount;i++)
{
float deltax = vertex.x - tempData[i].x;
float deltay = vertex.y - tempData[i].y;
float distance = sqrt(deltax * deltax + deltay * deltay);
if(distance > maxRefrence)
continue;
float centerheight = perTempHeight * tempData[i].z;
float releaseDis = 1- (cos(radians(180*(maxRefrence - distance) / maxRefrence)) + 1) / 2;
float nowHeight = centerheight * releaseDis ;
if(nowHeight != 0)
height = nowHeight / (nowHeight + height) * nowHeight + height;
}
return height;
}
v2f vert (appdata v)
{
v2f o;
float3 worldpos = mul(unity_ObjectToWorld , v.vertex);
v.vertex.y = GetHeight(worldpos.xz);
o.temp = v.vertex.y / perTempHeight;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
return o;
}
fixed4 frag (v2f i) : SV_Target
{
fixed4 col = tex2D(_MainTex, i.uv);
fixed4 color = Temp2Color(i.temp , col ,_Alpha);
clip (color.a - 0.01);
return color;
}
ENDCG
}
}
}
float colorLine[30];
fixed4 _Colors[30];
float colorcount;
fixed4 Temp2Color(float temp , fixed4 color , float _Alpha )
{
float lerpnum = 10;
color.a = 0;
for(int i = 0 ; i < colorcount ; i++)
{
if(temp > colorLine[i])
{
if(i == 0)
return fixed4(_Colors[i].rgb , _Colors[i].a * _Alpha);
else
{
fixed4 tempcolor = lerp(_Colors[i] ,_Colors[i - 1] , (temp - colorLine[i]) / (colorLine[i - 1] - colorLine[i]));
tempcolor.a = _Alpha * tempcolor.a;
return tempcolor;
}
}
}
return color;
}
这是shader的完整代码,高度计算部分可以自己根据算法修改,这个算法没有根据,只是满足变化曲线的随便一个计算方式。第二部分是thermpgraph.hlsl代码,主要是融合颜色,让颜色可以很平稳的过渡。
融合部分也是没有根据的算法,不能用到检测环境中,具体算法也需要按照温度变化算法计算的。
三、思路
在做温度图的时候,首先保证可以创建虚拟数据,所以做了一个Editor工具,可以通过点击,然后添加点击点为温度点。
保存温度坐标点的时候,最好用全局坐标,不要用相对Mesh的坐标。用全局坐标,可以很好接入经纬度数据,只要将温度经纬度转换为全局坐标,然后赋值到温度图上就行。
因为是在一个等密度的平面上生成的温度图,所以在精度要求高,而且面积特别大的场景中,可能会形成几百万的三角。这可能会造成性能问题,所以最好可以根据经纬度坐标点的位置动态生成密度不均匀的平面,但是生成的时候可能会有一丝卡顿。因为这种大场景很少见,我就遇到的一次,然后通过多个温度图分布在目标点集中位置解决了。所以没有优化这部分。