使用glReadPixels()获取点击颜色时始终返回背景色的问题
解决glReadPixels始终返回背景色的问题
我尝试通过OpenGL的glReadPixels()函数获取点击位置的颜色,但无论点击屏幕哪个位置,都只返回背景色。相关代码如下:
#include <iostream> #include <glad/glad.h> #include <GLFW/glfw3.h> #include <glm/gtc/matrix_transform.hpp> #include <glm/gtc/type_ptr.hpp> const char *vertexShaderSource = "#version 330 core " "layout (location = 0) in vec4 aPos; " "uniform mat4 projection; " "uniform mat4 model; " "void main() " "{ " " gl_Position = projection * model * vec4(aPos.xy, 0.0, 1.0); " "}\0"; const char *fragmentShaderSource = "#version 330 core " "out vec4 FragColor; " "void main() " "{ " " FragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f); " "}\n\0"; int main() { if (glfwInit()) { glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4); glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0); glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); } else { std::cout << "glfw failed to initialise" << std::endl; } GLFWwindow *window = glfwCreateWindow(500, 500, "Opengl Selection", NULL, NULL); glfwMakeContextCurrent(window); if (!gladLoadGLLoader((GLADloadproc) glfwGetProcAddress)) { std::cout << "Failed to initialise glad" << std::endl; } glClearColor(0.3f, 0.2f, 0.1f, 1.0f); glViewport(0, 0, 500, 500); unsigned int vertexShader = glCreateShader(GL_VERTEX_SHADER); glShaderSource(vertexShader, 1, &vertexShaderSource, NULL); glCompileShader(vertexShader); int success; char infoLog[512]; glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success); if (!success) { glGetShaderInfoLog(vertexShader, 512, NULL, infoLog); std::cout << "ERROR::SHADER::VERTEX::COMPILATION_FAILED " << infoLog << std::endl; } unsigned int fragmentShader = glCreateShader(GL_FRAGMENT_SHADER); glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL); glCompileShader(fragmentShader); glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success); if (!success) { glGetShaderInfoLog(fragmentShader, 512, NULL, infoLog); std::cout << "ERROR::SHADER::FRAGMENT::COMPILATION_FAILED " << infoLog << std::endl; } unsigned int shaderProgram = glCreateProgram(); glAttachShader(shaderProgram, vertexShader); glAttachShader(shaderProgram, fragmentShader); glLinkProgram(shaderProgram); glGetProgramiv(shaderProgram, GL_LINK_STATUS, &success); if (!success) { glGetProgramInfoLog(shaderProgram, 512, NULL, infoLog); std::cout << "ERROR::SHADER::PROGRAM::LINKING_FAILED " << infoLog << std::endl; } glDeleteShader(vertexShader); glDeleteShader(fragmentShader); float vertices[] = { // pos // tex 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f }; unsigned int VBO, VAO; glGenVertexArrays(1, &VAO); glGenBuffers(1, &VBO); glBindBuffer(GL_ARRAY_BUFFER, VBO); glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); glBindVertexArray(VAO); glEnableVertexAttribArray(0); glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void *) 0); glBindBuffer(GL_ARRAY_BUFFER, 0); glBindVertexArray(0); glUseProgram(shaderProgram); glm::mat4 projection = glm::ortho(0.0f, 1000.0f, 1000.0f, 0.0f, -1.0f, 1.0f); glUniformMatrix4fv(glGetUniformLocation(shaderProgram, "projection"), 1, GL_FALSE, glm::value_ptr(projection)); while (!glfwWindowShouldClose(window)) { glfwPollEvents(); glClear(GL_COLOR_BUFFER_BIT); glUseProgram(shaderProgram); // glm::mat4 projection = glm::ortho(0.0f, 500.0f, 500.0f, 0.0f, -1.0f, 1.0f); glm::mat4 model = glm::mat4(1.0f); model = glm::translate(model, glm::vec3(0.0f, 0.0f, 0.0f)); model = glm::scale(model, glm::vec3(100.0f, 100.0f, 0.0f)); glUniformMatrix4fv(glGetUniformLocation(shaderProgram, "model"), 1, GL_FALSE, glm::value_ptr(model)); glBindVertexArray(VAO); glDrawArrays(GL_TRIANGLES, 0, 6); glBindVertexArray(0); glfwSetMouseButtonCallback(window, [](GLFWwindow *window, int button, int action, int mods) { if (action == GLFW_PRESS) { double xPos, yPos; glfwGetCursorPos(window, &xPos, &yPos); glPixelStorei(GL_UNPACK_ALIGNMENT, 1); unsigned char data[4]; glReadPixels(xPos, yPos, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, data); std::cout << (float) data[0] << " " << (float) data[1] << " " << (float) data[2] << " " << (float) data[ 3] << std::endl; } }); glfwSwapBuffers(window); glfwPollEvents(); } return 0; }
问题根源及修复步骤
1. 鼠标Y轴坐标不匹配
GLFW的glfwGetCursorPos返回的Y坐标从窗口顶部开始计算,而OpenGL帧缓冲的Y坐标从底部开始。直接使用原始坐标会导致读取位置与实际点击位置上下颠倒。
修复:获取窗口高度,转换Y坐标:
int width, height; glfwGetWindowSize(window, &width, &height); yPos = height - yPos; // 转换为OpenGL帧缓冲坐标系
2. 回调函数重复注册
你在主循环的每次迭代中都调用glfwSetMouseButtonCallback,反复覆盖回调函数会导致逻辑异常。应将回调注册移到主循环外部,初始化窗口后仅设置一次。
3. 投影矩阵与窗口尺寸不匹配
你的投影矩阵范围是0.0f到1000.0f,但窗口尺寸为500x500。这导致渲染的正方形(缩放100倍)仅占据窗口左上角100x100的区域,大部分点击都落在正方形之外,自然读取到背景色。
修复:将投影矩阵范围改为与窗口尺寸一致:
glm::mat4 projection = glm::ortho(0.0f, 500.0f, 500.0f, 0.0f, -1.0f, 1.0f);
4. glReadPixels读取时机问题
原代码中点击触发回调时,当前帧已通过glfwSwapBuffers交换到前台,此时读取的是后台缓冲(下一帧的初始背景)。修复后的回调确保读取当前上下文的帧缓冲数据。
修复后的完整代码
#include <iostream> #include <glad/glad.h> #include <GLFW/glfw3.h> #include <glm/gtc/matrix_transform.hpp> #include <glm/gtc/type_ptr.hpp> const char *vertexShaderSource = "#version 330 core\n" "layout (location = 0) in vec4 aPos;\n" "uniform mat4 projection;\n" "uniform mat4 model;\n" "void main()\n" "{\n" " gl_Position = projection * model * vec4(aPos.xy, 0.0, 1.0);\n" "}\0"; const char *fragmentShaderSource = "#version 330 core\n" "out vec4 FragColor;\n" "void main()\n" "{\n" " FragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f);\n" "}\n\0"; // 鼠标回调函数 void mouseButtonCallback(GLFWwindow* window, int button, int action, int mods) { if (action == GLFW_PRESS && button == GLFW_MOUSE_BUTTON_LEFT) { double xPos, yPos; glfwGetCursorPos(window, &xPos, &yPos); int width, height; glfwGetWindowSize(window, &width, &height); // 转换Y坐标为OpenGL帧缓冲坐标系 yPos = height - yPos; glPixelStorei(GL_UNPACK_ALIGNMENT, 1); unsigned char data[4]; // 读取当前帧缓冲对应位置的像素 glReadPixels(static_cast<GLint>(xPos), static_cast<GLint>(yPos), 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, data); std::cout << static_cast<float>(data[0]) << " " << static_cast<float>(data[1]) << " " << static_cast<float>(data[2]) << " " << static_cast<float>(data[3]) << std::endl; } } int main() { if (!glfwInit()) { std::cout << "glfw failed to initialise" << std::endl; return -1; } glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4); glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0); glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); GLFWwindow* window = glfwCreateWindow(500, 500, "Opengl Selection", NULL, NULL); if (!window) { std::cout << "Failed to create GLFW window" << std::endl; glfwTerminate(); return -1; } glfwMakeContextCurrent(window); // 仅注册一次鼠标回调 glfwSetMouseButtonCallback(window, mouseButtonCallback); if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) { std::cout << "Failed to initialise glad" << std::endl; return -1; } glClearColor(0.3f, 0.2f, 0.1f, 1.0f); glViewport(0, 0, 500, 500); // 编译顶点着色器 unsigned int vertexShader = glCreateShader(GL_VERTEX_SHADER); glShaderSource(vertexShader, 1, &vertexShaderSource, NULL); glCompileShader(vertexShader); int success; char infoLog[512]; glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success); if (!success) { glGetShaderInfoLog(vertexShader, 512, NULL, infoLog); std::cout << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n" << infoLog << std::endl; } // 编译片段着色器 unsigned int fragmentShader = glCreateShader(GL_FRAGMENT_SHADER); glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL); glCompileShader(fragmentShader); glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success); if (!success) { glGetShaderInfoLog(fragmentShader, 512, NULL, infoLog); std::cout << "ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n" << infoLog << std::endl; } // 链接着色器程序 unsigned int shaderProgram = glCreateProgram(); glAttachShader(shaderProgram, vertexShader); glAttachShader(shaderProgram, fragmentShader); glLinkProgram(shaderProgram); glGetProgramiv(shaderProgram, GL_LINK_STATUS, &success); if (!success) { glGetProgramInfoLog(shaderProgram, 512, NULL, infoLog); std::cout << "ERROR::SHADER::PROGRAM::LINKING_FAILED\n" << infoLog << std::endl; } glDeleteShader(vertexShader); glDeleteShader(fragmentShader); float vertices[] = { // pos // tex 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f }; unsigned int VBO, VAO; glGenVertexArrays(1, &VAO); glGenBuffers(1, &VBO); glBindBuffer(GL_ARRAY_BUFFER, VBO); glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); glBindVertexArray(VAO); glEnableVertexAttribArray(0); glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void*)0); glBindBuffer(GL_ARRAY_BUFFER, 0); glBindVertexArray(0); glUseProgram(shaderProgram); // 投影矩阵与窗口尺寸匹配 glm::mat4 projection = glm::ortho(0.0f, 500.0f, 500.0f, 0.0f, -1.0f, 1.0f); glUniformMatrix4fv(glGetUniformLocation(shaderProgram, "projection"), 1, GL_FALSE, glm::value_ptr(projection)); while (!glfwWindowShouldClose(window)) { glfwPollEvents(); glClear(GL_COLOR_BUFFER_BIT); glUseProgram(shaderProgram); glm::mat4 model = glm::mat4(1.0f); model = glm::translate(model, glm::vec3(0.0f, 0.0f, 0.0f)); model = glm::scale(model, glm::vec3(100.0f, 100.0f, 0.0f)); glUniformMatrix4fv(glGetUniformLocation(shaderProgram, "model"), 1, GL_FALSE, glm::value_ptr(model)); glBindVertexArray(VAO); glDrawArrays(GL_TRIANGLES, 0, 6); glBindVertexArray(0); glfwSwapBuffers(window); } // 清理资源 glDeleteVertexArrays(1, &VAO); glDeleteBuffers(1, &VBO); glDeleteProgram(shaderProgram); glfwTerminate(); return 0; }
相关产品推荐
相关产品推荐

