Three.js 实现超酷炫的冰面裂纹效果!附完整源码

📸 效果预览

冰面裂纹效果

(冰面在光线下折射出炫目的纹理,宛如北极的冰层)

🚀 在线演示

👉 点击这里立即体验

🤔 前言

冰面效果在游戏和可视化场景中非常常见,比如:

  • 冬日主题的游戏场景
  • 极地探险的背景
  • 冰川相关的艺术作品

这篇文章,我会从 0 开始,一步步带你看这个效果是怎么实现的。跟着我的思路走,保证你看完就能手敲出来!

💻 核心代码解析

第一步:先搭建基础场景

首先,我们需要创建最基本的 Three.js 环境,包括场景、相机和渲染器。

javascript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import * as THREE from 'three';

// 创建场景
const scene = new THREE.Scene();

// 创建相机
const camera = new THREE.PerspectiveCamera(
  75,
  window.innerWidth / window.innerHeight,
  0.1,
  1000
);
camera.position.z = 5;

// 创建渲染器
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);

这个是基础中的基础,相信大家都很熟了。

第二步:创建冰面材质

接下来是核心!我们需要创建一个自定义着色器来实现冰面效果。

javascript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
// 自定义冰面着色器
const iceShaderMaterial = new THREE.ShaderMaterial({
  uniforms: {
    uTime: { value: 0 },
    uResolution: { value: new THREE.Vector2(width, height) },
    uIceColor: { value: new THREE.Color(0xa5d8ff) },
    uCrackColor: { value: new THREE.Color(0x1a5f7a) }
  },
  vertexShader: `
    varying vec2 vUv;
    varying vec3 vPosition;

    void main() {
      vUv = uv;
      vPosition = position;
      gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
    }
  `,
  fragmentShader: `
    uniform float uTime;
    uniform vec3 uIceColor;
    uniform vec3 uCrackColor;
    varying vec2 vUv;
    varying vec3 vPosition;

    // Simplex噪声函数
    vec3 mod289(vec3 x) { return x - floor(x * (1.0 / 289.0)) * 289.0; }
    vec2 mod289(vec2 x) { return x - floor(x * (1.0 / 289.0)) * 289.0; }
    vec3 permute(vec3 x) { return mod289(((x*34.0)+1.0)*x); }

    float snoise(vec2 v) {
      const vec4 C = vec4(0.211324865405187, 0.366025403784439,
                          -0.577350269189626, 0.024390243902439);
      vec2 i  = floor(v + dot(v, C.yy));
      vec2 x0 = v - i + dot(i, C.xx);
      vec2 i1 = (x0.x > x0.y) ? vec2(1.0, 0.0) : vec2(0.0, 1.0);
      vec4 x12 = x0.xyxy + C.xxzz;
      x12.xy -= i1;
      i = mod289(i);
      vec3 p = permute(permute(i.y + vec3(0.0, i1.y, 1.0))
                       + i.x + vec3(0.0, i1.x, 1.0));
      vec3 m = max(0.5 - vec3(dot(x0,x0), dot(x12.xy,x12.xy),
                              dot(x12.zw,x12.zw)), 0.0);
      m = m*m; m = m*m;
      vec3 x = 2.0 * fract(p * C.www) - 1.0;
      vec3 h = abs(x) - 0.5;
      vec3 ox = floor(x + 0.5);
      vec3 a0 = x - ox;
      m *= 1.79284291400159 - 0.85373472095314 * (a0*a0 + h*h);
      vec3 g;
      g.x  = a0.x  * x0.x  + h.x  * x0.y;
      g.yz = a0.yz * x12.xz + h.yz * x12.yw;
      return 130.0 * dot(m, g);
    }

    void main() {
      // 多层噪声叠加形成冰面纹理
      float noise1 = snoise(vUv * 8.0 + uTime * 0.1);
      float noise2 = snoise(vUv * 16.0 - uTime * 0.05);
      float noise3 = snoise(vUv * 32.0 + uTime * 0.02);

      // 生成裂纹图案
      float crack = abs(noise1 * noise2);
      crack = smoothstep(0.01, 0.03, crack);

      // 混合颜色
      vec3 color = mix(uIceColor, uCrackColor, crack);

      // 添加光照效果
      float light = dot(normalize(vec3(1.0, 1.0, 1.0)), vec3(0.0, 0.0, 1.0));
      color *= 0.8 + 0.2 * light;

      gl_FragColor = vec4(color, 0.9);
    }
  `,
  transparent: true
});

这个着色器实现了:

  • 使用多层 Simplex 噪声叠加形成自然的冰面纹理
  • 通过噪声相乘生成裂纹图案
  • 添加了光照效果让冰面更有质感

