You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.23 05:10:38