OpenGL:从FrameBuffer读取像素以进行取整,最大舍入为255(0xFF)

OpenGL: Read pixels from FrameBuffer for picking rounded up to 255 (0xFF)

我正在尝试通过将vao id打包到RGBA并将其渲染到屏幕外的缓冲区中来实现对象选择,然后尝试使用缓冲区对象进行读取。

我通过创建纹理和z缓冲区RenderBuffer对象,然后将它们附加到FrameBuffer对象,来渲染到屏幕外缓冲区:

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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
/* create a framebuffer object */
glGenFramebuffers(1, &fbo);    
/* attach the texture and the render buffer to the frame buffer */
glBindFramebuffer(GL_FRAMEBUFFER, fbo);

/* generate a texture id */
glGenTextures(1, &tex);
/* bind the texture */
glBindTexture(GL_TEXTURE_2D, tex);
/* create the texture in the GPU */
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, WINDOW_SIZE_X, WINDOW_SIZE_Y
    , 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);

/* set texture parameters */
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);

/* unbind the texture */
glBindTexture(GL_TEXTURE_2D, 0);

/* create a renderbuffer object for the depth buffer */
glGenRenderbuffers(1, &rbo);
/* bind the texture */
glBindRenderbuffer(GL_RENDERBUFFER, rbo);
/* create the render buffer in the GPU */
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT
    , WINDOW_SIZE_X, WINDOW_SIZE_Y);

/* unbind the render buffer */
glBindRenderbuffer(GL_RENDERBUFFER, 0);


/* attach the texture and the render buffer to the frame buffer */
glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, tex, 0);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT
    , GL_RENDERBUFFER, rbo);

// check the frame buffer
if (glCheckFramebufferStatus(
    GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
        std::cout <<"Framebuffer status not complete" << '\
';
}
/* handle an error : frame buffer incomplete */
/* return to the default frame buffer */
glBindFramebuffer(GL_FRAMEBUFFER, 0);

渲染完成后,我还生成了一个像素缓冲区对象以从FrameBuffer中读取:

1
2
3
4
5
6
    /* generate the pixel buffer object */
glGenBuffers(1,&pbo);    
glBindBuffer(GL_PIXEL_PACK_BUFFER, pbo);
glBufferData(GL_PIXEL_PACK_BUFFER, WINDOW_SIZE_X * WINDOW_SIZE_Y * 4, nullptr, GL_STREAM_READ);
/* to avoid weird behaviour the first frame the data is loaded */
glReadPixels(0, 0, WINDOW_SIZE_X, WINDOW_SIZE_Y, GL_BGRA, GL_UNSIGNED_BYTE, 0);

然后在我的渲染循环中,将其绑定并渲染到该屏幕外的FrameBuffer:

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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
    GLubyte red, green, blue, alpha;

/* bind the frame buffer */
glBindFramebuffer(GL_FRAMEBUFFER, fbo);

/* clear the frame buffer */
glClearColor(0,0,0,0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

/* select the shader program */
glUseProgram(pickProgram);
/* set the object color */

/*alpha = house.vaoId & 0xFF;
blue = (house.vaoId >> 8) & 0xFF;
green = (house.vaoId >> 16) & 0xFF;
red = (house.vaoId >> 24) & 0xFF; */

GLuint objectId = 5;
alpha = objectId & 0xFF;
blue  = (objectId >> 8) & 0xFF;
green = (objectId >> 16) & 0xFF;
red   = (objectId >> 24) & 0xFF;

//Upload the packed RGBA values to the shader  
glUniform4f(baseColorUniformLocation, red, green ,blue, alpha);    

    //prepare to draw the object
pvm = projectionMatrix*viewMatrix*house.modelMatrix;
glUniformMatrix4fv(offScreenMatrixUniformLocation, 1, GL_FALSE, glm::value_ptr(pvm));

/* draw the object*/
glBindVertexArray(house.getVaoId());
glDrawRangeElements(GL_TRIANGLES,0,42,42,GL_UNSIGNED_SHORT,NULL);
glBindVertexArray(0);

//check that our framebuffer is ok
if(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
std::cout <<"Framebuffer Error" << '\
';
}

GLuint temp;
    //get the object id from the read pixels
temp = get_object_id(); <--- this function is explained later


/* return to the default frame buffer */
glBindFramebuffer(GL_FRAMEBUFFER, 0);

渲染到屏幕外缓冲区的片段着色器非常简单:

1
2
3
4
5
6
7
8
#version 420
uniform vec4 BaseColor;
layout(location=0) out vec4 fragColor;

void main()
{
 fragColor = BaseColor;
}

这是在屏幕外渲染期间调用的函数,用于从帧缓冲区中提取打包的RGBA:

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
26
27
28
29
30
31
32
33
34
35
36
GLuint Engine::get_object_id()
{
static int frame_event = 0;
GLuint object_id;
int x, y;
GLuint red, green, blue, alpha, pixel_index;
//GLuint read_pbo, map_pbo;
GLubyte* ptr;

/* read one pixel buffer */
glBindBuffer(GL_PIXEL_PACK_BUFFER, pbo_a);
/* map the other pixel buffer */  
    ///////////////////// NOTE :5th argument, BGRA or RGBA, doesn't make a difference right?
glReadPixels(0, 0, WINDOW_SIZE_X, WINDOW_SIZE_Y, GL_BGRA, GL_UNSIGNED_BYTE, 0);
ptr = (GLubyte*)glMapBuffer(GL_PIXEL_PACK_BUFFER, GL_READ_WRITE);
/* get the mouse coordinates */
/* OpenGL has the {0,0} at the down-left corner of the screen */
glfwGetMousePos(&x, &y);
y = WINDOW_SIZE_Y - y;
object_id = -1;
if (x >= 0 && x < WINDOW_SIZE_X && y >= 0 && y < WINDOW_SIZE_Y){
//////////////////////////////////////////
    //I have to admit I don't understand what he does here
///////////////////////////////////////////
    pixel_index = (x + y * WINDOW_SIZE_X) * 4;
    blue = ptr[pixel_index];
    green = ptr[pixel_index + 1];
    red = ptr[pixel_index + 2];
    alpha = ptr[pixel_index + 3];

object_id = alpha +(red << 24) + (green << 16) + (blue << 8);
}
glUnmapBuffer(GL_PIXEL_PACK_BUFFER);
glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
return object_id;
}

问题在于,本应从屏幕外的帧缓冲区读取像素并给我vao_id的最后一段代码具有以下怪异行为:

如果打包到RGBA中的4个字节中的任何一个(通过着色器发送)不是0,则该字节在另一端显示为0xFF。

所以如果我发送

1
00000000 00000000 00000000 00000001

我会得到

1
00000000 00000000 00000000 11111111

或者如果我发送

1
00000000 00010000 00000000 00000000

我会得到

1
00000000 11111111 00000000 00000000

...当我使用get_object_id()读取像素时。

如果将纹理绑定并渲染到普通FrameBuffer上的四边形上,则传递给屏幕外渲染的颜色在四边形上会正确显示。但是,由get_object_id()读取的像素会使发送的每个字节都向上舍入为255(0xFF)。因此,我的猜测是最终功能存在问题。


片段着色器输出的[0,1]范围内的值在写入帧缓冲区时会重新映射为[0,255]。 glUniform4f接受浮点值,您需要将ID作为[0,1]值而不是[0,255]值发送:

1
glUniform4f(baseColorUniformLocation, red / 255.0f, green / 255.0f...);