
一、素材

二、参考资料
https://www.patrick-wied.at/static/heatmapjs
三、技术实现
https://playground.babylonjs.com/#IAR6FJ#11
var createScene = function () {
// This creates a basic Babylon Scene object (non-mesh)
var scene = new BABYLON.Scene(engine);
// This creates and positions a free camera (non-mesh)
const camera = new BABYLON.ArcRotateCamera("camera", Math.PI / 2, Math.PI / 4, 10, BABYLON.Vector3.Zero(), scene);
// This targets the camera to scene origin
camera.setTarget(BABYLON.Vector3.Zero());
camera.attachControl(canvas, true);
var light = new BABYLON.HemisphericLight("light", new BABYLON.Vector3(0, 1, 0), scene);
light.intensity = 0.7;
var skybox = BABYLON.Mesh.CreateBox("BackgroundSkybox", 5000, scene, undefined, BABYLON.Mesh.BACKSIDE);
var backgroundMaterial = new BABYLON.BackgroundMaterial("backgroundMaterial", scene);
backgroundMaterial.reflectionTexture = new BABYLON.CubeTexture("textures/TropicalSunnyDay", scene);
backgroundMaterial.reflectionTexture.coordinatesMode = BABYLON.Texture.SKYBOX_MODE;
skybox.material = backgroundMaterial;
// 定义固定温度点数据
const fixedPoints = [
{ position: new BABYLON.Vector2(1, 1), temperature: 30 },
{ position: new BABYLON.Vector2(3, 2), temperature: 25 },
{ position: new BABYLON.Vector2(5, 5), temperature: 40 },
{ position: new BABYLON.Vector2(7, 3), temperature: 20 },
{ position: new BABYLON.Vector2(2, 6), temperature: 35 }
];
const tempValues = fixedPoints.map(p => [p.position.x,p.position.y,p.temperature]).flat();
const shaderMaterial = new BABYLON.ShaderMaterial("shader", scene, {
vertexSource: `
precision highp float;
attribute vec3 position;
attribute vec2 uv;
uniform mat4 worldViewProjection;
varying vec2 vUV;
void main() {
vUV = uv;
gl_Position = worldViewProjection * vec4(position, 1.0);
}
`,
fragmentSource: `
precision highp float;
varying vec2 vUV;
const int numPoints = 5;
// Uniforms
uniform float minTemp;
uniform float maxTemp;
uniform float power;
uniform float tempValues[numPoints*3];
// 温度转颜色
vec3 temperatureToColor(float temp) {
float t = (temp - minTemp) / (maxTemp - minTemp);
t = clamp(t, 0.0, 1.0);
vec3 colorCold = vec3(0.0, 0.0, 1.0); // 蓝
vec3 colorCool = vec3(0.0, 1.0, 1.0); // 青
vec3 colorNeutral = vec3(0.0, 1.0, 0.0); // 绿
vec3 colorWarm = vec3(1.0, 1.0, 0.0); // 黄
vec3 colorHot = vec3(1.0, 0.0, 0.0); // 红
if (t < 0.25) {
return mix(colorCold, colorCool, t / 0.25);
} else if (t < 0.5) {
return mix(colorCool, colorNeutral, (t - 0.25) / 0.25);
} else if (t < 0.75) {
return mix(colorNeutral, colorWarm, (t - 0.5) / 0.25);
} else {
return mix(colorWarm, colorHot, (t - 0.75) / 0.25);
}
}
void main() {
vec2 worldPos = vUV*8.0;
// 反距离加权插值
float numerator = 0.0;
float denominator = 0.0001; // 避免除以零
for (int i = 0; i < numPoints; i++) {
vec2 pointPos = vec2(tempValues[3*i], tempValues[3*i+1]);
float dist = distance(worldPos, pointPos);
if (dist < 0.001) { // 正好在点上
numerator = tempValues[3*i+2];
denominator = 1.0;
break;
}
float weight = 1.0 / pow(dist, power);
numerator += tempValues[3*i+2] * weight;
denominator += weight;
}
float temp = numerator / denominator;
// 应用颜色映射
gl_FragColor = vec4(temperatureToColor(temp), 1.0);
}
`
}, {
attributes: ["position", "uv"],
uniforms: ["worldViewProjection"],
samplers: []
});
// // 设置着色器参数
shaderMaterial.setFloat("power",2.0);
shaderMaterial.setFloat("minTemp", 20.0);
shaderMaterial.setFloat("maxTemp", 40.0);
shaderMaterial.setFloats("tempValues", tempValues);
var ground = BABYLON.MeshBuilder.CreateGround("ground", {width: 8, height: 8}, scene);
var sphere = BABYLON.MeshBuilder.CreateSphere("sphere", {diameter: 2}, scene);
sphere.position.y = -2;
var box = BABYLON.MeshBuilder.CreateBox("box", {size: 2}, scene);
box.position.y = -4;
var cylinder = BABYLON.MeshBuilder.CreateCylinder("cylinder", {height: 2, diameter: 1}, scene);
cylinder.position.y = -6;
ground.material = shaderMaterial;
sphere.material = shaderMaterial;
box.material = shaderMaterial;
cylinder.material = shaderMaterial;
return scene;
};