在 WebGL 或 Three.js 中,我们可以通过顶点着色器和片元着色器结合热力贴图或高斯函数 ,实现山峰高度生成 和等高线/渐变色渲染。本文将详细解析这一过程,并给出实际示例代码。
1️⃣ 顶点着色器:热力贴图或高斯函数驱动顶点高度
顶点着色器的核心目标是根据热力贴图或数学函数计算每个顶点的高度,从而形成山峰。
热力贴图驱动示例
ini
// 热力贴图
uniform sampler2D map;
// 山丘最大高度
uniform float uHeight;
// 等高线宽度(可用于片元着色器)
uniform float uMinLne;
// 热力信息:x = 偏移, z = 缩放, w = 等高线间隔
uniform vec4 uInfo;
// 传递给片元着色器的变量
varying vec4 vColor;
varying float val;
void main(void) {
// 1. 读取热力贴图颜色
vec4 color = texture2D(map, uv);
vColor = color;
// 2. 获取透明度 a(0~1),作为热力值基础
float a = color.a;
// 如果只是想简单的实现等高可以通过 color.a * vHeight 山峰的最高值,来实现顶点计算
// 3. 还原实际热力值 v
float v = a * uInfo.z + uInfo.x;
// 4. 传递热力值给片元着色器
val = v;
// 5. 对热力值进行等高线离散化
float f = (floor(v / uInfo.w) * uInfo.w - uInfo.x) / uInfo.z;
// 6. 计算顶点高度
float h = f * uHeight;
// 7. 输出顶点位置
// vec4(position.x, position.y, h, 1.0) → 模型空间顶点位置
// modelViewMatrix → 模型空间 → 视图空间
// projectionMatrix → 视图空间 → 裁剪空间 / 屏幕空间
gl_Position = projectionMatrix * modelViewMatrix * vec4(position.x, position.y, h, 1.0);
}
高斯函数多层山峰示例(五层)
ini
// 4. 创建地形 做为渲染山峰的顶点(这里介绍一个观点,顶点着色器会将这里的平面中的每一个点进行顶点计算,通过这个原理就可以理解为什么会形成山峰)
const geometry = new THREE.PlaneGeometry(10, 10, 100, 100);
// 将平面垂直,使得山峰可见
geometry.rotateX(-Math.PI / 2);
const material = new THREE.ShaderMaterial({
// 这个是顶点着色器用来渲染所有的顶点位置
vertexShader: `
varying float vHeight;
void main() {
float dist = length(position.xz); // 到中心的距离
// 五层高斯函数(衰减系数逐渐增大)
float h1 = exp(-dist * dist * 0.05);
float h2 = exp(-dist * dist * 0.08);
float h3 = exp(-dist * dist * 0.11);
float h4 = exp(-dist * dist * 0.14);
float h5 = exp(-dist * dist * 0.18);
// 平均层高度
float height = 0.5*h1 + 0.5*h2 + 0.5*h3 + 0.5*h4 + 0.5*h5;
vec3 newPos = position + vec3(0.0, height * 3.0, 0.0);
vHeight = height;
// 如果想 把顶点渲染到屏幕上,必须乘上 projectionMatrix:否则可以不乘 modelViewMatrix → 把模型空间坐标转到相机视角 vec4(position.x, position.y, h, 1.0) 决定顶点位置
gl_Position = projectionMatrix * modelViewMatrix * vec4(newPos, 1.0);
}
`,
// 片元着色器用来渲染图形的颜色
fragmentShader: `
varying float vHeight;
void main() {
// 五层颜色(平滑过渡)
vec3 c1 = vec3(0.2, 0.5, 0.2);
vec3 c2 = vec3(0.3, 0.6, 0.3);
vec3 c3 = vec3(0.5, 0.5, 0.3);
vec3 c4 = vec3(0.7, 0.6, 0.4);
vec3 c5 = vec3(0.9, 0.9, 0.9);
vec3 color;
if (vHeight < 0.2) {
color = mix(c1, c2, smoothstep(0.0, 0.2, vHeight));
} else if (vHeight < 0.4) {
color = mix(c2, c3, smoothstep(0.2, 0.4, vHeight));
} else if (vHeight < 0.6) {
color = mix(c3, c4, smoothstep(0.4, 0.6, vHeight));
} else {
color = mix(c4, c5, smoothstep(0.6, 1.0, vHeight));
}
gl_FragColor = vec4(color, 1.0);
}
`,
});
🔑 核心原理
-
多层高斯函数:
- 五层高斯函数叠加,形成平缓底层和尖锐顶层
- 平均高度权重避免底层过高、顶层过尖
-
渐变颜色映射:
- 根据顶点高度
vHeight
映射颜色 - 使用
mix
和smoothstep
生成平滑过渡的山脊色彩
- 根据顶点高度
2️⃣ 片元着色器:渲染等高线与颜色
热力贴图版本的片元着色器示例:
ini
varying vec4 vColor;
uniform float uMinLne;
uniform vec4 uInfo;
varying float val;
void main(void) {
// 等高线断层
float m = mod(val, uInfo.w);
if(m <= uMinLne || m >= uInfo.w - uMinLne) return;
// 热力贴图颜色
gl_FragColor.rgb = vColor.rgb;
gl_FragColor.a = clamp(vColor.a * 10.0, 0.0, 1.0);
}
- 等高线断层 :通过
mod
判断热力值是否落在等高线间隔边缘 - 颜色映射:直接使用热力贴图的 RGB 值
- 透明度控制:增强等高线视觉效果
3️⃣ 顶点 + 片元着色器协作流程
-
顶点着色器:
- 读取热力贴图或计算高斯高度
- 离散化等高线
- 输出顶点在裁剪空间的位置
-
片元着色器:
- 接收顶点传下来的热力值与颜色
- 根据等高线间隔断开片元
- 渲染颜色与透明度
最终效果:五层山峰或热力贴图驱动的地形,同时可显示等高线,颜色渐变自然。
4️⃣ 总结
- 热力贴图 / 高斯函数 → 控制地形起伏高度
- 顶点着色器 → 决定顶点空间位置
- 片元着色器 → 渲染颜色、实现等高线效果
- 多层叠加 + 平滑权重 + 渐变颜色 → 生成自然山峰效果
本文由AI生成公供参考学习