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

OpenGL着色器中model矩阵相关代码导致渲染失效的原因咨询

OpenGL着色器渲染故障排查:添加model矩阵后及移除后均无法渲染的原因

问题重现

开发OpenGL着色器渲染代码时遇到两个异常:

  • 在顶点着色器中添加uniform mat4 model;并将gl_Position设为model * vPosition时,无法渲染图形
  • 移除uniform mat4 model;并将gl_Position改为vPosition后,仍然无法渲染

用户提供的代码如下:

#include <GL\glew.h>
#include <GLFW\glfw3.h>
#include <iostream>

// GLM library 
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>

using namespace std;

int width, height;

// Draw Primitive(s)
void draw() {
    GLenum mode = GL_TRIANGLES;

    GLsizei indices = 6;

    glDrawElements(mode, indices, GL_UNSIGNED_BYTE, nullptr);
}

// Create and Compile Shaders
static GLuint CompileShader(const string& source, GLuint shaderType) {

    // Create Shader Object
    GLuint shaderID = glCreateShader(shaderType);
    const char* src = source.c_str();

    // Attach source code to Shader object
    glShaderSource(shaderID, 1, &src, nullptr);

    // Compile Shader
    glCompileShader(shaderID);

    // Return ID of Compiled shader
    return shaderID;
}

// Create Program Object
static GLuint CreateShaderProgram(const string& vertexShader, const string& fragmentShader) {

    // Compile vertex shader
    GLuint vertexShaderComp = CompileShader(vertexShader, GL_VERTEX_SHADER);

    // Compile fragment shader
    GLuint fragmentShaderComp = CompileShader(fragmentShader, GL_FRAGMENT_SHADER);

    // Create program object
    GLuint shaderProgram = glCreateProgram();

    // Attch vertex and fragment shaders to program object
    glAttachShader(shaderProgram, vertexShaderComp);
    glAttachShader(shaderProgram, fragmentShaderComp);

    // Link shaders to create executable
    glLinkProgram(shaderProgram);

    // Delete compiled vertex and fragment shaders
    glDeleteShader(vertexShaderComp);
    glDeleteShader(fragmentShaderComp);

    // Return Shader Program
    return shaderProgram;
}

int main(void) {

    width = 640; height = 480;

    GLFWwindow* window;

    // Initialize the library
    if (!glfwInit())
        return -1;

    // Create a windowed mode window and its OpenGL context
    window = glfwCreateWindow(width, height, "Main Window", NULL, NULL);
    if (!window) {
        glfwTerminate();
        return -1;
    }

    // Make the window's context current
    glfwMakeContextCurrent(window);

    // Initialize GLEW
    if (glewInit() != GLEW_OK)
        cout << "Error!" << endl;

    GLfloat vertices[] = {

        // Triangle 1
        -0.5, -0.5, 0.0, // vert 0
        1.0, 0.0, 0.0,  // red

        -0.5, 0.5, 0.0, // vert 1
        0.0, 1.0, 0.0,  // green

        0.5, -0.5, 0.0,  // vert 2
        0.0, 0.0, 1.0,  // blue

        // Part of Triangle 2

        0.5, 0.5, 0.0,  // vert 3
        1.0, 0.0, 1.0  // purple

    };

    // Define element indices 
    GLubyte indices[] = {
        0,1,2,
        1,2,3
    };

    GLuint VBO, EBO, VAO;

    glGenBuffers(1, &VBO); // Create VBO and returns ID
    glGenBuffers(1, &EBO); // Create EBO

    glGenVertexArrays(1, &VAO); // Create VAO
    glBindVertexArray(VAO);

    glBindBuffer(GL_ARRAY_BUFFER, VBO); // Select VBO and activate buffer
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO); // Select EBO

    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); // Load vertex attributes
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW); // Load indices attributes

    // Specify attributes location and layout to GPU
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat), (GLvoid*)0);
    glEnableVertexAttribArray(0);

    // Color attribute location and layout
    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat), (GLvoid*)(3 * sizeof(GLfloat)));
    glEnableVertexAttribArray(1);

    glBindVertexArray(0);

    // Vertex shader source code
    string vertexShaderSource =
        "#version 330 core\n"
        "layout(location = 0) in vec4 vPosition;\n"
        "layout(location = 1) in vec4 aColor;\n"
        "out vec4 oColor;\n"
        "uniform mat4 model;\n"
        "void main()\n"
        "{\n"
        "gl_Position = model * vPosition;\n"
        "oColor = aColor;\n"
        "}\n";

    // Fragment shader source code
    string fragmentShaderSource =
        "#version 330 core\n"
        "in vec4 oColor;\n"
        "out vec4 fragColor;\n"
        "void main()\n"
        "{\n"
        "fragColor = oColor;\n"
        "}\n";

    // Creating Shader Program
    GLuint shaderProgram = CreateShaderProgram(vertexShaderSource, fragmentShaderSource);

    while (!glfwWindowShouldClose(window)) {

        // Resize window and graphics simultaneously
        glfwGetFramebufferSize(window, &width, &height);
        glViewport(0, 0, width, height);

        // Render here
        glClear(GL_COLOR_BUFFER_BIT);

        // Use Shader Program exe and select VAO before drawing
        glUseProgram(shaderProgram); // Call Shader per-frame when updating attributes

        // Declare identity matrix 
        glm::mat4 modelMatrix;

        // Initialize transforms
        modelMatrix = glm::translate(modelMatrix, glm::vec3(0.0f, 0.0f, 0.0f));
        modelMatrix = glm::rotate(modelMatrix, 45.0f, glm::vec3(0.0f, 0.0f, 1.0f));
        modelMatrix = glm::scale(modelMatrix, glm::vec3(0.5f, 0.5f, 0.5f));

        // Select transform and sent to shader
        GLuint modelLoc = glGetUniformLocation(shaderProgram, "model");

        glBindVertexArray(VAO); // User-defined VAO must be called before draw

        // Draw primitive(s)
        draw();

        // Unbind Shader exe and VOA after drawing per frame
        glBindVertexArray(0); // In case different VAO will be used after
        glUseProgram(0); // In case different shader will be used after

        // Pass transform to Shader
        glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(modelMatrix));

        glBindVertexArray(VAO);

        // Swap front and back buffers
        glfwSwapBuffers(window);

        // Poll for and process events
        glfwPollEvents();
    }

    glfwTerminate();
    return 0;
}

