T06▸3D006▸three
Shader Gradient
Shader 動態漸層
難度 ★★★★★標籤 shader · glsl · gradient3 min read
效果簡述
第一個 fragment shader:在 plane 上跑自訂 GLSL,產生動態漸層。 比 CSS 漸層流暢、比 video 省頻寬。是入門 shader 的甜蜜起點。
Claude Code Prompt
參考:3D006 shader gradient 技術:shaderMaterial + uniforms uTime 具體行為: - vertex 只算 uv pass 給 fragment - fragment 用 sin/cos(uv + uTime) 混 3 色 - useFrame 更新 uniforms.uTime.value 限制: - GLSL 是 ES1 語法,注意 vec3、mix 的型別 - uniforms 用 useMemo 包,不然每次 render new object - 移動裝置 fragment 不要太重(avoid pow + log)
完整程式碼
'use client'
import { Canvas, useFrame } from '@react-three/fiber'
import { useMemo, useRef } from 'react'
import * as THREE from 'three'
const vert = `varying vec2 vUv; void main(){ vUv = uv; gl_Position = projectionMatrix * modelViewMatrix * vec4(position,1.0); }`
const frag = `uniform float uTime; varying vec2 vUv;
void main(){
float t = uTime * 0.2;
vec3 a = vec3(0.76,0.25,0.05);
vec3 b = vec3(0.98,0.57,0.24);
vec3 c = vec3(0.05,0.05,0.08);
float mA = sin(vUv.x * 3.0 + t) * 0.5 + 0.5;
float mB = cos(vUv.y * 3.0 + t * 1.3) * 0.5 + 0.5;
vec3 col = mix(c, a, mA);
col = mix(col, b, mB * 0.6);
gl_FragColor = vec4(col, 1.0);
}`
function GradientPlane() {
const ref = useRef<THREE.ShaderMaterial>(null)
const uniforms = useMemo(() => ({ uTime: { value: 0 } }), [])
useFrame((s) => { if (ref.current) ref.current.uniforms.uTime.value = s.clock.getElapsedTime() })
return (
<mesh>
<planeGeometry args={[6,4]} />
<shaderMaterial ref={ref} vertexShader={vert} fragmentShader={frag} uniforms={uniforms} />
</mesh>
)
}
export function ShaderGradient() {
return (
<Canvas camera={{ position: [0,0,3], fov: 55 }} dpr={[1,2]}>
<GradientPlane />
</Canvas>
)
}何時用
- ✅ Hero 背景(取代 CSS 漸層)
- ✅ 品牌色運用展示
- ✅ Loading 過場
何時別用
- ❌ 內容區(背景動會分心)
- ❌ 沒人想學 GLSL 但又趕時程
- ❌ 簡單的兩色漸層(CSS 就夠)
我踩過的坑
-
uniforms 沒 useMemo 每幀重建:值會被 reset 到 0。
-
vec3 加減 float 要小心:GLSL 嚴格型別,vec3 * float 可以, vec3 + float 不行。
-
gl_FragColor 是 ES1:新版有人寫
out vec4 fragColor(ES3), 別跟 ES1 混。
相關效果
- T07 Noise Background — 進階 shader
- F19 Animated Gradient — CSS 版