Three.js DataTexture高分辨率适配问题求助(已修复空纹理)
问题说明
我通过数组创建了BufferGeometry,为获取相邻顶点坐标,尝试将顶点坐标同时以attribute和DataTexture传入着色器。最初创建的DataTexture为空,改用RGBA格式后解决了该问题,但当设置分辨率为64时几何体消失,现寻求可稳定支持任意分辨率(如500)的优雅解决方案。
解决方案
问题根源分析
- 纹理尺寸兼容性限制:WebGL对纹理尺寸存在隐性限制,多数设备要求纹理宽高为2的幂次(非2幂次纹理兼容性差)。当分辨率设为64时,顶点总数为
(64+1)²=4225,该数值不是2的幂次,导致纹理创建失败。 - 数据格式对齐问题:RGB格式每个像素占12字节(3个float),GPU纹理内存要求按4字节对齐,RGBA格式(16字节/像素)能自然满足对齐要求,这也是最初RGB格式纹理为空的核心原因。
- 采样坐标计算错误:原代码将纹理宽度设为
vIndicesArray.length + 1,冗余的像素会导致采样坐标偏移,引发数据读取错误。
具体修复步骤
1. 统一纹理尺寸为2的幂次
创建工具函数计算大于等于顶点数的最小2的幂次,确保纹理尺寸兼容所有设备:
function getNextPowerOfTwo(n) { return n <= 1 ? 1 : Math.pow(2, Math.ceil(Math.log2(n))); }
2. 转换顶点数据为RGBA格式
将顶点坐标填充到RGBA数组中,用A通道占位以满足内存对齐要求:
const vertexCount = vIndicesArray.length; const textureWidth = getNextPowerOfTwo(vertexCount); const rgbaData = new Float32Array(textureWidth * 4); // 宽度*高度(1)*4通道 for (let i = 0; i < vertexCount; i++) { rgbaData[i * 4] = positionsArray[i * 3]; // R存储X坐标 rgbaData[i * 4 + 1] = positionsArray[i * 3 + 1]; // G存储Y坐标 rgbaData[i * 4 + 2] = positionsArray[i * 3 + 2]; // B存储Z坐标 rgbaData[i * 4 + 3] = 0.0; // A通道占位 }
3. 正确初始化DataTexture
关闭mipmap并使用邻近过滤,确保数据纹理的精准采样:
this.tex = new THREE.DataTexture( rgbaData, textureWidth, 1, THREE.RGBAFormat, THREE.FloatType ); this.tex.magFilter = THREE.NearestFilter; this.tex.minFilter = THREE.NearestFilter; this.tex.generateMipmaps = false; this.tex.needsUpdate = true;
4. 修正着色器采样逻辑
传入实际顶点数而非纹理宽度,避免冗余像素干扰采样坐标计算:
// 顶点着色器新增uniform uniform float uVertexCount; void main() { float wStep = 1. / uVertexCount; float hWStep = wStep * 0.5; float t = float(vindex) * wStep + hWStep; vec3 coordFromTex = texture(uSpatialTexture, vec2(t, 0.5)).rgb; // ... 原有逻辑 }
在JS中传入该uniform:
this.mesh.material.uniforms.uVertexCount = { value: vertexCount };
5. 验证顶点索引连续性
确保vindex属性从0开始连续编号,与纹理中的数据位置一一对应,避免采样错位。
核心修改代码示例
JS端DataTexture创建部分
// 计算顶点数与兼容纹理宽度 const vertexCount = vIndicesArray.length; const textureWidth = getNextPowerOfTwo(vertexCount); // 转换为RGBA格式数据 const rgbaData = new Float32Array(textureWidth * 4); for (let i = 0; i < vertexCount; i++) { rgbaData[i * 4] = positionsArray[i * 3]; rgbaData[i * 4 + 1] = positionsArray[i * 3 + 1]; rgbaData[i * 4 + 2] = positionsArray[i * 3 + 2]; rgbaData[i * 4 + 3] = 0.0; } // 初始化数据纹理 this.tex = new THREE.DataTexture( rgbaData, textureWidth, 1, THREE.RGBAFormat, THREE.FloatType ); this.tex.magFilter = THREE.NearestFilter; this.tex.minFilter = THREE.NearestFilter; this.tex.generateMipmaps = false; this.tex.needsUpdate = true; // 更新uniform参数 this.mesh.material.uniforms.uSpatialTexture.value = this.tex; this.mesh.material.uniforms.uVertexCount = { value: vertexCount };
顶点着色器采样修正部分
precision highp float; precision highp int; precision highp sampler2D; // ... 原有uniform uniform float uVertexCount; // 新增uniform // ... 原有attribute与输出 void main() { float wStep = 1. / uVertexCount; float hWStep = wStep * 0.5; float t = float(vindex) * wStep + hWStep; vec3 coordFromTex = texture(uSpatialTexture, vec2(t, 0.5)).rgb; // ... 原有逻辑 }
内容的提问来源于stack exchange,提问作者Spiri
相关产品推荐
相关产品推荐