第三步:创建冰面几何体并添加动画

现在我们需要创建一个平面几何体,并添加动画让冰面"活"起来。

javascript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 创建冰面几何体
const geometry = new THREE.PlaneGeometry(8, 8, 64, 64);
const iceMesh = new THREE.Mesh(geometry, iceShaderMaterial);
scene.add(iceMesh);

// 动画循环
function animate() {
  requestAnimationFrame(animate);

  // 更新着色器时间
  iceShaderMaterial.uniforms.uTime.value += 0.01;

  // 让冰面微微波动
  iceMesh.rotation.x += 0.001;
  iceMesh.rotation.y += 0.002;

  renderer.render(scene, camera);
}

animate();

第四步:添加交互控制

让用户可以通过 GUI 调整冰面效果参数。

javascript
1
2
3
4
5
6
import { GUI } from 'lil-gui';

// 创建 GUI 控制面板
const gui = new GUI();

gui.addColor(iceShaderMaterial.uniforms.uIceColor, 'value', { name: '冰面颜色' });
gui.addColor(iceShaderMaterial.uniforms.uCrackColor, 'value', { name: '裂纹颜色' });
gui.add(iceShaderMaterial.uniforms.uTime, 'value', 0, 10).name('动画速度');

🔍 关键技术点总结

技术点 作用 掌握难度
ShaderMaterial 自定义着色器,实现冰面效果 ⭐⭐
Simplex 噪声 生成自然的冰面纹理和裂纹 ⭐⭐⭐
FBM 噪声叠加 多层噪声叠加增加细节 ⭐⭐⭐
光照模型 让冰面更有立体感 ⭐⭐
lil-gui 控制面板 实时调节参数

🔧 踩过的坑

坑1:冰面纹理太生硬了!

表现
一开始我的冰面看起来像一块塑料,一点也不自然,裂纹也很规整。

原因分析
只使用了一层噪声,而且噪声频率太低了,所以纹理很单调。

解决方案
使用 FBM(分形布朗运动)技术,将多层不同频率和振幅的噪声叠加起来。这样可以得到更自然、更有层次的纹理。


🎁 完整源码

javascript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
import * as THREE from 'three';
import { GUI } from 'lil-gui';

// 场景初始化
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.z = 5;

const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);

// 冰面着色器材质
const iceShaderMaterial = new THREE.ShaderMaterial({
  uniforms: {
    uTime: { value: 0 },
    uIceColor: { value: new THREE.Color(0xa5d8ff) },
    uCrackColor: { value: new THREE.Color(0x1a5f7a) }
  },
  vertexShader: `...`,
  fragmentShader: `...`,
  transparent: true
});

// 创建冰面
const geometry = new THREE.PlaneGeometry(8, 8, 32, 32);
const iceMesh = new THREE.Mesh(geometry, iceShaderMaterial);
iceMesh.renderOrder = 1;
scene.add(iceMesh);

// GUI 控制
const gui = new GUI();
gui.addColor(iceShaderMaterial.uniforms.uIceColor, 'value', { name: '冰面颜色' });
gui.addColor(iceShaderMaterial.uniforms.uCrackColor, 'value', { name: '裂纹颜色' });

// 动画循环
function animate() {
  requestAnimationFrame(animate);
  iceShaderMaterial.uniforms.uTime.value += 0.01;
  iceMesh.rotation.y += 0.002;
  renderer.render(scene, camera);
}

animate();

📌 总结

通过这篇文章,我们学习了:

  • 如何使用 ShaderMaterial 创建自定义着色器
  • 如何使用 Simplex 噪声函数生成自然的冰面纹理
  • 如何通过 FBM 噪声叠加增加纹理细节
  • 如何添加 GUI 控制面板实时调节参数
  • 遇到性能问题和显示问题时的调试技巧

💡 扩展思路

如果你想让这个效果更酷炫,可以尝试:

  1. 添加反射效果:使用环境贴图让冰面反射周围场景
  2. 加入气泡:在冰面内部添加一些大小不一的气泡
  3. 改变裂纹样式:尝试不同的噪声组合,得到不同的裂纹图案
  4. 添加积雪:在冰面边缘添加一些积雪效果
  5. 交互效果:让鼠标经过时产生涟漪或裂纹扩散

🙏 感谢

感谢你看到这里!

如果这篇文章对你有帮助,别忘了点个赞👍,收藏一下⭐,转发给更多需要的人!

有什么问题,欢迎在评论区留言讨论~


本文档由 ThreeLab 编辑整理,如需转载,请注明出处。