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

OpenGL使用相同MVP矩阵绘制两个立方体出现拉伸变形问题

问题描述

基于OpenGL开发简易3D空间场景,计划绘制橙色、绿色两个立方体,为两个立方体传入完全相同的model、view、projection矩阵,预期两立方体处于同一位置、显示效果完全一致,但实际渲染出现拉伸变形异常,线框模式下效果如下:
已开启glPolygonMode线框渲染模式
最初怀疑异常由两个立方体使用相同model矩阵导致,附项目相关代码如下。

问题原因

变形和共用model矩阵没有任何关系,是代码中三个基础错误导致:

  • view矩阵未初始化:glm::mat4 view;声明的是栈上局部变量,GLM不会默认将其初始化为单位矩阵,内存中存储的是随机垃圾值,基于这个未初始化矩阵做平移变换,结果完全不可控。
  • 投影矩阵宽高比计算错误:float(window_width / window_height)的写法是先对两个int类型值做整数除法,800/600的整数运算结果为1,转为float后仍然是1.0f,不是正确的4:3宽高比,直接导致透视投影结果拉伸。
  • 未开启深度测试、未清除深度缓冲:代码仅调用glClear(GL_COLOR_BUFFER_BIT)清除颜色缓冲,没有清除深度缓冲,也没有启用GL_DEPTH_TEST,就算矩阵修正后也会出现面绘制顺序错乱的问题。
修正方法
  1. 初始化view矩阵为单位矩阵,修改对应代码为:
glm::mat4 view(1.0f);
view = glm::translate(view, glm::vec3(0.f, 0.f, -5.f));
  1. 修正宽高比计算逻辑,先将宽高转为浮点数再做除法:
projection = glm::perspective(glm::radians(45.f), (float)window_width / (float)window_height, 0.1f, 100.f);
  1. 在GLAD加载完成后添加代码开启深度测试:
glEnable(GL_DEPTH_TEST);
  1. 修改渲染循环中的清屏逻辑,同时清除颜色缓冲和深度缓冲:
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
原项目代码

main.cpp

#include "src/Program.hpp"
#include <GLFW/glfw3.h>


// functions
void framebufferResizeCallback(GLFWwindow* window, int width, int height) {
    glViewport(0, 0, width, height);
}
void errorCallback(int error, const char* description) {
    std::cout << "error :" << description << std::endl;
}

//variables
int window_width = 800;
int window_height = 600;

int main() {

    glfwInit();
    glfwWindowHint(GLFW_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_VERSION_MINOR, 3);
    glfwWindowHint(GLFW_OPENGL_ANY_PROFILE, GLFW_OPENGL_CORE_PROFILE);

    glfwSetErrorCallback(errorCallback);

    GLFWwindow* window = glfwCreateWindow(window_width, window_height, "Opengl_triangle", NULL, NULL);
    glfwMakeContextCurrent(window);
    glfwSetFramebufferSizeCallback(window, framebufferResizeCallback);

    gladLoadGLLoader((GLADloadproc)glfwGetProcAddress);

    Program light("src/vertex.glsl", "src/fragment.glsl");
    Program object("src/vertex.glsl", "src/fragment_green.glsl");

    /// going 3D
    // calculations
    glm::mat4 model(1.f);
    model = glm::rotate(model,glm::radians(20.f), glm::vec3(1.f, 0.f, 0.6f));

    glm::mat4 view;
    view = glm::translate(view, glm::vec3(0.f, 0.f, -5.f));

    glm::mat4 projection;
    projection = glm::perspective(glm::radians(45.f), float(window_width / window_height), 0.1f, 100.f);
    // sending it to the shaders  LIGHT
    light.use();
    light.uniformMatrix4(model, "model");
    light.uniformMatrix4(view, "view");
    light.uniformMatrix4(projection, "projection");
    // sending it to the shaders  OBJECT
    object.use();
    object.uniformMatrix4(model, "model");
    object.uniformMatrix4(view, "view");
    object.uniformMatrix4(projection, "projection");



    //fun stuff
    float vertices[] = {
     -0.5f, -0.5f, -0.5f,
      0.5f, -0.5f, -0.5f,
      0.5f,  0.5f, -0.5f,
      0.5f,  0.5f, -0.5f,
     -0.5f,  0.5f, -0.5f,
     -0.5f, -0.5f, -0.5f,

     -0.5f, -0.5f,  0.5f,
      0.5f, -0.5f,  0.5f,
      0.5f,  0.5f,  0.5f,
      0.5f,  0.5f,  0.5f,
     -0.5f,  0.5f,  0.5f,
     -0.5f, -0.5f,  0.5f,

     -0.5f,  0.5f,  0.5f,
     -0.5f,  0.5f, -0.5f,
     -0.5f, -0.5f, -0.5f,
     -0.5f, -0.5f, -0.5f,
     -0.5f, -0.5f,  0.5f,
     -0.5f,  0.5f,  0.5f,

      0.5f,  0.5f,  0.5f,
      0.5f,  0.5f, -0.5f,
      0.5f, -0.5f, -0.5f,
      0.5f, -0.5f, -0.5f,
      0.5f, -0.5f,  0.5f,
      0.5f,  0.5f,  0.5f,

     -0.5f, -0.5f, -0.5f,
      0.5f, -0.5f, -0.5f,
      0.5f, -0.5f,  0.5f,
      0.5f, -0.5f,  0.5f,
     -0.5f, -0.5f,  0.5f,
     -0.5f, -0.5f, -0.5f,

     -0.5f,  0.5f, -0.5f,
      0.5f,  0.5f, -0.5f,
      0.5f,  0.5f,  0.5f,
      0.5f,  0.5f,  0.5f,
     -0.5f,  0.5f,  0.5f,
     -0.5f,  0.5f, -0.5f,
    };

    unsigned int VBO, VAO_light, VAO_object ;
    glGenVertexArrays(1, &VAO_light);
    glGenVertexArrays(1, &VAO_object);
    glGenBuffers(1, &VBO);


    glBindVertexArray(VAO_light);
    glBindBuffer(GL_ARRAY_BUFFER, VBO);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), 0);
    glEnableVertexAttribArray(0);

    glBindVertexArray(VAO_object);
    glBindBuffer(GL_ARRAY_BUFFER, VBO);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), 0);
    glEnableVertexAttribArray(0);


    
    glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
    while (!glfwWindowShouldClose(window)) {

        glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT);

        light.use();
        glBindVertexArray(VAO_light);
        glDrawArrays(GL_TRIANGLES, 0, 36);

        object.use();
        glBindVertexArray(VAO_object);
        glDrawArrays(GL_TRIANGLES, 0, 36);

        
        
        glfwSwapBuffers(window);
        glfwPollEvents();
    }
    glfwTerminate();
    return 0;
}

