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

WebGL2中除R8外的纹理数据格式无法正常工作求助

WebGL2 3D纹理非R8格式采样全0问题排查与解决

问题现象

在Windows10 64位Chrome v104+环境下,使用TypeScript编写WebGL2代码处理单通道3D纹理时,仅R8格式能正常工作,其余格式均出现采样值全0的情况:

  • 切换R8UI格式(使用usampler3D)时,采样返回全0;
  • R16UI、R32F等格式(对应传入Uint8Array/Float32Array)同样采样全0;
  • 无着色器编译错误、WebGL运行时控制台报错或错误返回值。

核心问题排查与修复方案

1. 整数纹理必须禁用线性过滤

WebGL2中,整数格式纹理(如R8UI、R16UI)不支持线性过滤,当前代码仅设置了TEXTURE_MIN_FILTER为NEAREST,但未显式设置TEXTURE_MAG_FILTER,部分浏览器会默认使用线性过滤,导致整数纹理采样失效。

修改setDataTexture中的纹理参数设置:

// 补充设置MAG_FILTER为NEAREST,整数纹理不支持LINEAR
gl.texParameteri(gl.TEXTURE_3D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_3D, gl.TEXTURE_MAG_FILTER, gl.NEAREST); // 新增这一行

2. 确保数据类型与纹理格式严格匹配

  • R16UI格式需要传入Uint16Array类型数据,而非Uint8Array;
  • R32F格式需要传入Float32Array类型数据;
  • 错误的数据类型会导致纹理数据上传失败,最终采样全0。

示例修正(R16UI):

// R16UI 正确数据类型
const data = new Uint16Array(W*H*N); // 改用Uint16Array
internal_format = sys.gl.R16UI;
setDataTexture(W,H,N, data, internal_format);

3. 验证纹理uniform的绑定与采样器类型匹配

确保着色器中的采样器类型与纹理格式严格对应:

  • 浮点格式(R8、R32F)使用sampler3D;
  • 无符号整数格式(R8UI、R16UI)使用usampler3D;
  • 有符号整数格式(R16I)使用isampler3D。

同时,检查uniform的纹理单元绑定是否正确:

// 确保uniform变量绑定到正确的纹理单元(以R8UI为例)
const volumeLoc = gl.getUniformLocation(program, "volume");
gl.uniform1i(volumeLoc, 0); // 对应TEXTURE0

4. 确认采样坐标在有效范围内

着色器中的采样坐标pos必须处于[0.0, 1.0]区间内,超出范围的坐标会触发CLAMP_TO_EDGE(但整数纹理边界值可能为0)。可以在着色器中添加调试输出验证坐标:

// 片段着色器中添加调试,输出坐标是否正常
if (pos.x < 0.0 || pos.x > 1.0 || pos.y < 0.0 || pos.y > 1.0 || pos.z < 0.0 || pos.z > 1.0) {
    gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); // 坐标越界输出红色
    return;
}

5. 补充WebGL错误检查(辅助排查)

虽然当前无控制台报错,但添加错误检查可以定位潜在问题:

// 在texSubImage3D后添加错误检查
gl.texSubImage3D(gl.TEXTURE_3D, 0, 0,0,0, X,Y,Z, fmt, typ, data);
const err = gl.getError();
if (err !== gl.NO_ERROR) {
    console.error(`WebGL error after texSubImage3D: ${err}`);
}

完整修正后的代码片段

setDataTexture 修正版

setDataTexture(X: number, Y: number, Z: number, data: any, internal_format: GLenum) {
    const gl = this.gl;
    const params: Record<GLenum, any> = {};
    params[gl.R8]    = ["R8",    gl.RED,         gl.UNSIGNED_BYTE];
    params[gl.R8UI]  = ["R8UI",  gl.RED_INTEGER, gl.UNSIGNED_BYTE];
    params[gl.R16UI] = ["R16UI", gl.RED_INTEGER, gl.UNSIGNED_SHORT];
    params[gl.R16I]  = ["R16I",  gl.RED_INTEGER, gl.SHORT];
    params[gl.R32F]  = ["R32F",  gl.RED,         gl.FLOAT];
    
    gl.activeTexture(gl.TEXTURE0);

    if (this.dataTex !== null) {
        gl.deleteTexture(this.dataTex);
        this.dataTex = null;
    }

    if (!params[internal_format]) {
        console.log(`Unknown internal format ${internal_format}`);
        return;
    }

    const [str, fmt, typ] = params[internal_format];

    this.dataTex = gl.createTexture();
    gl.bindTexture(gl.TEXTURE_3D, this.dataTex);
    gl.pixelStorei(gl.UNPACK_ALIGNMENT, 1);
    gl.texStorage3D(gl.TEXTURE_3D, 1, internal_format, X,Y,Z);
    
    // 统一设置过滤模式,整数纹理必须用NEAREST
    const filter = (internal_format === gl.R8UI || internal_format === gl.R16UI || internal_format === gl.R16I) ? gl.NEAREST : gl.NEAREST; // 浮点格式可按需切换为LINEAR
    gl.texParameteri(gl.TEXTURE_3D, gl.TEXTURE_MIN_FILTER, filter);
    gl.texParameteri(gl.TEXTURE_3D, gl.TEXTURE_MAG_FILTER, filter);
    gl.texParameteri(gl.TEXTURE_3D, gl.TEXTURE_WRAP_R, gl.CLAMP_TO_EDGE);
    gl.texParameteri(gl.TEXTURE_3D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
    gl.texParameteri(gl.TEXTURE_3D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);    
    
    gl.texSubImage3D(gl.TEXTURE_3D, 0, 0,0,0, X,Y,Z, fmt, typ, data);
    
    // 检查错误
    const err = gl.getError();
    if (err !== gl.NO_ERROR) {
        console.error(`WebGL error: ${err}`);
    }
}

R16UI 主程序示例

// R16UI 正确使用方式
const data = new Uint16Array(W*H*N);
// 初始化数据,比如填充非零值
data.fill(0xFFFF);
internal_format = sys.gl.R16UI;
setDataTexture(W,H,N, data, internal_format);

R8UI 片段着色器调试版

#version 300 es

precision highp int;
precision highp float;

uniform highp usampler3D volume;
uniform vec2 resolution;

out vec4 fragColor;

void main() {
    vec3 pos = vec3(gl_FragCoord.x / resolution.x, gl_FragCoord.y / resolution.y, 0.5); // 替换为你的实际坐标计算逻辑
    
    // 检查坐标范围
    if (pos.x < 0.0 || pos.x > 1.0 || pos.y < 0.0 || pos.y > 1.0 || pos.z < 0.0 || pos.z > 1.0) {
        fragColor = vec4(1.0, 0.0, 0.0, 1.0);
        return;
    }
    
    uint val_ = texture(volume, pos).r;
    fragColor = val_ > 0u ? vec4(0.5, 0.5, 0.5, 1.0) : vec4(0.0, 0.0, 0.0, 1.0);
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.22 15:03:51