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

Rust+OpenGL:代码中距离相等但绘制后不符的问题排查

OpenGL矩形变形问题排查与解决

我用Rust结合GLFW和OpenGL绑定开发,已经创建了用于VBO的顶点数组,预期左上顶点到顶部边框与左侧边框的距离相等,绘制出正方形,但实际显示是矩形。简化代码后问题依旧,怀疑和窗口尺寸有关,附上相关代码请求排查。


顶点数组(VBO)代码

let x = 2.0 / ((HORIZONTAL_BOOK_AMOUNT * 2) + HORIZONTAL_BOOK_AMOUNT + 1) as f32; 
// x是基于NDC宽度除以水平绘制物体数量得到的期望间距
// 对应附图的情况,HORIZONTAL_BOOK_AMOUNT设为4

let mut vbo = 0;

let side = x * IMAGE_HEIGHT_MULTIPLIER; 
// x乘以常量,得到物体的期望高度

let vertices: Vec<f32> = vec![
-1.0 + x, 1.0 - x, 0.0, 1.0, //左上顶点
-1.0 + 3.0 * x, 1.0 - x, 1.0, 1.0, //右上顶点
-1.0 + 3.0 * x, 1.0 - x - (2.0 * side), 1.0, 0.0, //右下顶点
-1.0 + 3.0 * x, 1.0 - x - (2.0 * side), 1.0, 0.0, //右下顶点
-1.0 + x, 1.0 - x - (2.0 * side), 0.0, 0.0, //左下顶点
-1.0 + x, 1.0 - x, 0.0, 1.0]; //左上顶点 

gl::GenBuffers(1, &mut vbo);

gl::BindBuffer(gl::ARRAY_BUFFER, vbo);

gl::BufferData(gl::ARRAY_BUFFER, (vertices.len() * std::mem::size_of::<GLfloat>()) as GLsizeiptr, &vertices[0] as *const f32 as *const std::ffi::c_void, gl::STATIC_DRAW);

VAO代码

let mut VAO = 0;
gl::GenVertexArrays(1, &mut VAO);

gl::BindVertexArray(VAO);

let stride = 4 * std::mem::size_of::<GLfloat>() as GLsizei;

gl::VertexAttribPointer(0, 2, gl::FLOAT, gl::FALSE, stride, std::ptr::null());

gl::EnableVertexAttribArray(0);

gl::VertexAttribPointer(1, 2, gl::FLOAT, gl::FALSE, stride, (2 * std::mem::size_of::<GLfloat>()) as *const std::ffi::c_void);

gl::EnableVertexAttribArray(1);

顶点着色器代码

#version 430 core
layout (location = 0) in vec2 aPos;

layout (location = 1) in vec2 aTexCoord;

out vec2 TexCoord;

uniform mat4 translatematrix; 
// 这个矩阵用于遍历物体数组(比如本例中的书籍),根据数量和水平排列情况绘制

void main()
{
    gl_Position = translatematrix * vec4(aPos, 0.0, 1.0);
    TexCoord = aTexCoord;
}

片段着色器代码

#version 430 core

out vec4 FragColor;

in vec2 TexCoord;

uniform sampler2D tex1;

void main()
{
    FragColor = texture(tex1, TexCoord);
}

glViewport设置代码

for (_, event) in glfw::flush_messages(&events) {

...

match event {
    glfw::WindowEvent::FramebufferSize(width, height) => {
        gl::Viewport(0, 0, width, height); 
    },

问题原因与解决方法

核心问题:窗口宽高比未适配NDC坐标

OpenGL的NDC(标准化设备坐标)是正方形范围(x、y轴均为[-1,1]),但实际窗口是矩形的。glViewport仅负责将NDC映射到窗口像素,不会纠正宽高比差异导致的拉伸——如果窗口宽高不等,NDC中的正方形会被拉伸成对应窗口比例的矩形。

具体解决步骤

  1. 添加正交投影矩阵
    在顶点着色器中引入正交投影矩阵,用于根据窗口宽高比修正NDC坐标,结合现有的平移矩阵使用:

    • 计算窗口宽高比:aspect_ratio = 窗口宽度 as f32 / 窗口高度 as f32
    • 构建正交投影矩阵(保持y轴范围[-1,1],x轴按宽高比缩放):
      let aspect_ratio = window.get_framebuffer_size().0 as f32 / window.get_framebuffer_size().1 as f32;
      let proj_matrix = [
          1.0 / aspect_ratio, 0.0, 0.0, 0.0,
          0.0, 1.0, 0.0, 0.0,
          0.0, 0.0, 1.0, 0.0,
          0.0, 0.0, 0.0, 1.0,
      ];
      
    • 修改顶点着色器,整合投影矩阵与平移矩阵:
      #version 430 core
      layout (location = 0) in vec2 aPos;
      layout (location = 1) in vec2 aTexCoord;
      
      out vec2 TexCoord;
      
      uniform mat4 projmatrix; // 新增投影矩阵uniform
      uniform mat4 translatematrix;
      
      void main()
      {
          gl_Position = projmatrix * translatematrix * vec4(aPos, 0.0, 1.0);
          TexCoord = aTexCoord;
      }
      
  2. 窗口Resize时同步更新投影矩阵
    在FramebufferSize事件中,除了更新glViewport,还要重新计算并上传投影矩阵到GPU,确保窗口尺寸变化时比例始终适配。

  3. 额外排查点

    • 检查IMAGE_HEIGHT_MULTIPLIER是否为1.0,若不为1,即使适配宽高比,顶点高度与宽度也会不成比例,导致矩形。
    • 确认translatematrix仅做平移操作,未引入缩放、旋转等会导致变形的变换。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.13 06:35:48