OpenGL渲染PNG笑脸纹理出现重复错乱问题,如何排查解决?
OpenGL纹理显示错乱问题排查
问题现象
尝试使用OpenGL在屏幕上渲染笑脸贴图,预期在矩形区域内正常显示完整笑脸,实际出现纹理重复、显示错乱的问题。
实际运行效果

原始素材

相关代码
主程序代码
#include <glad/glad.h> #include <GLFW/glfw3.h> #include <stb_image.h> #include "include/Shader.h" #include <iostream> #include <math.h> #include <stdexcept> void ercb(int err, const char* d) { fprintf(stderr, "Error: %s\n", d); } void fbfcb(GLFWwindow* w, int t, int h) { glViewport(0, 0, t, h); } void glfwProcessInput(GLFWwindow* w) { if(glfwGetKey(w, GLFW_KEY_ESCAPE) == GLFW_PRESS) { glfwSetWindowShouldClose(w, true); } } float vertices[] = { // positions // colors // texture coords 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, // top right 0.5f, -0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, // bottom right -0.5f, -0.5f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, // bottom left -0.5f, 0.5f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f // top left }; unsigned int indices[] = { // note that we start from 0! 0, 1, 3, // first triangle 1, 2, 3 // second triangle }; int main(int argc, char *argv[]) { glfwInit(); glfwSetErrorCallback(ercb); glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); GLFWwindow* w = glfwCreateWindow(1080, 720, "OpenGL works!", NULL, NULL); if(!w) { std::cout << "Failed to create a window" << std::endl; glfwTerminate(); _Exit(1); } glfwMakeContextCurrent(w); if(!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) { std::cout << "Failed to initialize GLAD" << std::endl; _Exit(1); } glViewport(0, 0, 800, 600); glfwSetFramebufferSizeCallback(w, fbfcb); unsigned int VBO; glGenBuffers(1, &VBO); glBindBuffer(GL_ARRAY_BUFFER, VBO); glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); Shader shader("shaders/vertexshader.glsl", "shaders/fragmentshader.glsl"); unsigned int VAO; glGenVertexArrays(1, &VAO); glBindVertexArray(VAO); glBindBuffer(GL_ARRAY_BUFFER, VBO); glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); stbi_set_flip_vertically_on_load(true); unsigned int texture; int width, height, nrChannels; glGenTextures(1, &texture); glBindTexture(GL_TEXTURE_2D, texture); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); unsigned char *data = stbi_load("awesomeface.png", &width, &height, &nrChannels, 0); if(data) { glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data); } else { std::cout << "Failed to load texture" << std::endl; } stbi_image_free(data); unsigned int EBO; glBindVertexArray(VAO); glBindBuffer(GL_ARRAY_BUFFER, VBO); glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); glGenBuffers(1, &EBO); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO); glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW); glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)0); glEnableVertexAttribArray(0); glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(3 * sizeof(float))); glEnableVertexAttribArray(1); glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(6 * sizeof(float))); glEnableVertexAttribArray(2); glUseProgram(shader.ID); glUniform1i(glGetUniformLocation(shader.ID, "texture"), 0); while(!glfwWindowShouldClose(w)) { glfwProcessInput(w); glClearColor(0.f, 0.5f, 1.f, 0.f); glClear(GL_COLOR_BUFFER_BIT); glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, texture); shader.use(); //Scware :OO glBindVertexArray(VAO); glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0); glfwPollEvents(); glfwSwapBuffers(w); } glDeleteVertexArrays(1, &VAO); glDeleteBuffers(1, &VBO); glDeleteBuffers(1, &EBO); glfwTerminate(); system("pause"); }
自定义Shader类代码
#ifndef SHADER_H_INCLUDED #define SHADER_H_INCLUDED #include <glad/glad.h> #include <string> #include <fstream> #include <sstream> #include <iostream> class Shader { public: unsigned int ID; // constructor generates the shader on the fly // ------------------------------------------------------------------------ Shader(const char* vertexPath, const char* fragmentPath) { // 1. retrieve the vertex/fragment source code from filePath std::string vertexCode; std::string fragmentCode; std::ifstream vShaderFile; std::ifstream fShaderFile; // ensure ifstream objects can throw exceptions: vShaderFile.exceptions (std::ifstream::failbit | std::ifstream::badbit); fShaderFile.exceptions (std::ifstream::failbit | std::ifstream::badbit); try { // open files vShaderFile.open(vertexPath); fShaderFile.open(fragmentPath); std::stringstream vShaderStream, fShaderStream; // read file's buffer contents into streams vShaderStream << vShaderFile.rdbuf(); fShaderStream << fShaderFile.rdbuf(); // close file handlers vShaderFile.close(); fShaderFile.close(); // convert stream into string vertexCode = vShaderStream.str(); fragmentCode = fShaderStream.str(); } catch (std::ifstream::failure& e) { std::cout << "ERROR::SHADER::FILE_NOT_SUCCESFULLY_READ" << std::endl; } const char* vShaderCode = vertexCode.c_str(); const char * fShaderCode = fragmentCode.c_str(); // 2. compile shaders unsigned int vertex, fragment; // vertex shader vertex = glCreateShader(GL_VERTEX_SHADER); glShaderSource(vertex, 1, &vShaderCode, NULL); glCompileShader(vertex); checkCompileErrors(vertex, "VERTEX"); // fragment Shader fragment = glCreateShader(GL_FRAGMENT_SHADER); glShaderSource(fragment, 1, &fShaderCode, NULL); glCompileShader(fragment); checkCompileErrors(fragment, "FRAGMENT"); // shader Program ID = glCreateProgram(); glAttachShader(ID, vertex); glAttachShader(ID, fragment); glLinkProgram(ID); checkCompileErrors(ID, "PROGRAM"); // delete the shaders as they're linked into our program now and no longer necessary glDeleteShader(vertex); glDeleteShader(fragment); } // activate the shader // ------------------------------------------------------------------------ void use() { glUseProgram(ID); } // utility uniform functions // ------------------------------------------------------------------------ void setBool(const std::string &name, bool value) const { glUniform1i(glGetUniformLocation(ID, name.c_str()), (int)value); } // ------------------------------------------------------------------------ void setInt(const std::string &name, int value) const { glUniform1i(glGetUniformLocation(ID, name.c_str()), value); } // ------------------------------------------------------------------------ void setFloat(const std::string &name, float value) const { glUniform1f(glGetUniformLocation(ID, name.c_str()), value); } // ------------------------------------------------------------------------ void setVec4(const std::string &name, float x, float y, float z, float w) { glUniform4f(glGetUniformLocation(ID, name.c_str()), x, y, z, w); } // ------------------------------------------------------------------------ void setVec3(const std::string &name, float x, float y, float z) { glUniform3f(glGetUniformLocation(ID, name.c_str()), x, y, z); } // ------------------------------------------------------------------------ void setVec2(const std::string &name, float x, float y) { glUniform2f(glGetUniformLocation(ID, name.c_str()), x, y); } private: // utility function for checking shader compilation/linking errors. // ------------------------------------------------------------------------ void checkCompileErrors(unsigned int shader, std::string type) { int success; char infoLog[1024]; if (type != "PROGRAM") { glGetShaderiv(shader, GL_COMPILE_STATUS, &success); if (!success) { glGetShaderInfoLog(shader, 1024, NULL, infoLog); std::cout << "ERROR::SHADER_COMPILATION_ERROR of type: " << type << "\n" << infoLog << "\n -- --------------------------------------------------- -- " << std::endl; } } else { glGetProgramiv(shader, GL_LINK_STATUS, &success); if (!success) { glGetProgramInfoLog(shader, 1024, NULL, infoLog); std::cout << "ERROR::PROGRAM_LINKING_ERROR of type: " << type << "\n" << infoLog << "\n -- --------------------------------------------------- -- " << std::endl; } } } }; #endif // SHADER_H_INCLUDED
着色器代码
顶点着色器
#version 330 core layout (location = 0) in vec3 aPos; // the position variable has attribute position 0 layout (location = 1) in vec3 aColor; // the color variable has attribute position 1 layout (location = 2) in vec2 aTexCoord; out vec3 ourColor; // output a color to the fragment shader out vec2 TexCoord; void main() { gl_Position = vec4(aPos, 1.0); ourColor = aColor; TexCoord = aTexCoord; }
片段着色器
#version 330 core out vec4 FragColor; in vec3 color; in vec2 TexCoord; uniform sampler2D texture; void main() { FragColor = texture(texture, TexCoord); //colored texture //FragColor = vec4(color, 1.0) * texture(texture, TexCoord); }
问题原因
- 纹理通道格式不匹配:使用的笑脸素材是带透明通道的PNG图片,包含RGBA四个通道,但调用
glTexImage2D时指定的内部格式和源格式都是GL_RGB,OpenGL会按照3字节每像素的规则读取纹理数据,而实际源数据是4字节每像素,数据错位直接导致纹理显示错乱。 - 着色器输入输出变量名不匹配:顶点着色器中输出的颜色变量名为
ourColor,但片段着色器中定义的对应输入变量名为color,名称不匹配会导致链接后的程序中该变量值未定义,如果后续要使用顶点颜色和纹理混合的功能会出现异常,当前注释了混合逻辑,所以该错误暂时不影响纹理显示,但也需要修复。
修复方案
- 修复纹理加载的格式问题,加载时根据
nrChannels判断通道数,动态调整glTexImage2D的格式参数,同时补充生成多级渐远纹理避免小分辨率显示异常:
if(data) { GLenum format = nrChannels == 4 ? GL_RGBA : GL_RGB; glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, GL_UNSIGNED_BYTE, data); glGenerateMipmap(GL_TEXTURE_2D); }
- 修复着色器变量名不匹配问题,把片段着色器中的
in vec3 color;修改为in vec3 ourColor;即可。
内容的提问来源于stack exchange,提问作者Henry
相关产品推荐
相关产品推荐

