WebGL (Shaders优化工具)

优化 WebGl 着色器性能(动态调整复杂度、纹理优化、视锥裁剪等)

目前為 2025-05-21 提交的版本,檢視 最新版本

// ==UserScript==
// @name         WebGL (Shaders优化工具)
// @namespace    http://tampermonkey.net/
// @version      1.0
// @description  优化 WebGl 着色器性能(动态调整复杂度、纹理优化、视锥裁剪等)
// @author       KiwiFruit
// @match        *://*/*
// @grant        none
// @license      MIT
// ==/UserScript==

(function () {
    'use strict';

    // 1. 动态调整着色器复杂度(根据设备 DPI)
    function getShaderPrecision() {
        const dpi = window.devicePixelRatio;
        return dpi >= 2 ? 'highp' : 'mediump'; // 高 DPI 使用高精度 [[3]]
    }

    // 2. 创建顶点着色器
    function createVertexShader(gl) {
        const precision = getShaderPrecision();
        const vertexShaderSource = `
            attribute vec4 a_Position;
            attribute vec3 a_Normal;
            uniform mat4 u_ModelViewMatrix;
            uniform mat4 u_ProjectionMatrix;
            varying vec3 v_Normal;

            void main() {
                v_Normal = normalize((u_ModelViewMatrix * vec4(a_Normal, 0.0)).xyz);
                gl_Position = u_ProjectionMatrix * u_ModelViewMatrix * a_Position;
            }
        `;
        return compileShader(gl, gl.VERTEX_SHADER, vertexShaderSource);
    }

    // 3. 创建片元着色器(动态精度)
    function createFragmentShader(gl) {
        const precision = getShaderPrecision();
        const fragmentShaderSource = `
            precision ${precision} float; // 动态精度 [[3]]
            varying vec3 v_Normal;
            uniform vec3 u_LightColor;
            uniform vec3 u_LightDirection;

            void main() {
                float light = max(dot(v_Normal, u_LightDirection), 0.0);
                gl_FragColor = vec4(u_LightColor * light, 1.0);
            }
        `;
        return compileShader(gl, gl.FRAGMENT_SHADER, fragmentShaderSource);
    }

    // 4. 编译着色器通用函数
    function compileShader(gl, type, source) {
        const shader = gl.createShader(type);
        gl.shaderSource(shader, source);
        gl.compileShader(shader);
        if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
            console.error('Shader compile failed:', gl.getShaderInfoLog(shader));
            gl.deleteShader(shader);
            return null;
        }
        return shader;
    }

    // 5. 预编译并缓存着色器程序
    let shaderProgramCache = null;
    async function preloadShaders(gl) {
        if (shaderProgramCache) return shaderProgramCache;

        const vs = createVertexShader(gl);
        const fs = createFragmentShader(gl);
        const program = gl.createProgram();
        gl.attachShader(program, vs);
        gl.attachShader(program, fs);
        gl.linkProgram(program);

        if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
            console.error('Program link failed:', gl.getProgramInfoLog(program));
            return null;
        }

        shaderProgramCache = program;
        return program;
    }

    // 6. 创建带 Mipmap 的纹理 [[10]]
    function createMipmapTexture(gl, image) {
        const texture = gl.createTexture();
        gl.bindTexture(gl.TEXTURE_2D, texture);
        gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image);
        gl.generateMipmap(gl.TEXTURE_2D);
        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST_MIPMAP_LINEAR);
        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
        return texture;
    }

    // 7. 视锥裁剪逻辑 [[5]]
    function isObjectVisible(object, camera) {
        const distance = Math.sqrt(
            (object.position.x - camera.position.x) ** 2 +
            (object.position.y - camera.position.y) ** 2 +
            (object.position.z - camera.position.z) ** 2
        );
        return distance < camera.far; // 根据摄像机远裁剪面剔除 [[5]]
    }

    // 8. 静态分辨率适配(根据设备 DPI)[[2]]
    function adjustResolutionByDPI(canvas) {
        const dpi = window.devicePixelRatio;
        const targetDpi = 1.0; // 固定目标 DPI
        const scale = dpi > targetDpi ? targetDpi / dpi : 1.0;
        canvas.width = Math.floor(canvas.clientWidth * scale);
        canvas.height = Math.floor(canvas.clientHeight * scale);
        canvas.getContext('webgl').viewport(0, 0, canvas.width, canvas.height);
    }

    // 9. 主函数:初始化 WebGL 并应用优化
    function initWebGL() {
        const canvas = document.createElement('canvas');
        adjustResolutionByDPI(canvas); // 静态分辨率适配 [[2]]
        document.body.appendChild(canvas);

        const gl = canvas.getContext('webgl');
        if (!gl) {
            console.error('WebGL not supported');
            return;
        }

        // 预编译着色器 [[3]]
        preloadShaders(gl).then(program => {
            if (!program) return;

            // 示例:绘制一个简单立方体
            const vertices = new Float32Array([
                // 顶点坐标
                -1, -1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1,
                -1, -1, 1, 1, -1, 1, 1, 1, 1, -1, 1, 1,
            ]);
            const normals = new Float32Array([
                // 法线向量
                0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1,
                0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1,
            ]);

            // 创建缓冲区
            const vertexBuffer = gl.createBuffer();
            gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
            gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);

            const normalBuffer = gl.createBuffer();
            gl.bindBuffer(gl.ARRAY_BUFFER, normalBuffer);
            gl.bufferData(gl.ARRAY_BUFFER, normals, gl.STATIC_DRAW);

            // 绑定属性
            gl.useProgram(program);
            const aPosition = gl.getAttribLocation(program, 'a_Position');
            const aNormal = gl.getAttribLocation(program, 'a_Normal');
            gl.enableVertexAttribArray(aPosition);
            gl.enableVertexAttribArray(aNormal);

            gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
            gl.vertexAttribPointer(aPosition, 3, gl.FLOAT, false, 0, 0);

            gl.bindBuffer(gl.ARRAY_BUFFER, normalBuffer);
            gl.vertexAttribPointer(aNormal, 3, gl.FLOAT, false, 0, 0);

            // 设置统一变量
            const uModelViewMatrix = gl.getUniformLocation(program, 'u_ModelViewMatrix');
            const uProjectionMatrix = gl.getUniformLocation(program, 'u_ProjectionMatrix');
            const uLightColor = gl.getUniformLocation(program, 'u_LightColor');
            const uLightDirection = gl.getUniformLocation(program, 'u_LightDirection');

            gl.uniform3f(uLightColor, 1.0, 1.0, 1.0);
            gl.uniform3f(uLightDirection, 0.5, 0.5, 1.0);

            // 渲染循环
            function render() {
                gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
                gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4); // 绘制立方体面
                requestAnimationFrame(render);
            }
            render();
        });
    }

    // 10. 监听页面加载完成
    window.addEventListener('load', () => {
        try {
            initWebGL(); // 初始化 WebGL 渲染 [[3]]
        } catch (error) {
            console.error('WebGL 初始化失败:', error);
        }
    });
})();