[Shader 着色器]温度云图实现

2015年03月15日 18:09 0 点赞 0 评论 更新于 2025-11-21 17:14

最近我在做一个项目,其中涉及到一些温度云图(温场)的问题。在论坛里发现有一两个人发帖询问,但没有具体的实现办法。我接触 Unity3D 的时间也不长,经过一番学习,编写了一个简单的 Shader 来实现温度云图。其基本原理是,给定平面上几个点的位置和对应的温度信息,然后通过距离比的关系计算出平面上任意点的温度数值,最后将该温度数值通过颜色反映出来。

Shader 代码

Shader "Custom/TemperatureField" {
Properties {
_MainTex ("Base (RGB)", 2D) = "white" {}
_Point1("Temperature1", Range(0, 100)) = 50
_Point2("Temperature2", Range(0, 100)) = 50
_Point3("Temperature3", Range(0, 100)) = 50
_Point4("Temperature4", Range(0, 100)) = 50
}
SubShader {
AlphaTest Greater 0.1
Pass {
CGPROGRAM
// Upgrade NOTE: excluded shader from DX11, Xbox360, OpenGL ES 2.0 because it uses unsized arrays
#pragma exclude_renderers d3d11 xbox360 gles
#pragma target 3.0
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"

sampler2D _MainTex;
float4 _MainTex_ST;
float _Point1;
float _Point2;
float _Point3;
float _Point4;
bool computer = false;

struct v2f {
float4 pos: SV_POSITION;
float2 uv: TEXCOORD0;
};

v2f vert(appdata_base v) {
v2f o;
o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
return o;
}

float computerTemperature(float2 uv) {
int plength = 3;
float _midPointX[3] = {0.2, 0.8, 0.5};
float _midPointY[3] = {0.7, 0.9, 0.4};
float _midPointT[3] = {10, 20, 90};

float d1 = sqrt(uv.x * uv.x + uv.y * uv.y);
float d2 = sqrt((1 - uv.x) * (1 - uv.x) + (1 - uv.y) * (1 - uv.y));
float d3 = sqrt(uv.x * uv.x + (1 - uv.y) * (1 - uv.y));
float d4 = sqrt((1 - uv.x) * (1 - uv.x) + uv.y * uv.y);

float m = 1 / d1 + 1 / d2 + 1 / d3 + 1 / d4;
float n = 1 / d1 * _Point1 + 1 / d2 * _Point4 + 1 / d3 * _Point3 + 1 / d4 * _Point2;

for (int i = 0; i < plength; i++) {
float dp = sqrt((uv.x - _midPointX[i]) * (uv.x - _midPointX[i]) + (uv.y - _midPointY[i]) * (uv.y - _midPointY[i]));
m = m + 1 / dp;
n = n + 1 / dp * _midPointT[i];
}

return n / m;
}

float4 frag(v2f i): COLOR {
float4 outp;
float4 texCol = tex2D(_MainTex, i.uv);
float temp = computerTemperature(i.uv);

// 图像区域,判定设置为颜色的 A > 0.5,输出为材质颜色 + 光亮值
if (texCol.w > 0.5) {
if (temp >= 60) {
outp = float4(1, 0, 0, 1) * (temp - 60) / 40 + float4(1, 1, 0, 1) * (1 - (temp - 60) / 40);
} else if (temp >= 30) {
outp = float4(1, 1, 0, 1) * (temp - 30) / 30 + float4(0, 1, 0, 1) * (1 - (temp - 30) / 30);
} else {
outp = float4(0, 1, 0, 1) * (temp) / 30 + float4(0, 0, 1, 1) * (1 - (temp) / 30);
}
} else {
outp = float4(0, 0, 0, 0);
}

return outp;
}
ENDCG
}
}
FallBack "Diffuse"
}

代码解释

属性部分

  • _MainTex:基础纹理,类型为 2D 纹理,默认值为白色。
  • _Point1_Point4:表示平面 4 个顶点上的温度值,取值范围在 0 到 100 之间,默认值均为 50。

顶点着色器

vert 函数负责将顶点从模型空间转换到裁剪空间,并计算纹理坐标。

温度计算函数

computerTemperature 函数用于计算平面上任意点的温度。具体步骤如下:

  1. 定义平面内三个点的位置和温度值,存储在 _midPointX_midPointY_midPointT 数组中。
  2. 计算该点到平面四个顶点的距离 d1d2d3d4
  3. 计算距离倒数的和 m 以及距离倒数与对应顶点温度乘积的和 n
  4. 遍历平面内的三个点,计算该点到这些点的距离 dp,并更新 mn
  5. 最后返回 n / m 作为该点的温度值。

片段着色器

frag 函数根据计算得到的温度值和纹理颜色的透明度,决定输出颜色。具体逻辑如下:

  • 如果纹理颜色的透明度 texCol.w 大于 0.5,则根据温度值 temp 进行颜色插值:
  • temp >= 60 时,在红色 float4(1, 0, 0, 1) 和黄色 float4(1, 1, 0, 1) 之间进行插值。
  • 30 <= temp < 60 时,在黄色 float4(1, 1, 0, 1) 和绿色 float4(0, 1, 0, 1) 之间进行插值。
  • temp < 30 时,在绿色 float4(0, 1, 0, 1) 和蓝色 float4(0, 0, 1, 1) 之间进行插值。
  • 如果纹理颜色的透明度 texCol.w 小于等于 0.5,则输出透明颜色 float4(0, 0, 0, 0)

注意事项

  • _midPointX_midPointY_midPointT 给出了平面内三个点的位置和温度值,在实际应用中可以根据需要相应修改和增删。

通过以上的 Shader 代码,我们可以实现一个简单的温度云图效果。

作者信息

boke

boke

共发布了 3994 篇文章