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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.11 19:40:52