简体   繁体   English

屏幕外帧缓冲区opengl上的glGetPixels

[英]glGetPixels on Offscreen framebuffer opengl

I generate a PointCloud in my program, and now, I want to be able to click on a point in this point cloud rendered to my screen using OpenGL. 我在程序中生成了一个PointCloud,现在,我希望能够单击使用OpenGL渲染到屏幕上的该点云中的一个点。

In order to do so, I used the trick of giving to each pixel in an offscreen render a colour based on its index in the VBO. 为此,我使用了一种技巧,即根据VBO中的索引为屏幕外的每个像素提供一种颜色。 I use the same camera for my offscreen render and my onscreen render so they move together, and when I click, I get values of my offscreen render to retrieve the position in the VBO to get the point I clicked on. 我为屏幕外渲染和屏幕上渲染使用同一台摄像机,以便它们一起移动,单击时,我会获得屏幕外渲染的值,以检索VBO中的位置来获得单击的点。 This is the theory since when I click, I have only (0,0,0). 这是理论,因为当我单击时,我只有(0,0,0)。 I believe that means my FBO is not well renderer but I'm not sure whether it is that or if the problem comes from somewhere else... 我认为这意味着我的FBO渲染效果不佳,但是我不确定是不是这样,还是问题出在其他地方...

So here are the steps. 所以这是步骤。 clicFBO is the FBO I'm using for offscreen render, and clicTextureColorBuf is the texture in which I write in the 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);

After that, I wrote a shader that gives to each point the color of its index in the VBO... 之后,我编写了一个着色器,为每个点提供了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);

and the vertex shader: 和顶点着色器:

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

And the Fragment: 和片段:

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

And this is how I render this texture: 这就是我渲染此纹理的方式:

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

And then, when I click, I Use this piece of Code: 然后,当我单击时,我使用这段代码:

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

I know the error might be really stupid but I still have some problems with how OpenGL works. 我知道该错误可能确实很愚蠢,但是OpenGL的工作方式仍然存在一些问题。

I put what I thought was necessary to understand the problem (without extending too much), but if you need more code, I can write it too. 我提出了我认为对理解该问题所必需的内容(无需过多扩展),但是如果您需要更多代码,我也可以编写。

I know this is not a question in which you can say Yes or No and it's more like debugging my program, but since I really don't find from where the problem comes from, I'm looking toward someone who can explain to me what I did wrong. 我知道这不是一个可以说“是”或“否”的问题,它更像是调试程序,但是由于我真的找不到问题的根源,因此我正在寻找可以向我解释什么的人我做错了。 I do not necessarily seek the solution itself, but clues that could help me understand where my error is ... 我并不一定要寻求解决方案本身,而是可以帮助我了解错误在哪里的线索...

Using a framebuffer object FBO to store a "object identifier" is a cool method. 使用帧缓冲区对象FBO存储“对象标识符”是一种很酷的方法。 But also want to see the objects, right? 而且还想看物体吧? Then you must render also to the default frame buffer (let me call it "defFB", which is not a FBO). 然后,您还必须渲染到默认的帧缓冲区(我称其为“ defFB”,它不是FBO)。

Because you need to render to two different targets, you need one of these techniques: 因为需要渲染到两个不同的目标,所以需要以下技术之一:

  • Draw objects twice (eg with two glDrawArrays calls), one to the FBO and a second one to the defFB. 绘制对象两次(例如,使用两次glDrawArrays调用),一次绘制到FBO,第二次绘制到defFB。
  • Draw to two FBO's images at once and later blit one of then (with colors) to the defFB. 一次绘制两个FBO的图像,然后将其中一个(用颜色)涂抹到defFB。

For the first technique you may use a texture attached to a FBO (as you currently do). 对于第一种技术,您可以使用附着在FBO上的纹理(如您目前所做的那样)。 Or you can use a "Renderbuffer" and draw to it. 或者,您可以使用“渲染缓冲区”并对其进行绘制。

The second approach needs a second "out" in the fragment shader: 第二种方法在片段着色器中需要第二个“输出”:

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

and setting the two attachments with glDrawBuffers . 并使用glDrawBuffers设置两个附件。

For the blit part, read this answer . 对于阴暗的部分,请阅读此答案

Note that both "out" have the same format, vec3 in this example. 请注意,两个“输出”具有相同的格式,在此示例中为vec3

A fail in your code is that you set a RGB texture format and also use this format at glReadPixels , but your "out" in the FS is vec4 instead of vec3 . 代码中的失败是你设置一个RGB纹理格式,还可以使用这个格式glReadPixels ,但你的“走出去”的FS是vec4代替vec3

More concerns are: 更令人担忧的是:

  • Check the completeness with glCheckFramebufferStatus 使用glCheckFramebufferStatus检查完整性
  • Using a "depth attachment" to the FBO may be needed, even it will not be used for reading. 可能需要在FBO上使用“深度附件”,即使它不会被用于阅读。
  • Disabling the depth test will put all elements if the frame. 禁用深度测试会将所有元素置于框架中。 Your point-picking will select the last drawn, not the nearest. 您的选点将选择最后绘制的,而不是最近的。

I found the problem. 我发现了问题。
There were 2 failures in my code : 我的代码中有2个失败:
The first one is that in OpenGL, there is an Y inversion between the image and the framebuffer. 第一个是在OpenGL中,图像和帧缓冲区之间存在Y反转。 So in order to pick the good point, you have to flip Y using the size of the viewport : I did it like this : 因此,为了选择要点,您必须使用视口的大小来翻转Y:我这样做是这样的:

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

The second one is the use of glReadPixels(Xpos - 2, Ypos - 2, width, height, GL_RGB, GL_UNSIGNED_BYTE, &pixels[0]); 第二个是使用glReadPixels(Xpos - 2, Ypos - 2, width, height, GL_RGB, GL_UNSIGNED_BYTE, &pixels[0]); , the 6th parameter must be GL_FLOAT since the datas i'm returning are float. ,第6个参数必须为GL_FLOAT因为我要返回的数据是浮点型的。

Thanks all! 谢谢大家!
Best regards, 最好的祝福,
RS RS

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM