I'm using glm::frustum to do large screenshots, and it works quite well, except the SSAO shader leaves seams where the frustums meet up.
What is the recommended way of doing large screenshots in OpenGL 4?
The relevant code is:
void uv_camera::Set_Large_Screenshot(size_t num_cams, size_t cam_index_x, size_t cam_index_y, const int width_px, const int height_px)
{
win_x = width_px;
win_y = height_px;
// No guarantees about the behaviour of this functionality. It wasn't tested a lot.
const float deg_to_rad = (1.0f / 360.0f) * 2.0f * glm::pi<float>();
float aspect = float(win_x) / float(win_y);
float tangent = tanf((fov / 2.0f) * deg_to_rad);
float height = near_plane * tangent; // Half height of near_plane plane.
float width = height * aspect; // Half width of near_plane plane.
float cam_width = 2 * width / num_cams;
float cam_height = 2 * height / num_cams;
float left = -width + cam_index_x * cam_width;
float right = -width + (cam_index_x + 1) * cam_width;
float bottom = -height + cam_index_y * cam_height;
float top = -height + (cam_index_y + 1) * cam_height;
model_mat = glm::mat4(1.0f);
projection_mat = frustum(
left,
right,
bottom,
top,
near_plane,
far_plane);
view_mat = lookAt(
eye,
look_at,
up
);
}
…
void take_screenshot(size_t num_cams_wide, const char* filename, const bool reverse_rows = false)
{
screenshot_mode = true;
// Set up Targa TGA image data.
unsigned char idlength = 0;
unsigned char colourmaptype = 0;
unsigned char datatypecode = 2;
unsigned short int colourmaporigin = 0;
unsigned short int colourmaplength = 0;
unsigned char colourmapdepth = 0;
unsigned short int x_origin = 0;
unsigned short int y_origin = 0;
cout << "Image size: " << static_cast<size_t>(win_x) * num_cams_wide << "x" << static_cast<size_t>(win_y) * num_cams_wide << " pixels" << endl;
if (static_cast<size_t>(win_x) * num_cams_wide > static_cast<unsigned short>(-1) ||
static_cast<size_t>(win_y) * num_cams_wide > static_cast<unsigned short>(-1))
{
cout << "Image too large. Maximum width and height is " << static_cast<unsigned short>(-1) << endl;
return;
}
unsigned short int px = win_x * static_cast<unsigned short>(num_cams_wide);
unsigned short int py = win_y * static_cast<unsigned short>(num_cams_wide);
unsigned char bitsperpixel = 24;
unsigned char imagedescriptor = 0;
vector<char> idstring;
size_t num_bytes = 3 * px * py;
vector<unsigned char> pixel_data(num_bytes);
vector<unsigned char> fbpixels(3 * win_x * win_y);
const size_t total_cams = num_cams_wide * num_cams_wide;
size_t cam_count = 0;
// Loop through subcameras.
for (size_t cam_num_x = 0; cam_num_x < num_cams_wide; cam_num_x++)
{
for (size_t cam_num_y = 0; cam_num_y < num_cams_wide; cam_num_y++)
{
cout << "Camera: " << cam_count + 1 << " of " << total_cams << endl;
// Set up camera, draw, then copy the frame buffer.
main_camera.Set_Large_Screenshot(num_cams_wide, cam_num_x, cam_num_y, win_x, win_y);
display_func();
glReadPixels(0, 0, win_x, win_y, GL_RGB, GL_UNSIGNED_BYTE, &fbpixels[0]);
// Copy pixels to large image.
for (GLint i = 0; i < win_x; i++)
{
for (GLint j = 0; j < win_y; j++)
{
size_t fb_index = 3 * (j * win_x + i);
size_t screenshot_x = cam_num_x * win_x + i;
size_t screenshot_y = cam_num_y * win_y + j;
size_t screenshot_index = 3 * (screenshot_y * (win_x * num_cams_wide) + screenshot_x);
pixel_data[screenshot_index] = fbpixels[fb_index + 2];
pixel_data[screenshot_index + 1] = fbpixels[fb_index + 1];
pixel_data[screenshot_index + 2] = fbpixels[fb_index];
}
}
cam_count++;
}
}
screenshot_mode = false;
main_camera.calculate_camera_matrices(win_x, win_y);
// Write Targa TGA file to disk.
ofstream out(filename, ios::binary);
if (!out.is_open())
{
cout << "Failed to open TGA file for writing: " << filename << endl;
return;
}
out.write(reinterpret_cast<char*>(&idlength), 1);
out.write(reinterpret_cast<char*>(&colourmaptype), 1);
out.write(reinterpret_cast<char*>(&datatypecode), 1);
out.write(reinterpret_cast<char*>(&colourmaporigin), 2);
out.write(reinterpret_cast<char*>(&colourmaplength), 2);
out.write(reinterpret_cast<char*>(&colourmapdepth), 1);
out.write(reinterpret_cast<char*>(&x_origin), 2);
out.write(reinterpret_cast<char*>(&y_origin), 2);
out.write(reinterpret_cast<char*>(&px), 2);
out.write(reinterpret_cast<char*>(&py), 2);
out.write(reinterpret_cast<char*>(&bitsperpixel), 1);
out.write(reinterpret_cast<char*>(&imagedescriptor), 1);
out.write(reinterpret_cast<char*>(&pixel_data[0]), num_bytes);
}
…
void display_func(void)
{
glEnable(GL_DEPTH_TEST);
if (false == screenshot_mode)
main_camera.calculate_camera_matrices(win_x, win_y);
glUseProgram(render.get_program());
const GLfloat background_colour[] = { 1.0f, 0.5f, 0.0f, 0.0f };
static const GLfloat one = 1.0f;
static const GLenum draw_buffers[] = { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1 };
glBindFramebuffer(GL_FRAMEBUFFER, render_fbo);
glEnable(GL_DEPTH_TEST);
glClearBufferfv(GL_COLOR, 0, background_colour);
glClearBufferfv(GL_COLOR, 1, background_colour);
glClearBufferfv(GL_DEPTH, 0, &one);
glBindBufferBase(GL_UNIFORM_BUFFER, 0, points_buffer);
draw_axis();
draw_mesh();
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glUseProgram(ssao.get_program());
glUniform1f(uniforms.ssao.ssao_radius, ssao_radius * float(win_x) / 1000.0f);
glUniform1f(uniforms.ssao.ssao_level, show_ao ? (show_shading ? 0.3f : 1.0f) : 0.0f);
glUniform1i(uniforms.ssao.weight_by_angle, weight_by_angle ? 1 : 0);
glUniform1i(uniforms.ssao.randomize_points, randomize_points ? 1 : 0);
glUniform1ui(uniforms.ssao.point_count, point_count);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, fbo_textures[0]);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, fbo_textures[1]);
glGenVertexArrays(1, &quad_vao);
glDisable(GL_DEPTH_TEST);
glBindVertexArray(quad_vao);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
glDeleteVertexArrays(1, &quad_vao);
glFlush();
if (false == screenshot_mode)
{
glutSwapBuffers();
}
}