GLSL中3x3矩阵作为顶点属性无法生效的问题求助
OpenGL实例化渲染2D矩形空白问题
我在用OpenGL实现简单2D矩形渲染器,打算把每个矩形的3x3变换矩阵存在缓冲区对象里,作为顶点属性传给顶点着色器,但屏幕始终空白。用uniform变量手动传矩阵能正常显示,所以问题肯定出在缓冲区/属性指针的创建或数据写入环节。
VBO与VAO创建代码
let mut quad_vbo: GLuint = 0; let mut instance_vbo: GLuint = 0; let mut vao: GLuint = 0; let instance_buffer_width = 100; unsafe { gl::GenVertexArrays(1, &mut vao); gl::GenBuffers(1, &mut quad_vbo); gl::BindVertexArray(vao); gl::BindBuffer(gl::ARRAY_BUFFER, quad_vbo); let (_, data_bytes, _) = quad_vertex_data.align_to::<u8>(); gl::BufferData( gl::ARRAY_BUFFER, (size_of::<Vertex>() * quad_vertex_data.len()) as GLsizeiptr, data_bytes.as_ptr() as *const GLvoid, gl::STATIC_DRAW ); let position_offset = memoffset::offset_of!(Vertex, pos) as i32; gl::EnableVertexAttribArray(0); gl::VertexAttribPointer( 0, 2, gl::FLOAT, gl::FALSE, size_of::<Vertex>() as i32, position_offset as *const std::ffi::c_void, ); gl::BindBuffer(gl::ARRAY_BUFFER, 0); gl::GenBuffers(1, &mut instance_vbo); gl::BindBuffer(gl::ARRAY_BUFFER, instance_vbo); gl::BufferData( gl::ARRAY_BUFFER, (size_of::<Matrix3<f32>>() * instance_buffer_width) as GLsizeiptr, 0 as *const GLvoid, gl::STATIC_DRAW ); let vec_3_size = size_of::<Matrix3<f32>>() as i32; gl::EnableVertexAttribArray(1); gl::VertexAttribPointer( 1, 3, gl::FLOAT, gl::FALSE, vec_3_size * 3, 0 as *const std::ffi::c_void, ); gl::EnableVertexAttribArray(2); gl::VertexAttribPointer( 2, 3, gl::FLOAT, gl::FALSE, vec_3_size * 3, (1 * vec_3_size) as *const std::ffi::c_void, ); gl::EnableVertexAttribArray(3); gl::VertexAttribPointer( 3, 3, gl::FLOAT, gl::FALSE, vec_3_size * 3, (2 * vec_3_size) as *const std::ffi::c_void, ); gl::VertexAttribDivisor(1, 1); gl::VertexAttribDivisor(2, 1); gl::VertexAttribDivisor(3, 1); gl::BindVertexArray(0); }
向缓冲区添加对象代码
pub fn add(&mut self, transform: Matrix3<f32>) { self.shader_program.use_(); // Use a uniform instead of a buffer // self.shader_program.set_matrix_3f("uModel", &transform); unsafe { let transform_slice = [transform]; let (_, data_bytes, _) = transform_slice.align_to::<u8>(); gl::BindBuffer(gl::ARRAY_BUFFER, self.instance_vbo); gl::BufferSubData( gl::ARRAY_BUFFER, (self.index*data_bytes.len()) as GLsizeiptr, data_bytes.len() as GLsizeiptr, data_bytes.as_ptr() as *const GLvoid, ); println!("Added object at index: {}, {}, {:?}", (self.index*data_bytes.len()), data_bytes.len(), data_bytes); } self.index += 1; }
绘制代码
pub fn present(&mut self) { unsafe { self.shader_program.use_(); gl::BindVertexArray(self.vao); gl::DrawArraysInstanced(gl::TRIANGLE_FAN, 0, 4, self.index as i32); gl::BindVertexArray(0); } }
顶点着色器
#version 330 core layout (location = 0) in vec2 position; layout (location = 1) in mat3 model; uniform mat4 uProjection; // uniform mat3 uModel; // Replacing model with uModel works just fine void main() { gl_Position = uProjection * vec4(model * vec3(position, 1.0), 1.0); }
问题排查与修复
1. 顶点属性步长与偏移设置错误
原代码错误地将整个Matrix3<f32>的大小当作单个vec3的大小,导致步长和偏移计算完全偏离实际数据布局。每个mat3在顶点着色器中会被拆分为3个vec3属性,分别占据location 1、2、3,正确的步长应为单个Matrix3<f32>的字节数,偏移则是每个vec3在矩阵中的字节偏移。
修改VAO创建中的属性指针部分:
// 替换原有的vec_3_size定义 let matrix_stride = size_of::<Matrix3<f32>>() as i32; let vec3_size = size_of::<[f32; 3]>() as i32; // 单个vec3的字节数 gl::EnableVertexAttribArray(1); gl::VertexAttribPointer( 1, 3, gl::FLOAT, gl::FALSE, matrix_stride, 0 as *const std::ffi::c_void, ); gl::EnableVertexAttribArray(2); gl::VertexAttribPointer( 2, 3, gl::FLOAT, gl::FALSE, matrix_stride, vec3_size as *const std::ffi::c_void, ); gl::EnableVertexAttribArray(3); gl::VertexAttribPointer( 3, 3, gl::FLOAT, gl::FALSE, matrix_stride, (2 * vec3_size) as *const std::ffi::c_void, );
2. 优化实例数据写入逻辑
原代码通过align_to转u8切片的方式没必要,直接使用矩阵的原始指针更高效,同时避免潜在的对齐问题:
修改add方法:
pub fn add(&mut self, transform: Matrix3<f32>) { unsafe { gl::BindBuffer(gl::ARRAY_BUFFER, self.instance_vbo); let offset = (self.index * size_of::<Matrix3<f32>>()) as GLsizeiptr; gl::BufferSubData( gl::ARRAY_BUFFER, offset, size_of::<Matrix3<f32>>() as GLsizeiptr, transform.as_ptr() as *const GLvoid, ); } self.index += 1; }
3. 确认矩阵存储顺序
OpenGL默认使用列主序矩阵,如果你使用的Matrix3<f32>是行主序实现,需要先转置矩阵再传入,否则变换会错误导致矩形不可见。
额外检查
- 确保
instance_buffer_width足够容纳你要添加的实例数量,避免缓冲区越界 - 绘制时确认
self.index大于0,DrawArraysInstanced的最后一个参数是实例数量,不能为0
内容的提问来源于stack exchange,提问作者Shuppin
相关产品推荐
相关产品推荐

