OpenGL sampler2D数组同drawcall绑定多纹理仅显示单纹理问题
问题根因
你之前怀疑的纹理绑定环节不存在问题,Texture::Bind实现、sampler uniform的赋值逻辑都是正确的。核心问题出在GLSL对sampler数组的索引限制:
- 你在片元着色器中使用顶点插值得到的逐片元变量
v_texIndex作为u_Textures数组的下标,在OpenGL 4.5桌面端标准实现中,sampler类型数组的索引必须为动态均匀表达式——即同一并行执行组内的所有片元着色器调用必须使用相同的索引值,否则会触发未定义行为,这就是你无论怎么修改索引值都只能渲染出单张纹理的根本原因。 - 快速验证方法:把片元着色器里的索引硬编码为0或1测试,如果硬编码0时正常显示tex1、硬编码1时正常显示tex2,即可完全确认问题来源。
解决方案
根据你的硬件兼容需求选以下任意一种方案即可:
方案1:使用无绑定纹理(Bindless Texture)
这是最匹配你单次draw call逐片元切换纹理需求的方案,2016年之后发布的独立显卡基本都支持GL_ARB_bindless_texture扩展:
- 纹理创建完成后获取每个纹理的64位全局句柄,将句柄标记为常驻后上传到着色器uniform数组
- 开启bindless扩展后,sampler数组不再要求索引为动态均匀表达式,可以直接用逐片元的插值变量作为索引
核心代码修改:
// 片元着色器 #version 450 core #extension GL_ARB_bindless_texture : require layout(location = 0) out vec4 color; in vec2 v_TexCoord; in float v_texIndex; layout(bindless_sampler) uniform sampler2D u_Textures[2]; void main() { int index = int(v_texIndex); vec4 texColor = texture(u_Textures[index], v_TexCoord); color = texColor.a < 0.01 ? vec4(1.0f, 0.0f, 0.0f, 1.0f) : texColor; }
// C++侧纹理上传逻辑 GLuint64 texHandles[2]; texHandles[0] = glGetTextureHandleARB(tex1.getRendererID()); glMakeTextureHandleResidentARB(texHandles[0]); texHandles[1] = glGetTextureHandleARB(tex2.getRendererID()); glMakeTextureHandleResidentARB(texHandles[1]); auto loc = glGetUniformLocation(sh.getRendererID(), "u_Textures"); glUniformHandleui64vARB(loc, 2, texHandles);
方案2:使用2D纹理数组替代sampler2D数组
该方案不需要依赖扩展,兼容性更好:
- 将两张纹理上传为
GL_TEXTURE_2D_ARRAY的两个独立层,要求两张纹理的尺寸、内部格式完全一致 - 采样时将纹理索引作为第三维坐标传入采样函数即可,不存在动态索引限制,示例:
uniform sampler2DArray u_TexArray; // 采样代码 vec4 texColor = texture(u_TexArray, vec3(v_TexCoord, v_texIndex));
方案3:按纹理拆分绘制批次
如果不需要严格控制单次draw call,直接将使用不同纹理的顶点拆分到两次绘制调用中,每次绘制前绑定对应的纹理即可,不需要在片元着色器中维护sampler数组。
额外排查项:如果硬编码索引后依然只能显示单张纹理,检查顶点属性指针的配置,确认
v_texIndex对应的属性步长、偏移、数据类型设置正确,没有出现所有顶点的索引值都被解析为1的情况。
内容的提问来源于stack exchange,提问作者Adrian
相关产品推荐
相关产品推荐

