如何在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; }
额外注意事项
- 原代码中
new vec3是错误写法,Three.js中创建向量必须用new THREE.Vector3。 - animate函数必须添加
requestAnimationFrame(animate)才能形成持续的帧循环。 - 若使用方案一,不要频繁创建新的
THREE.Vector3数组,直接修改现有向量属性更高效。
内容的提问来源于stack exchange,提问作者JennyW
相关产品推荐
相关产品推荐

