2012-09-28 22 views
18

He diseñado un programa que, básicamente, corta una forma geométrica en muchos triángulos pequeños (en un "lienzo izquierdo"), aplica una transformación matemática simple al grupo de triángulos y los vuelve a dibujar en su nueva configuración. Ver captura de pantalla a continuación.Qt/C++: dibujo eficientemente

screen cap 1

Para la elaboración de estos triángulos, utilizo QPainter::drawPolygon. Cada triángulo a la derecha corresponde a un triángulo a la izquierda, así que sé qué color quiero usar para dibujarlo.

Hasta ahora, bien. Incluso si dibujo muchos más triángulos que este (cuando uso triángulos mucho más pequeños para cortar la forma), esto es lo suficientemente rápido.

He agregado una característica a mi programa: puedo dibujar triángulos extraídos de una imagen en lugar de triángulos simples: ver la siguiente captura de pantalla.

enter image description here

El problema es que mi forma de hacer esto es demasiado lento. Así es como lo hago:

  1. corro por todos los triángulos
  2. Para cada triángulo, puedo calcular las coordenadas de cada píxel que se mostrará.
  3. Para cada uno de estos píxeles, calculo las coordenadas del píxel correspondiente en la imagen (esta es una operación matemática fácil) y recupero el color de ese píxel.
  4. Yo uso QPainter::setPen(QColor) y QPainter::drawPoint(QPoint) para dibujar el píxel.

Soy nuevo a la programación en Qt y no sé nada de gráficos, así que esto es lo que podría ocurrir. El problema es que es "inaceptablemente" demasiado lento (el paintEvent de cada lienzo ocupa aproximadamente 0,15 segundos, en comparación con 0,01 en el caso de los triángulos sencillos).

me encontré con un perfilador para tratar de entender lo que está pasando, me di cuenta de que en que se gasta de paintEvent,

  1. 58% del tiempo se gasta en QPainter::drawPoint
  2. 27% de las veces el widget de lona en QPainter::setPen

parece que QPainter::drawPoint es demasiado complicado y lento: sólo quiero que para imprimir un píxel de un color determinado, eso es todo.

Pude haber encontrado una solución a mi problema: almacenar QImage (como una variable miembro de mi widget canvas) que representa todo lo que quiero que mi lienzo muestre, y definirlo completamente en mi paintEvent píxel por píxel, y luego dibujarlo de una vez al final de mi paintEvent con QPainter::drawImage. Tengo una pista de que esto será mucho más rápido. Pero antes de volver a escribir mi código, me gustaría saber si eso es realmente lo que quiero hacer.

Espero que no te haya aburrido para hacer la muerte! Muchas gracias de antemano por sus ideas.

+1

¿está dibujando píxel por píxel? (zomg !!) – UmNyobe

Respuesta

4

solución para no OpenGL:

usa un buffer RGB imagen de destino para. Trabaja tus 3 primeros pasos como lo hiciste antes. Una vez que haya encontrado la posición y el color del píxel, configúrelo en este búfer. A continuación, utilice

QImage::QImage (uchar * data, int width, int height, Format format) 

para construir la imagen en función del búfer anterior. Está cerca de la solución que proporcionó y será mucho más rápido que lo que actualmente tiene.

+0

Muchas gracias!Probablemente use esto (hasta que sepa algo sobre OpenGL algún día). – Seub

+2

Confirmo, ¡esto está bien por lo que hago! (paintEvent en max 0.04s) – Seub

7

OpenGL hace la asignación de coordenadas de imagen (textura) muy bien. Probablemente quiera usar alguna forma de OpenGL. Qt tiene algunos enlaces a OpenGL que pueden ayudarte.

+0

+1 De acuerdo, el OP parece estar creando una aplicación de mapas UV, OpenGL es la herramienta perfecta para esto. – cmannett85

+0

¡Gracias por tu respuesta! Definitivamente voy a investigar esto algún día. El problema es que no sé nada sobre OpenGL. – Seub

3

Una forma de hacer esto sería usar una clase heredando de QGLWidget en lugar del combo QGraphicsScene/QGraphicsView. Desafortunadamente, la curva de aprendizaje para OpenGL comienza un poco fuerte. Sin embargo, será muy rápido porque ocurrirá directamente en la tarjeta gráfica que está optimizada para este tipo de operación.
Cargará la imagen QGLWidget::bindTexture().
Asociarás puntos en la imagen con tu malla triangular y los enviarás a tu tarjeta gráfica. En la versión legado de OpenGL (que es más fácil de usar que la nueva API en mi opinión), se vería algo como esto:

glEnable(GL_TEXTURE_2D); 

glBegin(GL_TRIANGLES); 
for (int ii=0;ii<triangle.size();++ii) { 
    for (int jj=0;jj<3;++jj) { 
    glTexCoord2d(triangle[ii].tex[jj][0],triangle[ii].tex[jj][1]); 
    glVertex2d(triangle[ii].point[jj[0],triangle[ii].point[jj][1]); 
    } 
} 
glEnd(); 

Dónde triangle es una estructura de datos que usted ha hecho la celebración de los vértices del triángulo y asignaciones asociadas en la imagen. La tarjeta gráfica manejará la interpolación de píxeles para usted.

+0

¡Muchas gracias por su respuesta! Esto es muy interesante. "Desafortunadamente, la curva de aprendizaje para OpenGL comienza un poco fuerte". ¡Supongo que ese es el problema para mí! Pero probablemente tendré que aprender sobre esto en algún momento. – Seub

+0

Esto usa código obsoleto. Realmente quieres usar shaders y samplers de textura. – doron

+0

@doron Está en desuso. Pero si la funcionalidad de tubería fija hará el trabajo, es mucho más fácil ir por ese camino. GLSL solo aumenta la dificultad de aprender programación 3D. Da más flexibilidad, pero también lo hace el código de ensamblaje. Aún así, hay una razón por la que la mayoría de nosotros no programamos demasiado en ensamblaje. – JCooper

1

Otra opción aparte de OpenGL es usar OpenCL, que puede ser más fácil para usted. Simplemente tiene que asignar un mapa de memoria del mapa de bits de entrada/salida a la tarjeta gráfica, escribir un pequeño kernel en C que maneja un triángulo y luego poner en cola una ejecución del kernel para cada triángulo. Esto funcionará tanto como 100 veces más rápido que un solo núcleo en la CPU.

Hay un envoltorio Qt para Host API OpenCL aquí:

http://doc.qt.digia.com/opencl-snapshot/index.html

0

Un enfoque diferente es aprovechar el recorte y transformaciones ya implementado de manera eficiente dentro del motor de pintura de la trama. Siempre que la transformación entre los dos triángulos se pueda expresar usando una matriz de transformación aumentada de 3x3, solo necesita establecerla en el pintor de destino y luego dibujar toda la imagen de origen en el objetivo. Se recortará y transformará para llenar el triángulo objetivo. También puede dibujar simplemente el rectángulo delimitador del triángulo fuente, en lugar de dibujar toda la imagen, si el perfil muestra una ventaja.

Esto se puede paralelizar para que pueda procesar tantos triángulos en paralelo como núcleos de CPU.