离屏帧缓冲区opengl上的glGetPixels

Posted

技术标签:

【中文标题】离屏帧缓冲区opengl上的glGetPixels【英文标题】:glGetPixels on Offscreen framebuffer opengl 【发布时间】:2018-08-17 07:47:34 【问题描述】:

我在我的程序中生成了一个点云,现在,我希望能够点击这个点云中使用 OpenGL 渲染到我的屏幕上的一个点。

为了做到这一点,我使用了一种技巧,即根据其在 VBO 中的索引为屏幕外渲染中的每个像素赋予一种颜色。我为我的屏幕外渲染和屏幕渲染使用相同的相机,因此它们一起移动,当我单击时,我获取屏幕外渲染的值以检索 VBO 中的位置以获取我单击的点。这是理论,因为当我点击时,我只有 (0,0,0)。我相信这意味着我的 FBO 渲染器不好,但我不确定是不是这样,或者问题是否来自其他地方......

所以这里是步骤。 clicFBO 是我用于屏幕外渲染的 FBO,clicTextureColorBuf 是我在 FBO 中写入的纹理

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 中的索引颜色...

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);

和顶点着色器:

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;

还有片段:

#version 330 core
layout(location = 0) out vec4 FragColor;
in float Col;
void main()

    FragColor = vec4(Col, Col, Col ,1.0);

这就是我渲染这个纹理的方式:

    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);

然后,当我点击时,我使用这段代码:

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,我称之为pickmap。我用谷歌搜索了一下,找到了这个教程:Picking with an OpenGL hack。顺便提一句。与此同时,我们踢了这项技术。因为太慢了。 (相反,我们在 CPU 端进行交叉测试。)很有趣 - 教程也提到了这个弱点:它可能会引入明显的帧率下降是的,我可以确认这一点。 嗨!谢谢!我会看看那个!要进行交叉测试,我应该使用八叉树等创建一个数据结构。由于我做了很多计算并且我只想查看数据,因此 FPS 下降实际上不会让我烦恼。再次感谢! 在我们的例子中,我们将三角形汤分成组(主体)。数据量是可以调用的东西。 (CPU 无法提供像 GPU 那样的大量并行化。)因此,大多数时候需要某种分支和边界。如果数据存储在经典场景图中(带有边界球),则可以利用这一点。在您的情况下,八叉树可能是一种足够的方法。剩下的就是 3d 数学。当您使用glm 时,大多数需要的功能可能已经存在或可以构建在上面。 我已经为另一个项目使用 glm 构建了一个八叉树。第二种方法是使用glUnproject 2次,在Unprojected这2个点之间创建一条射线,然后用八叉树看看哪个点离这条射线更近,对吧?由于我认为我现在使用的方法非常接近实现,所以我想完成。如果我真的找不到方法,我会做另一个(如果我有太多问题,我会尝试新方法)。但是现在,我会坚持解决我的错误^^' 关于光线投射,对于点我做的有点不同:我根据一个平截头体检查点(平截头体像视锥体)。优点:我可以使用相同的测试来挑选和选择橡皮筋。对于拾取,我定义了一个相当小的平截头体来考虑一个常数(距离无关)点大小。 (这与我使用glPointSize() 的渲染完美匹配。)顺便说一句。从视锥体和 l、r、t、b 的一些额外缩放(通常用于定义视锥体的 6 个参数中的 4 个)定义这个选取视锥体非常容易,即我不需要glUnproject() 【参考方案1】:

使用帧缓冲区对象FBO 来存储“对象标识符”是一种很酷的方法。而且也想看对象吧?然后您还必须渲染到默认帧缓冲区(让我称之为“defFB”,它不是 FBO)。

因为您需要渲染到两个不同的目标,所以您需要以下技术之一:

绘制对象两次(例如,使用两次 glDrawArrays 调用),一次到 FBO,第二次到 defFB。 一次绘制到两个 FBO 的图像,然后将其中一个(使用颜色)blit 到 defFB。

对于第一种技术,您可以使用附加到 FBO 的纹理(就像您目前所做的那样)。或者您可以使用“渲染缓冲区”并绘制到它。

第二种方法需要在片段着色器中进行第二次“输出”:

layout(location = 0) out vec3 color; //GL_COLOR_ATTACHMENT0
layout(location = 1) out vec3 objID; //GL_COLOR_ATTACHMENT1

并使用glDrawBuffers 设置两个附件。

对于 blit 部分,请阅读this answer。

请注意,两个“out”具有相同的格式,在此示例中为 vec3

代码中的失败是您设置了RGB 纹理格式并在glReadPixels 使用此格式,但您在FS 中的“输出”是vec4 而不是@ 987654331@.

更多的担忧是:

用glCheckFramebufferStatus检查完整性 可能需要对 FBO 使用“深度附件”,即使它不会用于读取。 禁用深度测试将放置所有元素,如果框架。您的点选将选择最后绘制的,而不是最近的。

【讨论】:

嗯,谢谢您的回答,但实际上,我在默认帧缓冲区上渲染。我可以有效地看到我想看到的东西,而我的问题只发生在离屏渲染上。我已经有了很好的渲染,我想在这个功能代码中添加我可以点击一个点的事实。 -我检查了帧缓冲区状态,我没有错误 -我在我的 FBO 中添加了一个深度附件 -我试图禁用深度测试 -我将 vec4 更改为 vec3 但没有任何改变 :( 非常感谢!【参考方案2】:

我发现了问题。 我的代码中有 2 次失败: 第一个是在 OpenGL 中,图像和帧缓冲区之间存在 Y 反转。所以为了选择好的点,你必须使用视口的大小来翻转 Y:我是这样做的:

GLint m_viewport[4];
glGetIntegerv(GL_VIEWPORT, m_viewport);
int YposTMP = m_viewport[3] - Ypos - 1;

第二个是使用 glReadPixels(Xpos - 2, Ypos - 2, width, height, GL_RGB, GL_UNSIGNED_BYTE, &amp;pixels[0]);,第 6 个参数必须是 GL_FLOAT,因为我返回的数据是浮点数。

谢谢大家! 最好的问候, R.S

【讨论】:

以上是关于离屏帧缓冲区opengl上的glGetPixels的主要内容,如果未能解决你的问题,请参考以下文章

如何在opengl es 3+中获取我的离屏帧缓冲区内的底层对象的像素颜色?

opengl 渲染方式在屏 离屏 CPU

离屏渲染,OpenGL的三种渲染方式

opengl Framebuffer离屏到纹理,颜色错误

离屏渲染学习笔记

帧缓冲区存储哪些状态?