texture texturas proyectos multiples example c++ c qt opengl image-scaling

c++ - texturas - texture mapping opengl



Escalado de imagen(KeepAspectRatioByExpanding) a través de OpenGL (2)

Simplemente puede copiar la rama "mantener relación de aspecto" (siempre que esté funcionando) y simplemente voltear el signo de comparación de proporción, es decir:

if (ratiox > ratioy)

se convierte

if (ratiox <= ratioy)

Pero no estoy seguro de que realmente esté funcionando (los cálculos de proporción siempre me han molestado, y el tuyo es complicado), y no tengo Qt atm, así que no puedo intentarlo. Pero eso debería hacerlo. Tenga en cuenta que la imagen estará centrada (no alineada a la izquierda como en su imagen), pero se puede arreglar con bastante facilidad.

EDITAR :

Aquí está el código fuente que funciona en la aplicación GLUT (no QT, lo siento):

static void DrawObject(void) { int img_width = 1280;//_frame->width(); int img_height = 720;//_frame->height(); GLfloat offset_x = -1; GLfloat offset_y = -1; int p_viewport[4]; glGetIntegerv(GL_VIEWPORT, p_viewport); // don''t have QT :''( GLfloat gl_width = p_viewport[2];//width(); // GL context size GLfloat gl_height = p_viewport[3];//height(); int n_mode = 0; switch(n_mode) { case 0: // KeepAspectRatioByExpanding { float ratioImg = float(img_width) / img_height; float ratioScreen = gl_width / gl_height; if(ratioImg < ratioScreen) { gl_width = 2; gl_height = 2 * ratioScreen / ratioImg; } else { gl_height = 2; gl_width = 2 / ratioScreen * ratioImg; } // calculate image size } break; case 1: // IgnoreAspectRatio gl_width = 2; gl_height = 2; // OpenGL normalized coordinates are -1 to +1 .. hence width (or height) = +1 - (-1) = 2 break; case 2: // KeepAspectRatio { float ratioImg = float(img_width) / img_height; float ratioScreen = gl_width / gl_height; if(ratioImg > ratioScreen) { gl_width = 2; gl_height = 2 * ratioScreen / ratioImg; } else { gl_height = 2; gl_width = 2 / ratioScreen * ratioImg; } // calculate image size offset_x = -1 + (2 - gl_width) * .5f; offset_y = -1 + (2 - gl_height) * .5f; // center on screen } break; } glMatrixMode(GL_PROJECTION); glLoadIdentity(); glMatrixMode( GL_MODELVIEW ); glLoadIdentity(); // just simple ortho view, no fancy transform ... glBegin(GL_QUADS); glTexCoord2f(0, 0); glVertex2f(offset_x, offset_y); glTexCoord2f(ImgWidth, 0); glVertex2f(offset_x + gl_width, offset_y); glTexCoord2f(ImgWidth, ImgHeight); glVertex2f(offset_x + gl_width, offset_y + gl_height); glTexCoord2f(0, ImgHeight); glVertex2f(offset_x, offset_y + gl_height); glEnd(); // draw a single quad }

Esto funciona comparando la relación de aspecto de la pantalla con la relación de aspecto de la imagen . En realidad está comparando las proporciones del ancho de la imagen con el ancho de la pantalla con la altura de la imagen a la altura de la pantalla. Eso es sospechoso al menos, por no decir mal.

Además, las coordenadas de OpenGL normalizadas (siempre que se trate de una vista ortogonal simple) están en el rango (-1, -1) para la esquina inferior izquierda a (1, 1) para la esquina superior derecha. Eso significa que el ancho y la altura normalizados son ambos 2, y el desplazamiento es (-1, -1). El resto del código debe ser autoexplicativo. En caso de que la textura se voltee (probé con una especie de textura genérica, no estoy seguro si estaba en posición vertical), simplemente cambie las coordenadas de la textura en la dirección respectiva (intercambie 0 para ImgWidth (o altura) y viceversa).

EDIT2 :

Usar coordenadas de píxeles (sin usar coordenadas OpenGL normalizadas) es aún más simple. Puedes usar:

static void DrawObject(void) { int img_width = 1280;//_frame->width(); int img_height = 720;//_frame->height(); GLfloat offset_x = 0; GLfloat offset_y = 0; int p_viewport[4]; glGetIntegerv(GL_VIEWPORT, p_viewport); GLfloat gl_width = p_viewport[2];//width(); // GL context size GLfloat gl_height = p_viewport[3];//height(); int n_mode = 0; switch(n_mode) { case 0: // KeepAspectRatioByExpanding { float ratioImg = float(img_width) / img_height; float ratioScreen = gl_width / gl_height; if(ratioImg < ratioScreen) gl_height = gl_width / ratioImg; else gl_width = gl_height * ratioImg; // calculate image size } break; case 1: // IgnoreAspectRatio break; case 2: // KeepAspectRatio { float ratioImg = float(img_width) / img_height; float ratioScreen = gl_width / gl_height; GLfloat orig_width = gl_width; GLfloat orig_height = gl_height; // remember those to be able to center the quad on screen if(ratioImg > ratioScreen) gl_height = gl_width / ratioImg; else gl_width = gl_height * ratioImg; // calculate image size offset_x = 0 + (orig_width - gl_width) * .5f; offset_y = 0 + (orig_height - gl_height) * .5f; // center on screen } break; } glMatrixMode(GL_PROJECTION); glLoadIdentity(); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glTranslatef(-1, -1, 0); glScalef(2.0f / p_viewport[2], 2.0f / p_viewport[3], 1.0); // just simple ortho view for vertex coordinate to pixel matching glBegin(GL_QUADS); glTexCoord2f(0, 0); glVertex2f(offset_x, offset_y); glTexCoord2f(img_width, 0); glVertex2f(offset_x + gl_width, offset_y); glTexCoord2f(img_width, img_height); glVertex2f(offset_x + gl_width, offset_y + gl_height); glTexCoord2f(0, img_height); glVertex2f(offset_x, offset_y + gl_height); glEnd(); // draw a single quad }

Tenga en cuenta que ambas versiones del código usan texturas NPOT. Para adaptar el código a su objeto, uno haría algo como esto:

void GLWidget::paintEvent(QPaintEvent *event) { QPainter painter(this); painter.setRenderHint(QPainter::Antialiasing); qDebug() << "> GLWidget::paintEvent OpenGL:" << ((painter.paintEngine()->type() != QPaintEngine::OpenGL && painter.paintEngine()->type() != QPaintEngine::OpenGL2) ? "disabled" : "enabled"); QGLContext* context = const_cast<QGLContext *>(QGLContext::currentContext()); if (!context) { qDebug() << "> GLWidget::paintEvent !!! Unable to retrieve OGL context"; return; } context->makeCurrent(); painter.fillRect(QRectF(QPoint(0, 0), QSize(1280, 768)), Qt::black); painter.beginNativePainting(); /* Initialize GL extensions */ GLenum err = glewInit(); if (err != GLEW_OK) { qDebug() << "> GLWidget::paintEvent !!! glewInit failed with: " << err; return; } if (!GLEW_VERSION_2_1) // check that the machine supports the 2.1 API. { qDebug() << "> GLWidget::paintEvent !!! System doesn''t support GLEW_VERSION_2_1"; return; } /* Setting up texture and transfering data to the GPU */ static GLuint texture = 0; if (texture != 0) { context->deleteTexture(texture); } glPixelStorei(GL_UNPACK_ALIGNMENT, 1); glBindTexture(GL_TEXTURE_RECTANGLE_ARB, texture); glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, GL_LUMINANCE, _frame->width(), _frame->height() + _frame->height() / 2, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, _frame->bits()); assert(glGetError() == GL_NO_ERROR); glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); glEnable(GL_TEXTURE_RECTANGLE_ARB); glClearColor(0.3, 0.3, 0.4, 1.0); /* Initialize shaders and execute them */ _init_shaders(); int img_width = _frame->width(); int img_height = _frame->height(); GLfloat offset_x = 0; GLfloat offset_y = 0; GLfloat gl_width = width(); // GL context size GLfloat gl_height = height(); qDebug() << "paint(): gl_width:" << gl_width << " gl_height:" << gl_height << " img:" << _frame->width() << "x" << _frame->height(); int fill_mode = 0; switch(fill_mode) { case 0: // KeepAspectRatioByExpanding { float ratioImg = float(img_width) / img_height; float ratioScreen = gl_width / gl_height; if(ratioImg < ratioScreen) gl_height = gl_width / ratioImg; else gl_width = gl_height * ratioImg; // calculate image size } break; case 1: // IgnoreAspectRatio break; case 2: // KeepAspectRatio { float ratioImg = float(img_width) / img_height; float ratioScreen = gl_width / gl_height; GLfloat orig_width = gl_width; GLfloat orig_height = gl_height; // remember those to be able to center the quad on screen if(ratioImg > ratioScreen) gl_height = gl_width / ratioImg; else gl_width = gl_height * ratioImg; // calculate image size offset_x = 0 + (orig_width - gl_width) * .5f; offset_y = 0 + (orig_height - gl_height) * .5f; // center on screen } break; } glDisable(GL_CULL_FACE); // might cause problems if enabled glBegin(GL_QUADS); glTexCoord2f(0, 0); glVertex2f(offset_x, offset_y); glTexCoord2f(img_width, 0); glVertex2f(offset_x + gl_width, offset_y); glTexCoord2f(img_width, img_height); glVertex2f(offset_x + gl_width, offset_y + gl_height); glTexCoord2f(0, img_height); glVertex2f(offset_x, offset_y + gl_height); glEnd(); // draw a single quad painter.endNativePainting(); }

No puedo garantizar que este último fragmento de código no contenga errores, ya que no tengo QT. Pero en caso de que haya errores tipográficos, debería ser bastante sencillo solucionarlos.

Estoy tratando de implementar la escala de la imagen en OpenGL usando solo glTexCoord2f() y glVertex2f() .

Permítanme explicarles: después de cargar un QImage y enviarlo al gpu con glTexImage2D() tengo que realizar operaciones de escalado de imagen basadas en la especificación de Qt . Qt define estas 3 operaciones (ver imagen a continuación):

Creo que esta es la única forma de hacerlo, ya que mi aplicación es un complemento Qt y esta tarea debe hacerse dentro del método paint() de la clase. La operación IgnoreAspectRatio es bastante directa y está funcionando en este momento. El KeepAspectRatio me dio algunos problemas inicialmente, pero ahora también está funcionando. Desafortunadamente, KeepAspectRatioByExpanding me está dando dolores de cabeza .

Estoy compartiendo lo que hice hasta ahora y agradezco su ayuda en este tema:

main.cpp:

#include "oglWindow.h" #include <QtGui/QApplication> int main(int argc, char *argv[]) { QApplication a(argc, argv); oglWindow w; w.show(); return a.exec(); }

oglWindow.cpp:

#include "oglWindow.h" #include "glwidget.h" #include <QGridLayout> oglWindow::oglWindow(QWidget *parent, Qt::WFlags flags) : QMainWindow(parent, flags) { ui.setupUi(this); GLWidget *openGL = new GLWidget(this); QGridLayout *layout = new QGridLayout; setLayout(layout); } oglWindow::~oglWindow() { }

oglWindow.h:

#ifndef oglWindow_H #define oglWindow_H #include <QtGui/QMainWindow> #include "ui_yuv_to_rgb.h" class oglWindow : public QMainWindow { Q_OBJECT public: oglWindow(QWidget *parent = 0, Qt::WFlags flags = 0); ~oglWindow(); private: Ui::oglWindowClass ui; }; #endif // oglWindow_H

glwidget.cpp:

#ifdef _MSC_VER #include <windows.h> #include <GL/glew.h> #include <GL/gl.h> #else #include <GL/gl.h> #endif #include "glwidget.h" #include <QDebug> #include <iostream> #include <fstream> #include <assert.h> static const char *p_s_fragment_shader = "#extension GL_ARB_texture_rectangle : enable/n" "uniform sampler2DRect tex;" "uniform float ImgHeight, chromaHeight_Half, chromaWidth;" "void main()" "{" " vec2 t = gl_TexCoord[0].xy;" // get texcoord from fixed-function pipeline " float CbY = ImgHeight + floor(t.y / 4.0);" " float CrY = ImgHeight + chromaHeight_Half + floor(t.y / 4.0);" " float CbCrX = floor(t.x / 2.0) + chromaWidth * floor(mod(t.y, 2.0));" " float Cb = texture2DRect(tex, vec2(CbCrX, CbY)).x - .5;" " float Cr = texture2DRect(tex, vec2(CbCrX, CrY)).x - .5;" " float y = texture2DRect(tex, t).x;" // redundant texture read optimized away by texture cache " float r = y + 1.28033 * Cr;" " float g = y - .21482 * Cb - .38059 * Cr;" " float b = y + 2.12798 * Cb;" " gl_FragColor = vec4(r, g, b, 1.0);" "}"; GLWidget::GLWidget(QWidget *parent) : QGLWidget(QGLFormat(QGL::SampleBuffers), parent), _frame(NULL) { setAutoFillBackground(false); setMinimumSize(640, 480); /* Load 1280x768 YV12 frame from the disk */ _frame = new QImage(1280, 768, QImage::Format_RGB888); if (!_frame) { qDebug() << "> GLWidget::GLWidget !!! Failed to create _frame"; return; } std::ifstream yuv_file("bloco.yv12", std::ios::in | std::ios::binary | std::ios::ate); if (!yuv_file.is_open()) { qDebug() << "> GLWidget::GLWidget !!! Failed to load yuv file"; return; } int yuv_file_sz = yuv_file.tellg(); unsigned char* memblock = new unsigned char[yuv_file_sz]; if (!memblock) { qDebug() << "> GLWidget::GLWidget !!! Failed to allocate memblock"; return; } yuv_file.seekg(0, std::ios::beg); yuv_file.read((char*)memblock, yuv_file_sz); yuv_file.close(); qMemCopy(_frame->scanLine(0), memblock, yuv_file_sz); delete[] memblock; } GLWidget::~GLWidget() { if (_frame) delete _frame; } void GLWidget::paintEvent(QPaintEvent *event) { QPainter painter(this); painter.setRenderHint(QPainter::Antialiasing); qDebug() << "> GLWidget::paintEvent OpenGL:" << ((painter.paintEngine()->type() != QPaintEngine::OpenGL && painter.paintEngine()->type() != QPaintEngine::OpenGL2) ? "disabled" : "enabled"); QGLContext* context = const_cast<QGLContext *>(QGLContext::currentContext()); if (!context) { qDebug() << "> GLWidget::paintEvent !!! Unable to retrieve OGL context"; return; } context->makeCurrent(); painter.fillRect(QRectF(QPoint(0, 0), QSize(1280, 768)), Qt::black); painter.beginNativePainting(); /* Initialize GL extensions */ GLenum err = glewInit(); if (err != GLEW_OK) { qDebug() << "> GLWidget::paintEvent !!! glewInit failed with: " << err; return; } if (!GLEW_VERSION_2_1) // check that the machine supports the 2.1 API. { qDebug() << "> GLWidget::paintEvent !!! System doesn''t support GLEW_VERSION_2_1"; return; } /* Setting up texture and transfering data to the GPU */ static GLuint texture = 0; if (texture != 0) { context->deleteTexture(texture); } glPixelStorei(GL_UNPACK_ALIGNMENT, 1); glBindTexture(GL_TEXTURE_RECTANGLE_ARB, texture); glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, GL_LUMINANCE, _frame->width(), _frame->height() + _frame->height() / 2, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, _frame->bits()); assert(glGetError() == GL_NO_ERROR); glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); glEnable(GL_TEXTURE_RECTANGLE_ARB); glClearColor(0.3, 0.3, 0.4, 1.0); int img_width = _frame->width(); int img_height = _frame->height(); int offset_x = 0; int offset_y = 0; GLfloat gl_width = width(); // GL context size GLfloat gl_height = height(); /* Initialize shaders and execute them */ _init_shaders(); qDebug() << "paint(): gl_width:" << gl_width << " gl_height:" << gl_height << " img:" << _frame->width() << "x" << _frame->height(); int fill_mode = 1; switch (fill_mode) { case 0: // KeepAspectRatioByExpanding { // need help! } break; case 1: // IgnoreAspectRatio { // Nothing special needs to be done for this operation. } break; case 2: // KeepAspectRatio default: { // Compute aspect ratio and offset Y for widescreen borders double ratiox = img_width/gl_width; double ratioy = img_height/gl_height; if (ratiox > ratioy) { gl_height = qRound(img_height / ratiox); offset_y = qRound((height() - gl_height) / 2); gl_height += offset_y * 2; } else { gl_width = qRound(img_width / ratioy); offset_x = qRound((width() - gl_width) / 2); gl_width += offset_x * 2; } } break; } // Mirroring texture coordinates to flip the image vertically glBegin(GL_QUADS); glTexCoord2f(0.0f, img_height); glVertex2f(offset_x, gl_height - offset_y); glTexCoord2f(img_width, img_height); glVertex2f(gl_width - offset_x, gl_height - offset_y); glTexCoord2f(img_width, 0.0f); glVertex2f(gl_width - offset_x, offset_y); glTexCoord2f(0.0f, 0.0f); glVertex2f(offset_x, offset_y); glEnd(); painter.endNativePainting(); } void GLWidget::_init_shaders() { int f = glCreateShader(GL_FRAGMENT_SHADER); glShaderSource(f, 1, &p_s_fragment_shader, 0); glCompileShader(f); _shader_program = glCreateProgram(); glAttachShader(_shader_program, f); glLinkProgram(_shader_program); glUseProgram(_shader_program); glUniform1i(glGetUniformLocation(_shader_program, "tex"), 0); glUniform1f(glGetUniformLocation(_shader_program, "ImgHeight"), _frame->height()); glUniform1f(glGetUniformLocation(_shader_program, "chromaHeight_Half"), (_frame->height() / 2) / 2); glUniform1f(glGetUniformLocation(_shader_program, "chromaWidth"), _frame->width() / 2); }

glwidget.h :

#include <QtOpenGL/QGLWidget> #include <QtGui/QImage> #include <QPainter> class GLWidget : public QGLWidget { Q_OBJECT public: GLWidget(QWidget *parent = 0); ~GLWidget(); void paintEvent(QPaintEvent *event); private: void _init_shaders(); bool _checkShader(int n_shader_object); QImage* _frame; int _shader_program; };

Y aquí puedes descargar el archivo de datos .


Simplemente haz las mismas operaciones matemáticas que hiciste con KeepAspectRatio, pero esta vez mantén la altura en lugar del ancho. Obtendrás un ancho mayor que 1, por lo que tendrás que usar el inverso para la coordenada de tu textura.

Las coordenadas normalizadas sí / no no importan, solo tiene que agregar estos factores para las coordenadas de la textura. Para que esto funcione, supongo que la imagen llena toda la textura (de (0,0) a (1,1), lo que facilita la multiplicación de los valores con su ancho / alto). El envoltorio de textura tiene que estar apagado.

Coordenadas de textura predeterminadas:

(0,0)-(1,0) | | (0,1)-(1,1)

Proporciones de aspecto:

tex_ar = tex_width / tex_height; // aspect ratio for the texture being used scr_ar = scr_width / scr_height; // aspect ratio for the quad being drawn

KeepAspectRatio (toque desde el interior):

(0, 0)-----------------------(max(1, scr_ar / tex_ar), 0) | | (0, max(1, tex_ar / scr_ar))-(max(1, scr_ / tex_ar), max(1, tex_ar / scr_ar))

KeepAspectRatioByExpanding (toque desde el exterior; simplemente reemplace max() con min() ):

(0, 0)-----------------------(min(1, scr_ar / tex_ar), 0) | | (0, min(1, tex_ar / scr_ar))-(min(1, scr_ / tex_ar), min(1, tex_ar / scr_ar))

Para su caso, simplemente tendría que multiplicar las coordenadas de textura resultantes con su ancho / alto.