先记住 2 个核心根基:
- 所有流动 / 摆动 / 起伏 90% 只用:
sin(正弦)、cos(余弦) - 所有位置偏移 用
+ - * / - 时间统一变量:
uTime(全局动画时间)
一、先记万能基础公式
1. 正弦万能波动(最常用!90% 动画靠它)
js
// 基础格式
sin( 自变量 )
// 让结果范围变成 0 ~ 1(颜色/透明度/强度必用)
sin(x) * 0.5 + 0.5
值域
sin(x)→ 范围-1 ~ 1sin(x)*0.5+0.5→ 范围0 ~ 1
2. 控制 4 个参数,随便改动作
js
sin( 频率 * 坐标 + 速度 * 时间 )
- 频率:数字越大,波动越密、越快
- 时间:
uTime,控制整体动起来 - 速度:控制快慢
| 修改内容 | 写法 | 效果 |
|---|---|---|
| 波纹更密集 | 坐标 * 大数 | 纹路变多、紧凑 |
| 波纹更稀疏 | 坐标 * 小数 | 纹路变少、宽松 |
| 动画变快 | uTime * 2.0 | 运动加速 |
| 动画变慢 | uTime * 0.3 | 运动舒缓 |
| 正向运动 | + uTime | 正向流动 |
| 反向运动 | - uTime | 反向流动 |
二、按「想要什么效果」直接对应公式
1. 左右横向流动(最常用:流光、飘带、流云)
需求:画面从左往右缓缓流动
js
// uv.x 横向坐标
float flow = sin(uv.x * 5.0 + uTime);
uv.x * 5:波纹疏密+uTime:向右流动- 改成
-uTime:向左流动
2. 上下竖向流动
js
float flow = sin(uv.y * 5.0 + uTime);
3. 整体明暗闪烁 / 呼吸灯
需求:颜色一亮一暗慢慢呼吸
js
// 只跟时间有关,跟坐标无关
float breath = sin(uTime) * 0.5 + 0.5;
col *= breath;
4. 波浪起伏(水面、飘动旗帜)
需求:一排高低起伏波浪
js
// 水平波浪
float wave = sin(uv.x * 8.0 + uTime * 2.0);
// 用这个值去偏移位置/偏移颜色
5. 环形扩散波纹(圆心往外扩散水纹)
需求:中心点一圈一圈散开
js
float len = length(uv - 0.5); // 到中心距离
float ring = sin(len * 15.0 - uTime * 3.0);
-uTime= 向外扩散+uTime= 向内收缩
6. 旋转动画(整体旋转、漩涡)
需求:图案转圈
js
float angle = atan(uv.y, uv.x); // 获取角度
float rotate = sin(angle * 6.0 + uTime);
7. 随机闪烁星星(无规律闪动)
需求:星星各自乱闪
js
float r = rand(uv); // 随机值
float twinkle = sin(uTime + r * 10.0) * 0.5 + 0.5;
8. 快慢变速控制
- 动得慢:
uTime * 0.3 - 正常速度:
uTime * 1.0 - 动得快:
uTime * 2.5
9. 条纹移动(科技网格线)
js
float line = fract(uv.x * 10.0 + uTime);
float grid = smoothstep(0.0, 0.1, line);
10. 画面轻微扰动(星云/云雾柔化)
js
uv.x += sin(uv.y * 3.0 + uTime * 0.2) * 0.1;
uv.y += cos(uv.x * 3.0 + uTime * 0.15) * 0.15;
11. 屏幕边缘渐变淡化(所有背景通用美化)
js
float fade = 1.0 - length(uv - 0.5);
color *= fade;
三、最简搭配口诀(背会直接写)
- 想左右动 → 用 uv.x + uTime
- 想上下动 → 用 uv.y + uTime
- 想转圈扩散 → 用 length /atan + uTime
- 想呼吸明暗 → 只用纯 uTime
- 想疏密变化 → 乘大数
- 想快慢变化 → 乘时间系数
- *想 0~1 可用颜色 → 统一 0.5+0.5
四、三角函数通用进阶用法(快速增加画面复杂度)
1. GLSL 仅需掌握 3 个核心函数
js
sin(x) // 主力:波动、流动、起伏、闪烁
cos(x) // 辅助:和sin相位错位,搭配做出错位动感
atan(y,x) // 专用:计算角度,实现旋转、漩涡、环形动画
tan 极少使用,日常开发可直接忽略
2. 双层叠加公式(告别机械单调动画)
核心逻辑:不同方向、不同速度、正负错位叠加,模拟自然运动
js
// 横向波动
float w1 = sin(uv.x * 6.0 + uTime * 1.0);
// 纵向反向波动
float w2 = sin(uv.y * 4.0 - uTime * 1.2);
// 加权混合
float allWave = w1 * 0.6 + w2 * 0.4;
3. 多方向叠加万能模板(一键提升复杂度)
横向 + 纵向 + 斜向三维叠加,轻松做出高级流动效果
js
// 横向
float a = sin(uv.x * 5.0 + uTime);
// 纵向
float b = sin(uv.y * 6.0 - uTime);
// 斜向
float c = sin((uv.x + uv.y) * 4.0 + uTime);
// 均匀混合
float finalEffect = (a + b + c) / 3.0;
5. 四类高阶效果极简公式
- 自然海浪
js
float w1 = sin(uv.x*6. + uTime);
float w2 = sin(uv.y*4. - uTime*1.2);
float wave = w1*.6 + w2*.4;
- 扩散水纹
js
float d = length(uv-.5);
float ring = sin(d*12. - uTime*3.);
- 旋转螺旋
js
float a = atan(uv.y,uv.x);
float r = sin(a*8. - uTime*2.);
- 柔性流云
js
uv += vec2(sin(uv.y*2.5+uTime*0.2),cos(uv.x*2.5+uTime*0.15))*0.12;
五、速记口诀
- 想波动,用 sin
- 想自然,叠两层
- 想扩散,用 length
- 想旋转,用 atan想
- 复杂,叠方向
- 调疏密改系数,调快慢改时间
六、一些例子
1. 星云流动渐变背景
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>星云流动背景</title>
<style>body{margin:0;overflow:hidden;}</style>
</head>
<body>
<script type="module">
import * as THREE from 'https://cdn.jsdelivr.net/npm/three@0.160/build/three.module.js'
const scene = new THREE.Scene()
const camera = new THREE.OrthographicCamera(-1,1,1,-1,0,10)
camera.position.z = 1
const renderer = new THREE.WebGLRenderer({antialias:true})
renderer.setSize(innerWidth,innerHeight)
renderer.setPixelRatio(Math.min(devicePixelRatio,1.5))
document.body.appendChild(renderer.domElement)
const vertexShader = `
varying vec2 vUv;
void main(){
vUv = uv;
gl_Position = vec4(position,1.0);
}
`
const fragmentShader = `
uniform float uTime;
varying vec2 vUv;
void main(){
vec2 uv = vUv;
uv.x += sin(uv.y*3.0 + uTime*0.2)*0.1;
uv.y += cos(uv.x*3.0 + uTime*0.15)*0.1;
vec3 col1 = vec3(0.1,0.2,0.6);
vec3 col2 = vec3(0.2,0.5,0.9);
vec3 col3 = vec3(0.0,0.1,0.3);
float mix1 = sin(uv.x*4.0 + uTime*0.3)*0.5+0.5;
float mix2 = cos(uv.y*4.0 - uTime*0.2)*0.5+0.5;
vec3 finalCol = mix(col1,col2,mix1);
finalCol = mix(finalCol,col3,mix2*0.5);
gl_FragColor = vec4(finalCol,1.0);
}
`
const shaderMat = new THREE.ShaderMaterial({
vertexShader,
fragmentShader,
uniforms:{
uTime:{value:0}
}
})
const plane = new THREE.Mesh(new THREE.PlaneGeometry(2,2), shaderMat)
scene.add(plane)
window.addEventListener('resize',()=>{
renderer.setSize(innerWidth,innerHeight)
})
const clock = new THREE.Clock()
function animate(){
requestAnimationFrame(animate)
shaderMat.uniforms.uTime.value = clock.getElapsedTime()
renderer.render(scene,camera)
}
animate()
</script>
</body>
</html>
2. 中心动态波纹水纹
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>中心波纹水纹</title>
<style>body{margin:0;overflow:hidden;}</style>
</head>
<body>
<script type="module">
import * as THREE from 'https://cdn.jsdelivr.net/npm/three@0.160/build/three.module.js'
const scene = new THREE.Scene()
const camera = new THREE.OrthographicCamera(-1,1,1,-1,0,10)
camera.position.z = 1
const renderer = new THREE.WebGLRenderer({antialias:true})
renderer.setSize(innerWidth,innerHeight)
renderer.setPixelRatio(Math.min(devicePixelRatio,1.5))
document.body.appendChild(renderer.domElement)
const vertexShader = `
varying vec2 vUv;
void main(){
vUv = uv;
gl_Position = vec4(position,1.0);
}
`
const fragmentShader = `
uniform float uTime;
varying vec2 vUv;
void main(){
vec2 uv = vUv - 0.5;
float len = length(uv);
float wave = sin(len*12.0 - uTime*2.5) * 0.15;
float wave2 = sin(len*8.0 + uTime*1.8) * 0.1;
vec3 base = vec3(0.15,0.45,0.75);
vec3 color = base + vec3(wave+wave2);
color *= 1.0 - len*0.8;
gl_FragColor = vec4(color,1.0);
}
`
const shaderMat = new THREE.ShaderMaterial({
vertexShader,
fragmentShader,
uniforms:{
uTime:{value:0}
}
})
const plane = new THREE.Mesh(new THREE.PlaneGeometry(2,2), shaderMat)
scene.add(plane)
window.addEventListener('resize',()=>{
renderer.setSize(innerWidth,innerHeight)
})
const clock = new THREE.Clock()
function animate(){
requestAnimationFrame(animate)
shaderMat.uniforms.uTime.value = clock.getElapsedTime()
renderer.render(scene,camera)
}
animate()
</script>
</body>
</html>
3. 科技流动网格线条
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>科技流动网格</title>
<style>body{margin:0;overflow:hidden;}</style>
</head>
<body>
<script type="module">
import * as THREE from 'https://cdn.jsdelivr.net/npm/three@0.160/build/three.module.js'
const scene = new THREE.Scene()
const camera = new THREE.OrthographicCamera(-1,1,1,-1,0,10)
camera.position.z = 1
const renderer = new THREE.WebGLRenderer({antialias:true})
renderer.setSize(innerWidth,innerHeight)
renderer.setPixelRatio(Math.min(devicePixelRatio,1.5))
document.body.appendChild(renderer.domElement)
const vertexShader = `
varying vec2 vUv;
void main(){
vUv = uv;
gl_Position = vec4(position,1.0);
}
`
const fragmentShader = `
uniform float uTime;
varying vec2 vUv;
void main(){
vec2 uv = vUv * 10.0;
float lineX = smoothstep(0.0,0.08,fract(uv.x + uTime*0.5));
float lineY = smoothstep(0.0,0.08,fract(uv.y - uTime*0.3));
float grid = max(lineX,lineY);
vec3 lineCol = vec3(0.2,0.7,1.0);
vec3 bg = vec3(0.02,0.05,0.12);
vec3 res = mix(bg,lineCol,grid*0.6);
gl_FragColor = vec4(res,1.0);
}
`
const shaderMat = new THREE.ShaderMaterial({
vertexShader,
fragmentShader,
uniforms:{
uTime:{value:0}
}
})
const plane = new THREE.Mesh(new THREE.PlaneGeometry(2,2), shaderMat)
scene.add(plane)
window.addEventListener('resize',()=>{
renderer.setSize(innerWidth,innerHeight)
})
const clock = new THREE.Clock()
function animate(){
requestAnimationFrame(animate)
shaderMat.uniforms.uTime.value = clock.getElapsedTime()
renderer.render(scene,camera)
}
animate()
</script>
</body>
</html>
4. 极简闪烁星空背景
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>闪烁星空</title>
<style>body{margin:0;overflow:hidden;}</style>
</head>
<body>
<script type="module">
import * as THREE from 'https://cdn.jsdelivr.net/npm/three@0.160/build/three.module.js'
const scene = new THREE.Scene()
const camera = new THREE.OrthographicCamera(-1,1,1,-1,0,10)
camera.position.z = 1
const renderer = new THREE.WebGLRenderer({antialias:true})
renderer.setSize(innerWidth,innerHeight)
renderer.setPixelRatio(Math.min(devicePixelRatio,1.5))
document.body.appendChild(renderer.domElement)
const vertexShader = `
varying vec2 vUv;
void main(){
vUv = uv;
gl_Position = vec4(position,1.0);
}
`
const fragmentShader = `
uniform float uTime;
varying vec2 vUv;
float rand(vec2 p){
return fract(sin(dot(p,vec2(12.9898,78.233)))*43758.5453);
}
void main(){
vec2 uv = vUv * 25.0;
vec2 id = floor(uv);
float r = rand(id);
float twinkle = sin(uTime*2.0 + r*10.0)*0.4+0.6;
float star = smoothstep(0.85,1.0,r) * twinkle;
vec3 bg = vec3(0.01,0.02,0.06);
vec3 starCol = vec3(1.0,1.0,1.0);
vec3 col = bg + starCol*star;
gl_FragColor = vec4(col,1.0);
}
`
const shaderMat = new THREE.ShaderMaterial({
vertexShader,
fragmentShader,
uniforms:{
uTime:{value:0}
}
})
const plane = new THREE.Mesh(new THREE.PlaneGeometry(2,2), shaderMat)
scene.add(plane)
window.addEventListener('resize',()=>{
renderer.setSize(innerWidth,innerHeight)
})
const clock = new THREE.Clock()
function animate(){
requestAnimationFrame(animate)
shaderMat.uniforms.uTime.value = clock.getElapsedTime()
renderer.render(scene,camera)
}
animate()
</script>
</body>
</html>