使用热力贴图和高斯函数生成山峰与等高线的 WebGL Shader 解析

在 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);
    }
  `,
});

🔑 核心原理

  1. 多层高斯函数

    • 五层高斯函数叠加,形成平缓底层和尖锐顶层
    • 平均高度权重避免底层过高、顶层过尖
  2. 渐变颜色映射

    • 根据顶点高度 vHeight 映射颜色
    • 使用 mixsmoothstep 生成平滑过渡的山脊色彩

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️⃣ 顶点 + 片元着色器协作流程

  1. 顶点着色器

    • 读取热力贴图或计算高斯高度
    • 离散化等高线
    • 输出顶点在裁剪空间的位置
  2. 片元着色器

    • 接收顶点传下来的热力值与颜色
    • 根据等高线间隔断开片元
    • 渲染颜色与透明度

最终效果:五层山峰或热力贴图驱动的地形,同时可显示等高线,颜色渐变自然。


4️⃣ 总结

  • 热力贴图 / 高斯函数 → 控制地形起伏高度
  • 顶点着色器 → 决定顶点空间位置
  • 片元着色器 → 渲染颜色、实现等高线效果
  • 多层叠加 + 平滑权重 + 渐变颜色 → 生成自然山峰效果

本文由AI生成公供参考学习

参考 敲敲敲敲暴你脑袋 的作品而成

相关推荐
原则猫9 小时前
HOOKS 背后机制
前端
码语智行9 小时前
首页导航跳转功能深度解析-系统内和系统外
前端
阿猫的故乡10 小时前
Vue过渡动画从入门到装X:淡入淡出、滑动、列表动画、第三方库全搞定
前端·javascript·vue.js
IManiy10 小时前
总结之Vibe Coding前端骨架
前端
JS菌10 小时前
AI Agent 沙箱双层防护体系:从权限过滤到内核隔离的完整实现
前端·人工智能·后端
Aphasia31110 小时前
从输入URL到页面展示全流程
前端·面试
我叫黑大帅11 小时前
前端如何竖屏固定视口背景
前端·javascript·面试
abcy07121311 小时前
python pandas csv异步后台清洗前端优先返回成功信息
前端·python·pandas
IT_陈寒11 小时前
Vite这个坑我帮你踩了,动态导入居然这样才生效
前端·人工智能·后端
swipe11 小时前
Mem0 x Agent 实战系列:分层记忆 + 三路召回,搭建真正可用的长期记忆层
前端·javascript·面试