Three.js-矩形块shader

自定义shader代码在文末

Three.js-矩形块shader_第1张图片

Three.js-矩形块shader_第2张图片
调用方式:

new AudioMaterial({ 
	row: 10,
 	column: 5,
 	start: new Color("#00CC99"),
    end: new Color("#d3039c"),
 	brightness: "(p.y < fft && p.y > fft -0.1)"
})

就是这样
Three.js-矩形块shader_第3张图片
这样
Three.js-矩形块shader_第4张图片
默认不传递frequencybrightness
就是这样
Three.js-矩形块shader_第5张图片
使用xSymmetryx轴对称就是这样
Three.js-矩形块shader_第6张图片
使用repeat就是这样图案重复几遍
Three.js-矩形块shader_第7张图片
还有更多参数可调节可做出更多有趣的图案

封装自定义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))就是圆形

你可能感兴趣的:(Three.js,webGL,javascript,前端,three.js,webgl,shader)