一个简单利用WebGL绘制频谱瀑布图示例

2023-08-11 12:41:48 来源: 博客园


(资料图片仅供参考)

先看效果

还是比较节省性能的,这个还是包含了生成测试数据的性能,实际应用如果是直接通信获得数据应该还能少几毫秒吧!

准备工作用了React,但是关系不大WebGL的基础用法(推荐看一看掘金里的一个教程:WebGL 入门与实践)有兴趣应该读一读这个的源码GPU.JS,因为一开始偷学了一手flatten2dArrayTo这个方法,后来发现实现上述效果的功能的实现原理和GPU.JS的加速原理差不多的需要稍微写一个fragmentShader的代码;因为要把大多数耗时的计算扔到这个里面执行重要代码需要将测试数据生成的matrix二维数组通过flatten2dArrayTo转化为Uint8Array
const size = { width: 1400, height: 600 };    const sourceArr = new Float32Array(size.width * size.height).fill(-999.0);    const matrix = []; // 一个二维数组    setInterval(() => {      const res = [];      // TODO 按某些规则生成一行数据放入res      matrix.unshift(res);      if (matrix.length > size.height) {        matrix.pop();      }      flatten2dArrayTo(matrix, sourceArr);    }, 20);    // 二维数组转一维数组    const flatten2dArrayTo = (array, target) => {      let offset = 0;      for (let y = 0; y < array.length; y += 1) {        target.set(array[y], offset);        offset += array[y].length;      }    };        // ...实际给GL使用再将Float32Array转为Uint8Array    const d = sourceArr;    new Uint8Array(d.buffer);    // ...
需要给GL定义:调色板作为第0个纹理,数据转化为的Uint8Array当作第1个纹理
const drawDance = (palette, data, width, height, l) => {    if (canRef.current) {      const gl = canRef.current.getContext?.("webgl");      if (gl) {        // 这个暂时没用        const lengthHandle = gl.getUniformLocation(gl.program, "length");        gl.uniform1f(lengthHandle, l);        // 纹理0:一个256*2的调色板        const paletteLoc = gl.getUniformLocation(gl.program, "u_Palette");        gl.uniform1i(paletteLoc, 0);        // 纹理1:Float32Array转为Uint8Array的数据纹理        const samplerLoc = gl.getUniformLocation(gl.program, "u_Sampler");        gl.uniform1i(samplerLoc, 1);        // 纹理0放入        gl.activeTexture(gl.TEXTURE0);        const texture1 = gl.createTexture();        gl.bindTexture(gl.TEXTURE_2D, texture1);        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);        gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, 256 * 2, 1, 0, gl.RGB, gl.UNSIGNED_BYTE, palette);        // 纹理1放入        gl.activeTexture(gl.TEXTURE1);        const texture2 = gl.createTexture();        gl.bindTexture(gl.TEXTURE_2D, texture2);        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);        gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, bvbv(data));        // canvas那么大的一个网格        drawBuffer(gl, [-1.0, 1.0, -1.0, -1.0, 1.0, 1.0, 1.0, -1.0], "a_Position");        // 释放纹理        gl.deleteTexture(texture1);        gl.deleteTexture(texture2);      }    }  };
canvas尺寸变化的时候告诉GL {width,height} 方便在片元着色器中取到对应点的value
const onResize = useCallback(() => {    const { offsetHeight: height, offsetWidth: width } = containerRef.current || {};    const can = React.createElement("canvas", {      width,      height,      style: { background: "#90202020" },      ref: canRef,    });    containerRef.current && render(can, containerRef.current);    const gl = canRef.current.getContext("webgl");    if (gl) {      // 重新设置gl视口,gl的Oxy在canvas的中心      initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE);      gl.viewport(0, 0, width, height);      const widthHandle = gl.getUniformLocation(gl.program, "width");      const heightHandle = gl.getUniformLocation(gl.program, "height");      gl.uniform1f(widthHandle, width);      gl.uniform1f(heightHandle, height);      // 缩放相关的不重要      const x1H = gl.getUniformLocation(gl.program, "x1");      const x2H = gl.getUniformLocation(gl.program, "x2");      gl.uniform1f(x1H, 0);      gl.uniform1f(x2H, width);    }  }, []);
一个fragmentShader,主要是要把通过像素位置获取对应二维的数组中对应的数据值(重点是那个decode32函数,网上找了挺久的How do I convert a vec4 rgba value to a float? ),然后根据最小最大和调色板确定绘制的颜色
precision mediump float;  uniform float width;  uniform float height;  uniform float length;  uniform float x1;  uniform float x2;  uniform sampler2D u_Palette;  uniform sampler2D u_Sampler;  vec2 reslution =vec2(width,height);  highp float decode32(highp vec4 rgba) {    highp float Sign = 1.0 - step(128.0,rgba[0])*2.0;    highp float Exponent = 2.0 * mod(rgba[0],128.0) + step(128.0,rgba[1]) - 127.0;     highp float Mantissa = mod(rgba[1],128.0)*65536.0 + rgba[2]*256.0 +rgba[3] + float(0x800000);    highp float Result =  Sign * exp2(Exponent) * (Mantissa * exp2(-23.0 ));     return Result;  }   void main(){      // gl_FragColor=vec4(gl_FragCoord.xyx/reslution.xyx,1.0);      if(gl_FragCoord.y < 10.0){        // 简单画个调色板示意图        gl_FragColor=texture2D(u_Palette,gl_FragCoord.xy/reslution.xy);      }      else{        // 绘制数据点        float xx=x1+gl_FragCoord.x/reslution.x*(x2-x1);        vec2 pos = vec2(xx,reslution.y-gl_FragCoord.y)/vec2(reslution.x,reslution.y);        vec4 color=texture2D(u_Sampler,pos)*255.0;        float val=decode32(color.abgr);        if(val < -99.0){          gl_FragColor=vec4(0.0,0.0,0.0,1.0);        }        else{          gl_FragColor=texture2D(u_Palette,vec2((val+20.0)/100.0,1.0));        }      }    }
源码

基本上述代码过程就能实现了;直接看源码吧:ctrlcv->AudioDance8 ctrlCV工程师

关键词:
编辑:Edt_75

最近更新

一个简单利用WebGL绘制频谱瀑布图示例
四敢争先·半年度经济观察丨政策“加码” 服务“加持” 惠企政策“直达快享” 经济发展“添薪加火”
猫王的女儿(关于猫王的女儿的基本详情介绍)
透明的红萝卜作者(透明的红萝卜)
意外险非机动车能赔吗?怎么理赔?
1967 年本田 Prelude 运动轿跑车售价 79,000 美元
“订饭不是订你爱吃的”,女生安排公务宴请让领导石化:谁懂啊?
为什么越来越多富裕家庭,更倾向于留学美国?
OSSD保录是骗局?前一百名校录取率92.8%是怎么做到的?
天乙银饰今日银价多少一克(2023年08月11日)
广东省2023年10月自考成绩公布与复核申请事宜
广东省2023年10月自学考试准考证打印时间
广东省2023年10月自学考试在线报名操作指引
广东省2023年10月自学考试报名报考流程图
航海西路街道观澜社区召开微网格长座谈会
温州大学:2024年硕士研究生初试科目调整的温馨提示(一)
广西民族师范学院有硕士点吗
安阳工学院有硕士点吗
广西科技师范学院有硕士点吗
2023贺州学院艺术类学费多少钱一年-各专业收费标准
梧州学院有硕士点吗
“白衣天使”计梦瑶,谢谢你来过
8月10日进口棉报价小幅上涨
河北省因灾死亡29人 尚有16人失联
华为智选车首款纯电轿跑实车亮相:首搭鸿蒙 HarmonyOS 4 车机系统,本季度发布
美国发布2023年《国家情报战略》报告,再次渲染“中俄威胁”
59元!飞傲推出USB-C转0.78mm 2pin耳机线
李奕臻综艺现场成“神助攻” 演员间的“淋雨撑伞”是真的
K1178次列车因暴雨滞留72小时,乘务员、乘客、救援人员团结一心——守望相助 共护平安
青平:多措并举鼓励高校毕业生到基层建功立业