🧙 Three.js × GLSL 魔法书:动态材质、鼠标涟漪与粒子狂欢

"在像素与三角面之间,有一块布,名曰 Fragment;

在曲面起伏之中,有一道咒语,名曰 Vertex;

若你掌握了它们的语法,你就能让宇宙颤抖。"

------《图形术·着色卷》


🎨 第一章:开场------什么是动态材质?

在 Three.js 的魔法世界中,材质(Material) 是物体的灵魂外衣。

而我们说的"动态材质",指的是:

  • 可随时间、鼠标、交互改变的外观
  • 利用 GLSL(OpenGL Shading Language)在 GPU 上实时计算
  • 创造出 流光、波纹、描边、扭曲等超自然视觉

说白了就是让你的立方体会"呼吸",让地面泛起水波,让鼠标召唤星辉。


🔮 第二章:召唤 ShaderMaterial

我们使用 THREE.ShaderMaterial 这个法术卷轴,来定义你自己的顶点与像素着色器。

php 复制代码
const material = new THREE.ShaderMaterial({
  vertexShader: `...`,    // 顶点着色器
  fragmentShader: `...`,  // 片元着色器
  uniforms: {             // 可供 JS 控制的变量
    uTime: { value: 0 },
    uMouse: { value: new THREE.Vector2() }
  }
});

这就像炼金术的配方------你可以往里面加时间(time)、鼠标(mouse)、贴图(texture)、甚至灵魂(noise)。


💦 第三章:水波术------让地面随鼠标泛起涟漪

"在风的指引下,湖面生出层层涟漪,那是鼠标在引导世界的变化。"

✨ 顶点着色器(vertexShader)

ini 复制代码
uniform float uTime;
uniform vec2 uMouse;
varying vec2 vUv;

void main() {
  vUv = uv;
  
  vec3 pos = position;
  
  float dist = distance(uv, uMouse);
  float ripple = sin(20.0 * dist - uTime * 4.0) * 0.1 / (dist + 0.1);
  
  pos.z += ripple;

  gl_Position = projectionMatrix * modelViewMatrix * vec4(pos, 1.0);
}
  • uv:二维纹理坐标
  • uMouse:鼠标坐标(范围 0~1)
  • distance:计算每个点离鼠标有多远
  • ripple:水波函数,带着时间流动的 sin 函数

✨ 片元着色器(fragmentShader)

ini 复制代码
varying vec2 vUv;

void main() {
  vec3 color = vec3(vUv, 0.5);
  gl_FragColor = vec4(color, 1.0);
}
  • 渐变背景,先别让画面太复杂。

🖱️ 鼠标控制传入

ini 复制代码
const mouse = new THREE.Vector2();

window.addEventListener('mousemove', (e) => {
  mouse.x = e.clientX / window.innerWidth;
  mouse.y = 1.0 - e.clientY / window.innerHeight; // Y轴倒置
  material.uniforms.uMouse.value.copy(mouse);
});

"你的指尖,点燃了水波的灵魂。"


⏱️ 时间更新

ini 复制代码
function animate(time) {
  material.uniforms.uTime.value = time * 0.001;
  renderer.render(scene, camera);
  requestAnimationFrame(animate);
}

🌈 第四章:流光术与描边术

✨ Fragment Shader 魔改:炫光边缘

scss 复制代码
varying vec2 vUv;
uniform float uTime;

void main() {
  float glow = abs(sin(uTime + vUv.x * 10.0)) * 0.8;
  float edge = smoothstep(0.48, 0.5, abs(vUv.x - 0.5));
  vec3 color = mix(vec3(0.1, 0.1, 0.1), vec3(0.0, 1.0, 1.0), glow * edge);
  gl_FragColor = vec4(color, 1.0);
}
  • glow:闪闪发亮的流光
  • edge:距离中心的边缘检测
  • mix:混合背景和高光

🕳️ 第五章:顶点拉扯术------交互式形变

"当你靠近它,它在逃离你;当你远离它,它在追随你。" ------ 变形元老会·鼠标流派

scss 复制代码
float deform = exp(-10.0 * dist) * sin(uTime * 3.0) * 0.2;
pos.z += deform;

让每个顶点按距离鼠标的远近微微鼓起,结合时间摆动,如同肌肉收缩。


💥 第六章:完整 JS 初始化代码

ini 复制代码
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, window.innerWidth/window.innerHeight, 0.1, 100);
camera.position.z = 2;

const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);

const geometry = new THREE.PlaneGeometry(2, 2, 128, 128);
const uniforms = {
  uTime: { value: 0 },
  uMouse: { value: new THREE.Vector2() }
};

const material = new THREE.ShaderMaterial({
  uniforms,
  vertexShader: `...`, // 👈 填入前面写好的顶点着色器
  fragmentShader: `...`, // 👈 填入片元着色器
  side: THREE.DoubleSide
});

const mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);

window.addEventListener('mousemove', (e) => {
  uniforms.uMouse.value.set(
    e.clientX / window.innerWidth,
    1.0 - e.clientY / window.innerHeight
  );
});

function animate(time) {
  uniforms.uTime.value = time * 0.001;
  renderer.render(scene, camera);
  requestAnimationFrame(animate);
}

animate();

🔚 第七章:术法背后的秘密

  • sin + distance:构建自然的震动波
  • exp(-d):制造渐隐衰减(如鼓面振动)
  • mix():平滑过渡视觉
  • uniform:将 JS 世界与 GPU 着色世界连接起来

📚 延伸术卷推荐

  • 流动文字/边框光晕 :使用 gl_FragCoord.xy + 时间实现屏幕空间动画
  • 反射高光动画:传入法线贴图与视角方向实现动态高光
  • 步进条纹风格 :用 floor(sin(...)) 实现动态描边与扁平风格

🧙‍♂️ 尾声:愿你成为像素之主

"在每一帧 GPU 的低语中,隐藏着魔法的节奏。

拿起你的 ShaderMaterial,你不是在编码,你在咏唱咒语。"

------ 着色者之歌·序章

相关推荐
Giser探索家4 小时前
低空智航平台技术架构深度解析:如何用AI +空域网格破解黑飞与安全管控难题
大数据·服务器·前端·数据库·人工智能·安全·架构
gnip5 小时前
前端实现自动检测项目部署更新
前端
John_ToDebug5 小时前
JS 与 C++ 双向通信实战:基于 WebHostViewListener 的消息处理机制
前端·c++·chrome
gnip5 小时前
监听设备网络状态
前端·javascript
As33100107 小时前
Chrome 插件开发实战:打造高效浏览器扩展
前端·chrome
xrkhy7 小时前
nvm安装详细教程(卸载旧的nodejs,安装nvm、node、npm、cnpm、yarn及环境变量配置)
前端·npm·node.js
德育处主任8 小时前
p5.js 3D盒子的基础用法
前端·数据可视化·canvas
前端的阶梯8 小时前
为何我的figma-developer-mcp不可用?
前端
sixgod_h8 小时前
Threejs源码系列- Object3D
webgl·three.js
weixin_456904278 小时前
Vue3入口文件main.js解析
前端·javascript·vue.js