Рендеринг OpenGL в текстуру выглядит неровно

Я делаю программу с OpenGL, которая отображает кадры в графическом процессоре, которые я затем переношу в память, чтобы использовать их в другой программе. Мне не нужно окно или рендер на экран, поэтому я использую GLFW, но со скрытым окном и контекстом. После opengl-tutorial.com я настроил Framebuffer с текстурой и рендерингом глубины, чтобы я мог рендерить текстуру и затем читать ее пиксели. Просто чтобы проверить вещи, я могу сделать окно видимым, и я рендерил текстуру обратно на экран на четырехугольнике и использую сквозной шейдер.

Моя проблема в том, что когда я рендерим на экран напрямую (без Framebuffer или текстуры), изображение выглядит великолепно и гладко. Однако, когда я рендеринг в текстуру, а затем рендеринг текстуры на экран, это выглядит неровно Я не думаю, что проблема заключается в рендеринге текстуры на экран, потому что я также сохраняю пиксели, которые я прочитал, в формате .jpg, и там это тоже выглядит неровно.

Окно и текстура имеют размер 512×512 пикселей.

Вот код, где я настроил фреймбуфер:

FramebufferName = 0;
glGenFramebuffers(1, &FramebufferName);
glBindFramebuffer(GL_FRAMEBUFFER, FramebufferName);

//GLuint renderedTexture;
glGenTextures(1, &renderedTexture);
glBindTexture(GL_TEXTURE_2D, renderedTexture);
glTexImage2D(GL_TEXTURE_2D, 0, textureFormat, textureWidth, textureHeight, 0, textureFormat, GL_UNSIGNED_BYTE, 0);

numBytes = textureWidth * textureHeight * 3; // RGB
pixels = new unsigned char[numBytes]; // allocate image data into RAM

glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

//GLuint depthrenderbuffer;
glGenRenderbuffers(1, &depthrenderbuffer);
glBindRenderbuffer(GL_RENDERBUFFER, depthrenderbuffer);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, textureWidth, textureHeight);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depthrenderbuffer);

glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, renderedTexture, 0);

DrawBuffers[0] = GL_COLOR_ATTACHMENT0;
glDrawBuffers(1, DrawBuffers); // "1" is the size of DrawBuffers

if(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
std::cout << "Couldn't set up frame buffer" << std::endl;
}

g_quad_vertex_buffer_data.push_back(-1.0f);
g_quad_vertex_buffer_data.push_back(-1.0f);
g_quad_vertex_buffer_data.push_back(0.0f);

g_quad_vertex_buffer_data.push_back(1.0f);
g_quad_vertex_buffer_data.push_back(-1.0f);
g_quad_vertex_buffer_data.push_back(0.0f);

g_quad_vertex_buffer_data.push_back(-1.0f);
g_quad_vertex_buffer_data.push_back(1.0f);
g_quad_vertex_buffer_data.push_back(0.0f);

g_quad_vertex_buffer_data.push_back(-1.0f);
g_quad_vertex_buffer_data.push_back(1.0f);
g_quad_vertex_buffer_data.push_back(0.0f);

g_quad_vertex_buffer_data.push_back(1.0f);
g_quad_vertex_buffer_data.push_back(-1.0f);
g_quad_vertex_buffer_data.push_back(0.0f);

g_quad_vertex_buffer_data.push_back(1.0f);
g_quad_vertex_buffer_data.push_back(1.0f);
g_quad_vertex_buffer_data.push_back(0.0f);

//GLuint quad_vertexbuffer;
glGenBuffers(1, &quad_vertexbuffer);
glBindBuffer(GL_ARRAY_BUFFER, quad_vertexbuffer);
glBufferData(GL_ARRAY_BUFFER, g_quad_vertex_buffer_data.size() * sizeof(GLfloat), &g_quad_vertex_buffer_data[0], GL_STATIC_DRAW);

// PBOs
glGenBuffers(cantPBOs, pboIds);
for(int i = 0; i < cantPBOs; ++i) {
glBindBuffer(GL_PIXEL_PACK_BUFFER, pboIds[i]);
glBufferData(GL_PIXEL_PACK_BUFFER, numBytes, 0, GL_DYNAMIC_READ);
}
glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);

index = 0;
nextIndex = 0;

Вот код, где я отрисовываю текстуру:

glBindFramebuffer(GL_FRAMEBUFFER, FramebufferName);
glViewport(0,0,textureWidth,textureHeight); // Render on the whole framebuffer, complete from the lower left corner to the upper right

// Clear the screen
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

for(int i = 0; i < geometriesToDraw.size(); ++i) {
geometriesToDraw[i]->draw(program);
}

Где draw (ShaderProgram) — это функция, которая вызывает glDrawArrays. И вот код, где я рендерил текстуру на экран:

// Render to the screen
glBindFramebuffer(GL_FRAMEBUFFER, 0);
// Render on the whole framebuffer, complete from the lower left corner to the upper right
glViewport(0,0,textureWidth,textureHeight);

// Clear the screen
glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glUseProgram(shaderTexToScreen.getProgramID());

