glGetPixels on Offscreen framebuffer opengl
我在程序中生成了一个PointCloud,现在,我希望能够单击使用OpenGL渲染到屏幕上的该点云中的一个点。
为此,我使用了一种技巧,即根据VBO中的索引为屏幕外的每个像素提供一种颜色。我为屏幕外渲染和屏幕上渲染使用同一台摄像机,以便它们一起移动,单击时,我会获得屏幕外渲染的值,以检索VBO中的位置来获得单击的点。这是理论,因为当我单击时,我只有(0,0,0)。我认为这意味着我的FBO渲染效果不佳,但是我不确定是不是这样,还是问题出在其他地方...
所以这是步骤。
1 2 3 4 5 6 7 8 9 10 | glGenFramebuffers(1, &clicFBO); glBindFramebuffer(GL_FRAMEBUFFER, clicFBO); glGenTextures(1, &clicTextureColorBuf); glBindTexture(GL_TEXTURE_2D, clicTextureColorBuf); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, SCR_WIDTH, SCR_HEIGHT, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, clicTextureColorBuf, 0); GLenum DrawBuffers[1] = { GL_COLOR_ATTACHMENT0 }; glDrawBuffers(1, DrawBuffers); |
之后,我编写了一个着色器,为每个点提供了VBO中索引的颜色。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | std::vector<cv::Point3f> reconstruction3D; //Will contain the position of my points std::vector<float> indicesPointsVBO; //Will contain the indexes of each point for (int i = 0; i < pts3d.size(); ++i) { reconstruction3D.push_back(pts3d[i].pt3d); colors3D.push_back(pt_tmp); indicesPointsVBO.push_back(((float)i / (float)pts3d.size() )); } GLuint clicVAO, clicVBO[2]; glGenVertexArrays(1, &clicVAO); glGenBuffers(2, &clicVBO[0]); glBindVertexArray(clicVAO); glBindBuffer(GL_ARRAY_BUFFER, clicVBO[0]); glBufferData(GL_ARRAY_BUFFER, reconstruction3D.size() * sizeof(cv::Point3f), &reconstruction3D[0], GL_STATIC_DRAW); glEnableVertexAttribArray(0); glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (GLvoid*)0); glEnable(GL_PROGRAM_POINT_SIZE); glBindBuffer(GL_ARRAY_BUFFER, clicVBO[1]); glBufferData(GL_ARRAY_BUFFER, indicesPointsVBO.size() * sizeof(float), &indicesPointsVBO[0], GL_STATIC_DRAW); glEnableVertexAttribArray(1); glVertexAttribPointer(1, 1, GL_FLOAT, GL_FALSE, 0, (GLvoid*)0); glBindBuffer(GL_ARRAY_BUFFER, 0); glBindVertexArray(0); glBindFramebuffer(GL_FRAMEBUFFER, 0); |
和顶点着色器:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | layout (location = 0) in vec3 pos; layout (location = 1) in float col; out float Col; uniform mat4 model; uniform mat4 view; uniform mat4 projection; uniform int pointSize; void main() { gl_PointSize = pointSize; gl_Position = projection * view * model * vec4(pos, 1.0); Col = col; } |
和片段:
1 2 3 4 5 6 7 | #version 330 core layout(location = 0) out vec4 FragColor; in float Col; void main() { FragColor = vec4(Col, Col, Col ,1.0); } |
这就是我渲染此纹理的方式:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | glm::mat4 view = camera.GetViewMatrix(); glm::mat4 projection = glm::perspective(glm::radians(camera.Zoom), (float)SCR_WIDTH / (float)SCR_HEIGHT, 1.0f, 100.0f); glBindFramebuffer(GL_FRAMEBUFFER, clicFBO); clicShader.use(); glDisable(GL_DEPTH_TEST); glClearColor(1.0f, 1.0f, 1.0f, 1.0f); glClear(GL_COLOR_BUFFER_BIT); clicShader.setMat4("projection", projection); clicShader.setMat4("view", view); model = glm::mat4(); clicShader.setMat4("model", model); clicShader.setInt("pointSize", pointSize); glBindVertexArray(clicVAO); glDrawArrays(GL_POINTS, 0, (GLsizei)reconstruction3D.size()); glBindFramebuffer(GL_FRAMEBUFFER, 0); |
然后,当我单击时,我使用这段代码:
1 2 3 4 5 6 7 8 9 10 11 | glBindFramebuffer(GL_FRAMEBUFFER, clicFBO); glReadBuffer(GL_COLOR_ATTACHMENT0); int width = 11, height = 11; std::array<GLfloat, 363> arry{ 1 }; glReadPixels(Xpos - 5, Ypos - 5, width, height, GL_RGB, GL_UNSIGNED_BYTE, &arry); for (int i = 0; i < 363; i+=3) { // It's 3 time the same number anyways for each number std::cout << arry[i] <<""; // It gives me only 0's } std::cout << std::endl << std::endl; glBindFramebuffer(GL_FRAMEBUFFER, clicFBO); |
我知道该错误可能确实很愚蠢,但是OpenGL的工作方式仍然存在一些问题。
我提出了我认为对理解该问题所必需的内容(无需过多扩展),但是如果您需要更多代码,我也可以编写。
我知道这不是一个可以说"是"或"否"的问题,它更像是调试程序,但是由于我真的找不到问题的根源,因此我正在寻找可以向我解释什么的人我做错了。我并不一定要寻求解决方案本身,而是可以帮助我了解错误在哪里的线索...
使用帧缓冲区对象FBO存储"对象标识符"是一种很酷的方法。而且还想看物体吧?然后,您还必须渲染到默认的帧缓冲区(我称其为" defFB",它不是FBO)。
因为需要渲染到两个不同的目标,所以需要以下技术之一:
-
绘制两次对象(例如两次调用
glDrawArrays ),一次到FBO,另一次到defFB。 - 一次绘制两个FBO的图像,然后将其中一个(用颜色)涂抹到defFB。
对于第一种技术,您可以使用附着在FBO上的纹理(如您目前所做的那样)。或者,您可以使用"渲染缓冲区"并对其进行绘制。
第二种方法在片段着色器中需要第二个"输出":
1 2 | layout(location = 0) out vec3 color; //GL_COLOR_ATTACHMENT0 layout(location = 1) out vec3 objID; //GL_COLOR_ATTACHMENT1 |
并使用
对于阴暗的部分,请阅读此答案。
请注意,两个"输出"具有相同的格式,在此示例中为
代码失败的原因是您设置了
更令人担忧的是:
- 使用glCheckFramebufferStatus检查完整性
- 可能需要在FBO上使用"深度附件",即使它不会被用于阅读。
- 禁用深度测试会将所有元素置于框架中。您的选点将选择最后绘制的,而不是最近的。
我发现了问题。
我的代码中有2个失败:
第一个是在OpenGL中,图像和帧缓冲区之间存在Y反转。因此,为了选择要点,您必须使用视口的大小来翻转Y:我这样做是这样的:
1 2 3 | GLint m_viewport[4]; glGetIntegerv(GL_VIEWPORT, m_viewport); int YposTMP = m_viewport[3] - Ypos - 1; |
第二个是使用
谢谢大家!
最好的祝福,
R.S