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

如何在Three.js的ShaderMaterial中更新数组类型Uniform值

Three.js数组类型Uniform更新方案

错误原因

直接赋值普通数组给material.uniforms.pos.value时,Three.js无法正确将其转换为WebGL兼容的格式,尤其是vec3[4]这类复合类型数组,必须使用Three.js支持的数据类型(如THREE.Vector3数组或Float32Array)。


方案一:基于Three.js向量对象更新(直观易维护)

初始化时使用THREE.Vector3数组作为pos的value,更新时直接修改已有向量的属性,Three.js会自动检测到变化:

修正后的初始化代码

function init() {
  // ...渲染器、场景、相机等初始化代码
  
  // 使用THREE.Vector3创建数组,原代码的new vec3是错误写法
  mypos[0] = new THREE.Vector3(1, 0, 0);
  mypos[1] = new THREE.Vector3(0, 1, 0);
  mypos[2] = new THREE.Vector3(0, 0, 1);
  mypos[3] = new THREE.Vector3(1, 1, 0);

  mysize[0] = 0.1;
  mysize[1] = 0.2;        
  mysize[2] = 0.3;
  mysize[3] = 0.4;

  material = new THREE.ShaderMaterial({
    uniforms: {
      pos: { value: mypos }, // 直接传入THREE.Vector3数组
      size: { value: mysize },
      tt : {value: 0.3}
    },
    vertexShader: document.getElementById('vertexShader').textContent,
    fragmentShader: document.getElementById('fragmentShader').textContent
  });
}

animate函数中的更新代码

function animate() {
  requestAnimationFrame(animate); // 必须添加帧循环才能持续更新
  
  // 修正原循环的语法错误:缺少i++
  for(let i=0; i<4; i++) {
    // 直接修改已有THREE.Vector3的属性,无需重新创建数组
    mypos[i].x += 0.1 * i;
    mypos[i].y += 0.2 * i;
    mypos[i].z += 0.3 * i;
    mysize[i] += 0.5 * i;
  }
  
  tt += 0.01;
  material.uniforms.tt.value = tt;
  
  // pos和size数组无需重新赋值,Three.js会自动检测向量属性变化
  // 若必须重新赋值数组,需确保每个元素都是THREE.Vector3实例:
  // material.uniforms.pos.value = [
  //   new THREE.Vector3(新x, 新y, 新z),
  //   ...其他三个Vector3
  // ];
}

方案二:使用TypedArray(性能更优,适合高频更新)

对于数组类型的Uniform,使用Float32Array可以减少对象创建开销,提升更新效率:

修正后的初始化代码

function init() {
  // ...渲染器、场景、相机等初始化代码
  
  // 用Float32Array存储vec3[4]:每个vec3占3个连续元素
  const posArray = new Float32Array([
    1,0,0, // pos[0]
    0,1,0, // pos[1]
    0,0,1, // pos[2]
    1,1,0  // pos[3]
  ]);
  // float[4]直接用长度为4的Float32Array
  const sizeArray = new Float32Array([0.1, 0.2, 0.3, 0.4]);

  material = new THREE.ShaderMaterial({
    uniforms: {
      pos: { value: posArray },
      size: { value: sizeArray },
      tt : {value: 0.3}
    },
    vertexShader: document.getElementById('vertexShader').textContent,
    fragmentShader: document.getElementById('fragmentShader').textContent
  });
  
  // 将数组保存到全局,方便animate中更新
  window.posArray = posArray;
  window.sizeArray = sizeArray;
}

animate函数中的更新代码

function animate() {
  requestAnimationFrame(animate);
  
  for(let i=0; i<4; i++) {
    // 更新posArray中对应索引的元素
    posArray[i*3] += 0.1 * i;     // 第i个vec3的x分量
    posArray[i*3 + 1] += 0.2 * i; // 第i个vec3的y分量
    posArray[i*3 + 2] += 0.3 * i; // 第i个vec3的z分量
    sizeArray[i] += 0.5 * i;
  }
  
  tt += 0.01;
  material.uniforms.tt.value = tt;
  
  // 关键:修改TypedArray后必须标记uniform需要更新
  material.uniforms.pos.needsUpdate = true;
  material.uniforms.size.needsUpdate = true;
}

额外注意事项

  1. 原代码中new vec3是错误写法,Three.js中创建向量必须用new THREE.Vector3。
  2. animate函数必须添加requestAnimationFrame(animate)才能形成持续的帧循环。
  3. 若使用方案一,不要频繁创建新的THREE.Vector3数组,直接修改现有向量属性更高效。

内容的提问来源于stack exchange,提问作者JennyW

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.31 14:25:47