通过纹理向片段着色器传递浮点数组(音频波形)的精度问题
搞定1px纹理采样瑕疵的几个关键点
嘿,我之前也踩过用1像素高的纹理传音频波形的坑!你遇到的随机瑕疵问题,大概率是纹理采样的过滤逻辑、坐标精度或者数据格式这几个环节出了问题,咱们一步步来解决:
1. 先把纹理过滤模式改成「精准采样」
默认的LINEAR过滤模式会让GPU尝试插值相邻像素,但你的纹理只有1行,GPU可能会误采样到不存在的上下像素(或者触发mipmap逻辑),导致读出奇怪的值。
直接把过滤模式改成NEAREST,同时禁用mipmap,让GPU精准命中单个像素:
// 创建纹理时设置这些参数 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST); 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); // 彻底禁用mipmap,避免不必要的采样计算 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_BASE_LEVEL, 0); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAX_LEVEL, 0);
2. 修正纹理坐标的精度偏差
你用floor计算barIndex再转纹理坐标的方式,在mediump精度下容易出现浮点误差——比如本该取第5个波形点,结果因为精度偏移取到了第4或第6个,就会出现瑕疵。
解决办法是直接采样纹理像素的中心,而不是边缘:
precision mediump float; uniform sampler2D uDisp; uniform float barsCount; uniform float availableWidth; uniform float paddingH; uniform vec2 textureSize; // 把纹理的宽高传进来,比如vec2(barsCount, 1.0) void main(){ vec2 coord = gl_FragCoord.xy; float columnWidth = availableWidth / barsCount; float barIndex = floor((coord.x - paddingH) / columnWidth); // 关键修正:用(索引+0.5)/纹理宽度,精准命中像素中心 float textureX = (barIndex + 0.5) / textureSize.x; // 纹理高度是1,y坐标固定取0.5就行 float sampleValue = texture2D(uDisp, vec2(textureX, 0.5)).r; // 你的绘制逻辑... }
3. 检查纹理数据格式的精度
如果你的音频波形是浮点数据,别用8位的纹理格式(比如gl.RGB),不然会把0-1的浮点值压缩到0-255再还原,损失精度还可能引入异常值。
直接用浮点纹理格式:
// 假设audioWaveform是你的浮点波形数组,范围0-1 const data = new Float32Array(audioWaveform); // 用R32F格式存储单通道浮点数据 gl.texImage2D(gl.TEXTURE_2D, 0, gl.R32F, barsCount, 1, 0, gl.RED, gl.FLOAT, data);
额外小提示:处理非2的幂纹理
如果你的barsCount不是2的幂,有些老GPU可能会有纹理对齐问题。如果还是有瑕疵,可以把纹理宽度向上取最近的2的幂,然后在着色器里计算时忽略多余的部分,或者补0填充波形数据。
内容的提问来源于stack exchange,提问作者Nuthinking
相关产品推荐
相关产品推荐

