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

Android相机着色器中SSBO值异常问题排查求助

问题分析与解决

核心错误点

1. 条件判断完全写反,直接导致数组越界

你的着色器中if (index > overlay.data.length())的逻辑完全错误:只有当索引超出数组长度时才去访问overlay.data[index],这必然触发数组越界,导致着色器崩溃、输出损坏。正确逻辑应该是索引在有效范围内时才使用overlay数据:

if (index < overlay.data.length()) {
    float value = overlay.data[index];
    fragColor = vec4(value, value, value, 1.0); // 示例:用灰度值叠加
} else {
    fragColor = texture(sTexture, vTextureCoord);
}

2. SSBO数据的字节顺序错误

Kotlin中创建ByteBuffer时,默认是大端字节序,但OpenGL使用的是本地字节序(小端),这会导致着色器读取到的cols、rows以及data中的浮点值全部错乱。必须添加字节序设置:

private fun addOverlay(cols: Int, rows: Int, data: FloatArray) {
    val totalSize = 2 * Int.SIZE_BYTES + data.size * Float.SIZE_BYTES
    val buffer = ByteBuffer.allocateDirect(totalSize)
        .order(ByteOrder.nativeOrder()) // 关键:设置本地字节序
    buffer.putInt(cols)
    buffer.putInt(rows)
    data.forEach(buffer::putFloat) // 修正:FloatArray没有values属性,直接用forEach
    buffer.position(0)

    // ... 后续SSBO创建代码不变
}

3. 有符号/无符号整数类型不匹配导致的比较异常

GLSL中overlay.data.length()返回的是无符号size_t类型,而cols * rows是有符号int类型。直接比较时,有符号整数会被强制转换为无符号整数:如果cols * rows因溢出变为负数(比如高分辨率场景),转换后会变成极大的无符号数,导致cols * rows == overlay.data.length()返回false。
用中间变量将length()转为int后,类型统一,比较就会正常:

int size = overlay.cols * overlay.rows;
int length = int(overlay.data.length()); // 显式转为有符号int
if (size == length) { /* 结果正确 */ }

4. 窗口坐标与纹理坐标混淆

gl_FragCoord.x/y是窗口像素坐标,如果渲染窗口尺寸和overlay数组的cols/rows(纹理尺寸)不一致,x/y会超出overlay的有效范围,导致索引越界。正确做法是用传入的纹理坐标计算对应纹理的像素位置:

void main() {
    // 根据纹理坐标计算对应的overlay像素位置
    int x = int(vTextureCoord.x * overlay.cols);
    int y = int(vTextureCoord.y * overlay.rows);
    // 强制 clamp 到有效范围,避免浮点精度问题导致的越界
    x = clamp(x, 0, overlay.cols - 1);
    y = clamp(y, 0, overlay.rows - 1);
    int index = y * overlay.cols + x;

    // ... 后续逻辑
}

实用调试技巧

  • 强制开启OpenGL错误检查:在所有GL调用后加入错误检查,快速定位API层面的错误:

    fun checkGLError(tag: String) {
        var error: Int
        while (GLES32.glGetError().also { error = it } != GLES32.GL_NO_ERROR) {
            Log.e(tag, "OpenGL error code: $error")
        }
    }
    

    在glGenBuffers、glBindBuffer、glBufferData、glBindBufferBase之后都调用这个函数。

  • 简化着色器输出调试:暂时注释掉overlay数据访问,用固定颜色标记有效索引区域,验证索引计算是否正确:

    if (index < overlay.data.length()) {
        fragColor = vec4(1.0, 0.0, 0.0, 1.0); // 红色表示有效区域
    } else {
        fragColor = texture(sTexture, vTextureCoord);
    }
    
  • 可视化SSBO中的参数:将cols、rows转为颜色输出,验证SSBO数据是否正确传入:

    // 假设最大分辨率为2000x2000,将数值归一化到0-1范围
    fragColor = vec4(float(overlay.cols)/2000.0, float(overlay.rows)/2000.0, 0.0, 1.0);
    
  • 验证SSBO内存大小:在Kotlin中打印计算的总内存大小,确保与glBufferData传入的大小一致:

    val totalSize = 2 * Int.SIZE_BYTES + data.size * Float.SIZE_BYTES
    Log.d("SSBO", "Calculated total size: $totalSize")
    

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 18:15:01