c++ - 屏幕外帧缓冲区 opengl 上的 glGetPixels

标签 c++ opengl

我在我的程序中生成了一个 PointCloud,现在,我希望能够单击使用 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存储“对象标识符”是一种很酷的方法。而且还想看对象对不对?然后您还必须渲染到默认帧缓冲区(让我称之为“defFB”,它不是 FBO)。

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

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

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

第二种方法需要片段着色器中的第二个“输出”:

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 中使用了这个格式,但是你的“out”在 FS 中是 vec4 而不是 vec3

更多的担忧是:

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

关于c++ - 屏幕外帧缓冲区 opengl 上的 glGetPixels,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51890637/

相关文章:

c++ - 直接访问硬盘?

c++运算符字符串和char *导致歧义错误

python - numpy 数组的 glDrawPixels 未显示

c++ - memcpy() 给我段错误

c++ - 暴露复杂的短暂对象的正确方法

c++通过系统在shell中运行curl

opengl - 如何将递归数据结构发送到 OpenGL 着色器?

OpenGL GLSL SSAO 实现

c++ - 项目分布

c++ - 为什么打印一个变量会改变它的值?