Визуализация OpenGL в текстуру выглядит неровной

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

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

И окно, и текстура имеют размер 512x512 пикселей.

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

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);

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

direct

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

текстура

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

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


person aspirino67    schedule 03.11.2016    source источник
comment
В вашем драйвере графического процессора принудительно включена множественная выборка?   -  person genpfault    schedule 03.11.2016
comment
Это настройки в панели управления Nvidia imgur.com/UYnAh8i.   -  person aspirino67    schedule 03.11.2016
comment
Хорошо, тогда, наверное, пришло время для минимального воспроизводимого примера.   -  person genpfault    schedule 03.11.2016
comment
Рендеринг выглядит отлично. Как уже намекнул genpfault: решение RTT вообще не имеет сглаживания. См. Конец этого руководства: opengl-tutorial. org / intermediate-tutorials /   -  person Frischer Hering    schedule 03.11.2016
comment
Вы совершенно правы! Спасибо что подметил это. Я настраиваю два фреймбуфера, один с мультисэмплингом, в котором я рендерю сцену, а затем я переходю от одного к другому, который преобразует различные сэмплы в один. Затем я могу визуализировать текстуру, прикрепленную ко второму фреймбуферу, на экран, и она выглядит гладкой. Я напишу полный ответ с примерами кода, как только смогу.   -  person aspirino67    schedule 03.11.2016


Ответы (1)


Как указали genpfault и Фришер Херинг, при рендеринге с нормальной текстурой сглаживание отсутствует. Однако вы можете выполнить рендеринг в текстуру 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

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

person aspirino67    schedule 08.11.2016