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