// Bind our texture in Texture Unit 0
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, renderedTexture);
// Set our "renderedTexture" sampler to user Texture Unit 0
glUniform1i(shaderTexToScreen.getUniformLocation("renderedTexture"), 0);

// 1rst attribute buffer : vertices
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, quad_vertexbuffer);
glVertexAttribPointer(
0,
3,
GL_FLOAT,
GL_FALSE,
0,
(void*)0
);

glDrawArrays(GL_TRIANGLES, 0, 6);
glDisableVertexAttribArray(0);

Вот что я получаю, когда визуализирую сцену напрямую:

непосредственный

И вот что я получаю при рендеринге сцены в текстуру:

текстура

Я могу включить код для вершинных и фрагментных шейдеров, используемых при рендеринге текстуры на экран, но так как я читаю данные пикселей прямо из текстуры и записываю их в файл, и это все еще выглядит неровно, я не думаю, что это эта проблема. Если есть что-то еще, что вы хотите, чтобы я включил, дайте мне знать!

Я подумал, может быть, это может быть из-за некоторого скрытого масштабирования при рендеринге текстуры, и поэтому GL_NEAREST делает его плохим, но если это действительно пиксель в пиксель (и окна, и текстура имеют одинаковый размер), то не должно быть проблема там правильно?

1

Решение

Как указывают genpfault и Frischer Hering, при рендеринге на обычную текстуру сглаживание отсутствует. Тем не менее, вы можете выполнить рендеринг в текстуру Multisample, которая будет содержать информацию для столько образцов, сколько вы запросите. Чтобы отобразить это на экране, вам нужно сэмплировать текстуру, чтобы получить один цвет для каждого пикселя, что можно сделать, вызвав glBlitFramebuffer. Согласно ссылке OpenGL на glBlitFramebuffer:

Если SAMPLE_BUFFERS для буфера кадров чтения больше нуля, а SAMPLE_BUFFERS для буфера кадров рисования равен нулю, выборки, соответствующие каждому местоположению пикселя в источнике, перед записью в место назначения преобразуются в одну выборку.

Эти две ссылки тоже были очень полезны:

http://www.learnopengl.com/#!Advanced-OpenGL/Anti-aliasing
http://ake.in.th/2013/04/02/offscreening-and-multisampling-with-opengl/

Вот мое решение, создание объектов:

/// FRAMEBUFFER MULTISAMPLE

framebufferMS = 0;
glGenFramebuffers(1, &framebufferMS);
glBindFramebuffer(GL_FRAMEBUFFER, framebufferMS);

glGenTextures(1, &renderedTextureMS);
glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, renderedTextureMS);
glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, SAMPLES, textureFormat, textureWidth, textureHeight, GL_TRUE);

glGenRenderbuffers(1, &depthrenderbufferMS);
glBindRenderbuffer(GL_RENDERBUFFER, depthrenderbufferMS);
glRenderbufferStorageMultisample(GL_RENDERBUFFER, SAMPLES, GL_DEPTH24_STENCIL8, textureWidth, textureHeight);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depthrenderbufferMS);

glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, renderedTextureMS, 0);
DrawBuffersMS[0] = GL_COLOR_ATTACHMENT0;
glDrawBuffers(1, DrawBuffersMS);

if(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
std::cout << "Couldn't set up frame buffer" << std::endl;
}

/// FRAMEBUFFER SIMPLE

framebuffer = 0;
glGenFramebuffers(1, &framebuffer);
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);

glGenTextures(1, &renderedTexture);
glBindTexture(GL_TEXTURE_2D, renderedTexture);
glTexImage2D(GL_TEXTURE_2D, 0, textureFormat, textureWidth, textureHeight, 0, textureFormat, GL_UNSIGNED_BYTE, 0);

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, renderedTexture, 0);
DrawBuffers[0] = GL_COLOR_ATTACHMENT0;
glDrawBuffers(1, DrawBuffers); // "1" is the size of DrawBuffers

if(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
std::cout << "Couldn't set up frame buffer" << std::endl;
}

И процесс рендеринга:

// Render to framebuffer multisample
glBindFramebuffer(GL_FRAMEBUFFER, framebufferMS);
glViewport(0,0,textureWidth,textureHeight);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

for(int i = 0; i < geometriesToDraw.size(); ++i) {
geometriesToDraw[i]->draw(program);
}

// Sample to normal texture
glBindFramebuffer(GL_READ_FRAMEBUFFER, framebufferMS);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, framebuffer);
glBlitFramebuffer(0, 0, textureWidth, textureHeight, 0, 0, textureWidth, textureHeight, GL_COLOR_BUFFER_BIT, GL_NEAREST);

Вы также можете найти больше вопросов по этому вопросу здесь на stackoverflow, который я пропустил, потому что я ищу такие термины, как «зубчатые» вместо мультисэмплинга: P

Большое спасибо за вашу помощь!

1

Другие решения

Других решений пока нет …

По вопросам рекламы ammmcru@yandex.ru
Adblock
detector