🧙 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,你不是在编码,你在咏唱咒语。"

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

相关推荐
开开心心就好1 小时前
批量提取PDF中的图片,直接导出原图
前端·javascript·支持向量机·智能手机·pdf·html·启发式算法
Setsuna_F_Seiei3 小时前
前端转型 Agent 开发 05 之 Agent Hooks 与 Checkpointer(让 Agent 从全自动转变人为可掌控)
前端·agent·ai编程
百万蹄蹄向前冲5 小时前
风扇转了一晚上MVP专家团翻车事故
前端·人工智能
默_笙5 小时前
🏛 给 AI 配一间办公室:Harness Engineering 六大模块与它的实现
前端·javascript
linux_cfan6 小时前
videojs v10 源代码系列解读:14 · 谓词守卫:在运行时安全地调用能力
前端·javascript·音视频
kyriewen7 小时前
我扒了 10,221 条 JD:腾讯技术岗 75% 在要 AI
前端·人工智能·ai编程
郑州光合科技余经理7 小时前
同城外卖小程序开发:下单成功后,后台导出能不能对上用户端状态
开发语言·前端·git·后端·uni-app·php·ai编程
IT_陈寒8 小时前
Vue的响应式让我熬到凌晨三点,原来漏了这个小细节
前端·人工智能·后端
Blanche15008 小时前
利用 RAG 为答疑机器人扩展知识范围
前端
天若有情6738 小时前
【纯前端小工具】公历生日转农历,批量查询每年农历生日对应的公历日期(GitHub Pages在线直接用)
前端·javascript·github pages·农历转换·lunisolar·网页小工具