在 nVidia GPU 上使用 OpenGL 4.5。我需要生成一个具有 32 位整数像素的图像,但我得到的是浮点值。
我正在使用以内部格式 GL_R32I 创建的渲染缓冲区。这被记录为非规范化整数格式; OGL 运行时报告它完全支持写入和读取。
OpenGL 将读取缓冲区内部格式报告为 GL_RED_INTEGER,将像素类型报告为 GL_INT。我可以通过在 glClearColor() 中将一个 int 转换为浮点数来在渲染缓冲区中放置不同的整数值,并且 glReadPixels() 完整地返回这些位模式。
为了测试,我将缓冲区初始化为 -1。片段着色器为每个三角形输出一个整数,但输出图像包含等效的浮点值。
代码如下:
/* render a triangle index image
* vertex coords on pixel grid
* origin upper left
* pixel value is triangle id
*/
static const char* index_vs_text =
"#version 450\n"
"uniform mat4 MVP;\n"
"layout(location = 0) in vec2 vPos;\n"
"layout(location = 1) in int Tdx;\n"
"out flat int tid;\n"
"void main()\n"
"{\n"
" gl_Position = MVP * vec4(vPos, -1.0, 1.0);\n"
" tid = Tdx;\n"
"}\n";
static const char* index_fs_text =
"#version 450\n"
"layout(origin_upper_left) in vec4 gl_FragCoord;\n"
"in flat int tid;\n"
"out unsigned int color;\n"
"void main()\n"
"{\n"
" color = tid;\n"
"}\n";
bool GLops::tridx_image(
int width,
int height,
int* pixels, // result array
int ntris, // number of triangles
int* pvtids, // vertex indices, 3 per tri
int nverts, // number of vertices
float* verts // 2D vertex coordinates
) {
if (window == 0)
return false;
/* convert input to canonical arrays that can be
rendered with glDrawArrays:
* vpos 3 {x, y} coords per triangle (y inverted)
* vtid triangle id 3 times per tri
*/
int nv = 3 * ntris,
nc = 2 * nv;
vector<float> vvpos(nc);
vector<int> vvtid(nv);
float* vpos = vvpos.data(),
* pos = vpos;
int* vtid = vvtid.data(),
* pid = vtid,
* pdx = pvtids;
for (int itri = 0; itri < ntris; itri++, pid += 3, pdx += 3 ) {
pid[0] = pid[1] = pid[2] = itri;
for (int i = 0; i < 3; i++, pos += 2) {
int j = 2 * pdx[i];
pos[0] = verts[j++];
pos[1] = verts[j];
}
}
/* model/view/projection matrix */
glm::mat4 mvp = glm::ortho(
0.f, float(width),
0.f, float(height),
0.5f, 1.5f
);
GLuint vao, fbo, rbo,
vertex_buffer, tid_buffer,
vertex_shader, fragment_shader, program;
GLint mvp_loc, vpos_loc, tid_loc;
glfwMakeContextCurrent(window);
gladLoadGL(glfwGetProcAddress);
vertex_shader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertex_shader, 1, &index_vs_text, NULL);
glCompileShader(vertex_shader);
if (compile_error(vertex_shader, "tridx vs"))
return false;
fragment_shader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragment_shader, 1, &index_fs_text, NULL);
glCompileShader(fragment_shader);
if(compile_error(fragment_shader, "tridx fs"))
return false;
program = glCreateProgram();
glAttachShader(program, vertex_shader);
glAttachShader(program, fragment_shader);
glBindFragDataLocation(program, 0, "fragcolor");
glLinkProgram(program);
if (program_error(program, "tridx"))
return false;
mvp_loc = glGetUniformLocation(program, "MVP");
vpos_loc = glGetAttribLocation(program, "vPos");
tid_loc = glGetAttribLocation(program, "Tdx");
glUseProgram(program);
glUniformMatrix4fv(mvp_loc, 1, GL_FALSE, (const GLfloat*)&mvp);
if (ogl_error("tridx link"))
return false;
if (pixels == 0)
return false;
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);
glGenBuffers(1, &vertex_buffer);
glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer);
glBufferData(GL_ARRAY_BUFFER, nv * 2 * sizeof(float), vpos, GL_STATIC_DRAW);
glEnableVertexAttribArray(vpos_loc);
glVertexAttribPointer(vpos_loc, 2, GL_FLOAT, GL_FALSE, 0, 0);
glGenBuffers(1, &tid_buffer);
glBindBuffer(GL_ARRAY_BUFFER, tid_buffer);
glBufferData(GL_ARRAY_BUFFER, nv * sizeof(int), vtid, GL_STATIC_DRAW);
glEnableVertexAttribArray(tid_loc);
glVertexAttribPointer(tid_loc, 1, GL_INT, GL_FALSE, 0, 0);
glGenFramebuffers(1, &fbo);
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
GLenum RBfmt = GL_R32I;
glGenRenderbuffers(1, &rbo);
glBindRenderbuffer(GL_RENDERBUFFER, rbo);
glRenderbufferStorage(
GL_RENDERBUFFER,
RBfmt,
width,
height
);
glFramebufferRenderbuffer(
GL_FRAMEBUFFER,
GL_COLOR_ATTACHMENT0,
GL_RENDERBUFFER,
rbo
);
if (ogl_error("tridx buffers"))
return false;
/* check validity of renderbuffer format */
GLint RBparms[4] = { 0,0,0,0 };
GLenum supp_full = GL_FULL_SUPPORT,
supp_caveat = GL_CAVEAT_SUPPORT,
supp_none = GL_NONE;
glGetInternalformativ(
GL_RENDERBUFFER,
RBfmt,
GL_INTERNALFORMAT_SUPPORTED,
4,
RBparms
);
glGetInternalformativ(
GL_RENDERBUFFER,
RBfmt,
GL_COLOR_RENDERABLE,
3,
RBparms + 1
);
glGetInternalformativ(
GL_RENDERBUFFER,
RBfmt,
GL_FRAMEBUFFER_RENDERABLE,
2,
RBparms + 2
);
glGetInternalformativ(
GL_RENDERBUFFER,
RBfmt,
GL_READ_PIXELS,
1,
RBparms + 3
);
GLenum fbs = glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER);
if (fbs != GL_FRAMEBUFFER_COMPLETE)
return false;
glViewport(0, 0, width, height);
int bg = -1;
glClearColor(*(float*)&bg, 0.f, 0.f, 0.f);
glClear(GL_COLOR_BUFFER_BIT);
glDisable(GL_DEPTH_TEST);
glDisable(GL_CULL_FACE);
glDisable(GL_SCISSOR_TEST);
glDisable(GL_STENCIL_TEST);
glDrawArrays(
GL_TRIANGLES, 0, nv
);
glFinish();
if(ogl_error("tridx render"))
return false;
GLint
rdfmt, // gets GL_RED or GL_RED_INTEGER
rdtyp; // gets GL_FLOAT or GL_UNSIGNED_INT
glGetFramebufferParameteriv(
GL_READ_FRAMEBUFFER,
GL_IMPLEMENTATION_COLOR_READ_FORMAT,
&rdfmt
);
glGetFramebufferParameteriv(
GL_READ_FRAMEBUFFER,
GL_IMPLEMENTATION_COLOR_READ_TYPE,
&rdtyp
);
glReadPixels(0, 0, width, height, rdfmt, rdtyp, pixels);
if (ogl_error("tridx read"))
return false;
return true;
}