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
相关产品推荐
相关产品推荐

