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

Three.js DataTexture高分辨率适配问题求助(已修复空纹理)

问题说明

我通过数组创建了BufferGeometry,为获取相邻顶点坐标,尝试将顶点坐标同时以attribute和DataTexture传入着色器。最初创建的DataTexture为空,改用RGBA格式后解决了该问题,但当设置分辨率为64时几何体消失,现寻求可稳定支持任意分辨率(如500)的优雅解决方案。

解决方案

问题根源分析

  1. 纹理尺寸兼容性限制:WebGL对纹理尺寸存在隐性限制,多数设备要求纹理宽高为2的幂次(非2幂次纹理兼容性差)。当分辨率设为64时,顶点总数为(64+1)²=4225,该数值不是2的幂次,导致纹理创建失败。
  2. 数据格式对齐问题:RGB格式每个像素占12字节(3个float),GPU纹理内存要求按4字节对齐,RGBA格式(16字节/像素)能自然满足对齐要求,这也是最初RGB格式纹理为空的核心原因。
  3. 采样坐标计算错误:原代码将纹理宽度设为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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.20 22:17:01