自定义shader代码在文末
调用方式:
new AudioMaterial({
row: 10,
column: 5,
start: new Color("#00CC99"),
end: new Color("#d3039c"),
brightness: "(p.y < fft && p.y > fft -0.1)"
})
就是这样
这样
默认不传递frequency
和brightness
就是这样
使用xSymmetry
x轴对称就是这样
使用repeat
就是这样图案重复几遍
还有更多参数可调节可做出更多有趣的图案
封装自定义shader
/*
* @Author: hongbin
* @Date: 2023-08-28 18:38:15
* @LastEditors: hongbin
* @LastEditTime: 2023-09-11 18:55:12
* @Description:
*/
import * as THREE from "three";
const defaultParams = {
row: 10,
column: 10,
start: new THREE.Color("#fff"),
end: new THREE.Color("#BFFF00"),
hideBg: false,
minOpacity: 0.1,
maxOpacity: 1.2,
/** 如何取音频值
* @default p.x
*/
frequency: "p.x",
/** 亮度计算 */
brightness: "(p.y < fft)",
xSymmetry: false,
ySymmetry: false,
repeat: 0,
};
export const autoUpdateUniform: PropertyDecorator = (t: any, k) => {
let r: THREE.Texture;
Object.defineProperty(t, k, {
get: () => r,
set: (v) => {
t.audioTextureUniforms.forEach(
(uniform: Record<string, THREE.IUniform>) => {
uniform.iChannel0.value = v;
}
);
r = v;
},
});
};
export class AudioMaterial extends THREE.ShaderMaterial {
@autoUpdateUniform
static audioTexture: THREE.DataTexture;
static audioTextureUniforms: Array<Record<string, THREE.IUniform>> = [];
constructor(params: Partial<typeof defaultParams>) {
// 防止使用默认颜色的material改变uniform 导致默认颜色被更改
const { start, end, ...filterParams } = params;
const realParams = {
...defaultParams,
...filterParams,
start: start || defaultParams.start.clone(),
end: end || defaultParams.end.clone(),
};
const uniforms = {
iChannel0: { value: AudioMaterial.audioTexture },
row: { value: realParams.row },
column: { value: realParams.column },
start: { value: realParams.start },
end: { value: realParams.end },
hideBg: { value: Number(realParams.hideBg) },
minOpacity: { value: realParams.minOpacity },
maxOpacity: { value: realParams.maxOpacity },
};
AudioMaterial.audioTextureUniforms.push(uniforms);
super({
uniforms,
vertexShader: `
varying vec2 vUv;
void main() {
vUv = uv;
vec4 modelViewPosition = modelViewMatrix * vec4(position, 1.0);
gl_Position = projectionMatrix * modelViewPosition;
}`,
fragmentShader: `
varying vec2 vUv;
uniform sampler2D iChannel0;
uniform float row;
uniform float column;
uniform vec3 start;
uniform vec3 end;
uniform float hideBg;
uniform float minOpacity;
uniform float maxOpacity;
void main()
{
vec2 uv = vUv;
if(uv.x > 1.) discard;
${realParams.xSymmetry ? "uv.x = abs((0.5 - vUv.x) * 2.);" : ""}
${
realParams.ySymmetry
? "uv.y = 1. - abs((0.5 - vUv.y) * 2.);"
: ""
}
${
realParams.repeat
? `uv.x =fract(vUv.x * ${realParams.repeat}.0);`
: ""
};
float bands = row;
float segs = column;
vec2 p = vec2(floor(uv.x*bands)/bands,floor(uv.y*segs)/segs);
float fft = texture( iChannel0, vec2(${
realParams.frequency
},0.0) ).x;
vec3 color = mix(start, end, sqrt(uv.y));
float mask = ${realParams.brightness} ? maxOpacity : minOpacity;
vec2 d = fract((uv - p) * vec2(bands, segs)) - 0.5;
float led = smoothstep(0.5, 0.3, abs(d.x)) *
smoothstep(0.5, 0.3, abs(d.y));
vec3 ledColor = led * color * mask;
gl_FragColor = vec4(ledColor, mask);
}
`,
transparent: realParams.hideBg,
});
}
}
audioTexture
音频纹理 请见three.js shadertoy使用手册 - 使用音频Channel和图片Channel/将音频传入glsl shader
float led = smoothstep(0.5, 0.3, abs(d.x)) *
smoothstep(0.5, 0.3, abs(d.y));
控制方块边缘模糊程度smoothstep(0.5, 0.5, abs(d.x))
就是锐利 smoothstep(0.5, 0., abs(d.x))
就是圆形