p5.js WebGL着色器:如何直接传入vec2/vec3类型数组uniform?
解决p5.js WebGL中传递vec2/vec3数组作为uniform的黑屏问题
问题背景
已将Metaballs代码移植到p5.js WebGL并正常运行,但希望直接传入vec2类型的positions[]数组和vec3类型的colors[]数组作为uniform,替代原来拆分传递xs、ys、colorRs等单独float数组的方式。尝试用metaballShader.setUniform('positions', balls.map(b => [b.pos.x, b.pos.y]))传递位置数组(颜色同理)后,画面仅显示黑屏。
核心问题
p5.js传递复合类型(如vec2、vec3)的数组uniform时,需确保GLSL中uniform的声明格式正确,同时p5.js端传递的数组结构符合WebGL要求,否则着色器无法正确解析数据导致黑屏。
解决步骤
1. 修改片段着色器(metaball.frag)
将原来的多个float数组替换为vec2 positions[BALLS]和vec3 colors[BALLS],简化Metaball结构体初始化逻辑:
precision highp float; #define BALLS 3 uniform vec2 positions[BALLS]; // 替换原xs、ys数组 uniform float rs[BALLS]; uniform vec3 colors[BALLS]; // 替换原colorRs、colorGs、colorBs数组 varying highp vec2 vPos; struct Metaball { float r; vec2 pos; vec3 col; }; vec4 getDist(Metaball ball) { float dist = float(ball.r) / length(vPos - ball.pos); return vec4(ball.col * dist, dist); } void main() { float total = 0.0; vec3 color = vec3(0.0); for (int i = 0; i < BALLS; i++) { Metaball ball; ball.pos = positions[i]; ball.r = rs[i]; ball.col = colors[i]; vec4 colorValue = getDist(ball); total += colorValue.a; color += colorValue.rgb; } float threshold = total > 0.016 ? 1.0 : 0.0; if(total > 0.0) color /= total; // 避免除以0导致异常 gl_FragColor = vec4(color * threshold, 1.0); }
2. 修改p5.js代码(sketch.js)
- 在
setup函数中直接传递positions和colors数组,删除拆分的颜色数组传递逻辑 - 在
draw函数中仅更新positions数组即可,无需再维护xs、ys数组:
let metaballShader; let balls = []; function preload() { metaballShader = loadShader("metaball.vert", "metaball.frag"); } function setup() { createCanvas(680, 530, WEBGL); shader(metaballShader); balls.push(new Ball(random(width), random(height), [1, 0, 0])); balls.push(new Ball(random(width), random(height), [0, 1, 0])); balls.push(new Ball(random(width), random(height), [0, 0, 1])); metaballShader.setUniform('width', width); metaballShader.setUniform('height', height); metaballShader.setUniform('rs', balls.map(b => b.r)); // 直接传递vec2和vec3数组 metaballShader.setUniform('positions', balls.map(b => [b.pos.x, b.pos.y])); metaballShader.setUniform('colors', balls.map(b => b.color)); } function draw() { // 仅更新positions数组 metaballShader.setUniform('positions', balls.map(b => [b.pos.x, b.pos.y])); quad(-1, -1, 1, -1, 1, 1, -1, 1); for (const ball of balls) { ball.update(); } }
3. 关键注意点
- GLSL中数组uniform必须明确指定长度(通过
#define BALLS 3固定长度,确保p5.js传递的数组长度与着色器一致) - p5.js传递复合类型数组时,直接传递二维数组即可(如
[x1,y1], [x2,y2]对应vec2数组),内部会自动转换为WebGL所需格式 - 增加
if(total > 0.0)判断避免除以0,防止极端情况下的渲染异常
验证
修改完成后运行代码,metaball效果将正常显示,代码结构更简洁,无需维护多个拆分的float数组。
内容的提问来源于stack exchange,提问作者bread
相关产品推荐
相关产品推荐