核心错误及修复方案

1. Uniform矩阵设置时机完全错误

OpenGL要求在绘制调用前完成uniform变量的赋值,且必须在着色器程序激活(glUseProgram)的状态下执行。你的代码中先调用draw()(内部是glDrawElements),之后才设置model矩阵,导致绘制时uniform使用未初始化的垃圾值,顶点位置计算完全错误。

修复:调整main循环内的执行顺序,将uniform赋值移到draw()之前:

// 激活着色器程序
glUseProgram(shaderProgram);
// 获取uniform位置
GLuint modelLoc = glGetUniformLocation(shaderProgram, "model");
// 设置uniform矩阵
glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(modelMatrix));
// 绑定VAO
glBindVertexArray(VAO);
// 绘制
draw();
// 解绑VAO和着色器
glBindVertexArray(0);
glUseProgram(0);

2. GLM矩阵未初始化单位矩阵

glm::mat4 modelMatrix;默认创建的是未初始化的垃圾矩阵,而非单位矩阵。后续的平移、旋转、缩放操作基于垃圾矩阵计算,结果完全不可控。

修复:初始化矩阵为单位矩阵,同时注意GLM旋转参数为弧度而非角度:

// 初始化单位矩阵
glm::mat4 modelMatrix = glm::mat4(1.0f);
// 角度转弧度后再旋转
modelMatrix = glm::rotate(modelMatrix, glm::radians(45.0f), glm::vec3(0.0f, 0.0f, 1.0f));

3. 缺少着色器编译/链接错误检查

你的CompileShader和CreateShaderProgram函数没有检查编译和链接是否成功,一旦着色器语法错误或链接失败,程序会使用无效的着色器程序,导致无法渲染。

修复:添加编译和链接错误检查:

编译错误检查(修改CompileShader函数)

static GLuint CompileShader(const string& source, GLuint shaderType) {
    GLuint shaderID = glCreateShader(shaderType);
    const char* src = source.c_str();
    glShaderSource(shaderID, 1, &src, nullptr);
    glCompileShader(shaderID);

    // 检查编译错误
    GLint success;
    glGetShaderiv(shaderID, GL_COMPILE_STATUS, &success);
    if (!success) {
        char infoLog[512];
        glGetShaderInfoLog(shaderID, 512, nullptr, infoLog);
        cout << "Shader compilation failed:\n" << infoLog << endl;
    }

    return shaderID;
}

链接错误检查(修改CreateShaderProgram函数)

static GLuint CreateShaderProgram(const string& vertexShader, const string& fragmentShader) {
    GLuint vertexShaderComp = CompileShader(vertexShader, GL_VERTEX_SHADER);
    GLuint fragmentShaderComp = CompileShader(fragmentShader, GL_FRAGMENT_SHADER);

    GLuint shaderProgram = glCreateProgram();
    glAttachShader(shaderProgram, vertexShaderComp);
    glAttachShader(shaderProgram, fragmentShaderComp);
    glLinkProgram(shaderProgram);

    // 检查链接错误
    GLint success;
    glGetProgramiv(shaderProgram, GL_LINK_STATUS, &success);
    if (!success) {
        char infoLog[512];
        glGetProgramInfoLog(shaderProgram, 512, nullptr, infoLog);
        cout << "Program linking failed:\n" << infoLog << endl;
    }

    glDeleteShader(vertexShaderComp);
    glDeleteShader(fragmentShaderComp);

    return shaderProgram;
}

4. 循环内冗余的状态操作

main循环中draw()之后解绑VAO和着色器,又重新绑定VAO,属于无效操作,会导致状态混乱,建议直接移除这些冗余代码。

移除model矩阵后仍无法渲染的原因

即使移除model矩阵,上述第1、2、3项错误依然存在:绘制前状态设置错误、矩阵未初始化(或着色器程序无效),导致渲染流程完全异常。修复所有上述错误后,无论是否使用model矩阵,都能正常渲染。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.18 19:15:44