片段着色器

#version 330 core

out vec4 fragColor;

void main() {
    fragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f);
}

顶点着色器

#version 330 core

layout (location = 0) in vec3 aPos;

uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;

void main() {
    gl_Position = projection * view * model * vec4(aPos , 1.0f);
}

Shader与Program封装类

class Program {
    Shader vertex;
    Shader fragment;
    GLuint ID;

public:
    // essential methods
    Program() : ID(0) {
        std::cout << "program default constructor" << std::endl;
    }
    Program(const char* vertexShader_path, const char * fragmentShader_path) {
        // recreate shaders
        vertex = Shader(vertexShader_path, GL_VERTEX_SHADER);
        fragment = Shader(fragmentShader_path, GL_FRAGMENT_SHADER);

        // create shader program
        ID = glCreateProgram();
        glAttachShader(ID, vertex.getID());
        glAttachShader(ID, fragment.getID());
        glLinkProgram(ID);
        checkProgramLinking(ID);
    }

    /// usage methods
    void use()      { glUseProgram(ID); }
    GLuint getID()  { return ID; }
    // accesing uniforms methods
    void uniformMatrix4(glm::mat4 matrix, const char* matrixName) {
        GLuint uniformLocation = glGetUniformLocation(ID, matrixName);
        glUniformMatrix4fv(uniformLocation, 1, GL_FALSE, glm::value_ptr(matrix));
    }
private:
    void checkProgramLinking(unsigned int& program) {
        int success;
        char log[512];
        glGetProgramiv(program, GL_LINK_STATUS, &success);
        if (!success) {
            glGetProgramInfoLog(program, 512, NULL, log);
            std::cout << "error linking program : " << log << std::endl;
        }
    }
}




class Shader {
    GLuint ID;


public:
    // essential methods
    Shader() : ID(0) {
        std::cout << "default shader constructor" << std::endl;
    }
    Shader(const char* path, GLenum type) {

        // converting shader code from file
        std::string shaderCodeS;
        std::ifstream shaderFile;
        shaderFile.exceptions(std::ifstream::failbit | std::ifstream::badbit);
        try
        {
            shaderFile.open(path);
            std::stringstream shaderStream;
            shaderStream << shaderFile.rdbuf();
            shaderFile.close();
            shaderCodeS = shaderStream.str();
        } 
        catch (std::ifstream::failure& e) {
            std::cout << "ERROR::SHADER::FILE_NOT_SUCCESFULLY_READ: " << e.what() << std::endl;
        }
        const char* shaderCode = shaderCodeS.c_str();
        
        // shader creation
        ID = glCreateShader(type);
        glShaderSource(ID, 1, &shaderCode, NULL);
        glCompileShader(ID);
        checkShaderCompilation(ID); 


        std::cout << "shader argumented constructor" << std::endl;
    }

    // methods for usage
    GLuint getID() { return ID; }
private:
    void checkShaderCompilation(unsigned int& shader) {
        int success;
        char log[512];
        glGetShaderiv(shader, GL_COMPILE_STATUS, &success);
        if (!success) {
            glGetShaderInfoLog(shader, 512, NULL, log);
            std::cout << "error compiling shader:" << log << std::endl;
        }
    }

};

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 16:42:58