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中的正方形会被拉伸成对应窗口比例的矩形。
具体解决步骤
添加正交投影矩阵
在顶点着色器中引入正交投影矩阵,用于根据窗口宽高比修正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; }
- 计算窗口宽高比:
窗口Resize时同步更新投影矩阵
在FramebufferSize事件中,除了更新glViewport,还要重新计算并上传投影矩阵到GPU,确保窗口尺寸变化时比例始终适配。额外排查点
- 检查
IMAGE_HEIGHT_MULTIPLIER是否为1.0,若不为1,即使适配宽高比,顶点高度与宽度也会不成比例,导致矩形。 - 确认
translatematrix仅做平移操作,未引入缩放、旋转等会导致变形的变换。
- 检查
内容的提问来源于stack exchange,提问作者InfinityCity
相关产品推荐
相关产品推荐

