2012-09-28 7 views
18

Temel olarak, çok küçük üçgenlere (sol tuvalde) geometrik bir şekli kesen, üçgen demetine basit matematiksel dönüşümü uygulayan ve bunları yeniden çizen bir program tasarladım. yeni yapılandırmaları. Aşağıda ekran yakalama konusuna bakın. Bu üçgenleri çizmek içinQt/C++: verimli çizim

screen cap 1

, ben QPainter::drawPolygon kullanın. Sağdaki her üçgen soldaki bir üçgene karşılık gelir, bu yüzden çizmek için hangi rengi kullanmak istediğimi biliyorum.

Şimdiye kadar, iyi. Bundan çok daha fazla üçgen çizsem bile (şekli kesmek için çok daha küçük üçgenler kullandığımda), bu yeterince hızlıdır.

Programıma bir özellik ekledim: Düz üçgenler yerine bir resimden çıkarılan üçgenler çizebilirim: bkz. Aşağıdaki ekran görüntüsü.

enter image description here

sorun Bunu yol çok yavaş olmasıdır. İşte bunun için gereken adımları:

Bütün üçgenler her üçgen için
  • kadar devam
    1. , ben görüntülenecektir her piksel koordinatlarını hesaplamak.
    2. Bu piksellerin her biri için, resimdeki ilgili pikselin koordinatlarını hesaplıyorum (bu kolay bir matematiksel işlemdir) ve bu pikselin rengini alıyorum.
    3. Piksel çizmek için QPainter::setPen(QColor) ve QPainter::drawPoint(QPoint) kullanıyorum.

    Qt'de programlama konusunda yeniyim ve grafikler hakkında hiçbir şey bilmiyorum, bu yüzden gelebildim. Sorun şu ki, "kabul edilemez bir şekilde" çok yavaştır (her bir kanvasın paintEvent'u düz üçgenler durumunda 0.01'lere kıyasla yaklaşık 0.15 saniye sürer).

    ben neler olduğunu anlamaya çalışmak bir profil oluşturucu koştu

    , ben tuval Widget en paintEvent, zamanın

    1. 58% zamanın QPainter::drawPoint
    2. 27% olarak harcanmaktadır harcandığını içinde fark QPainter::setPen

    yılında o QPainter::drawPoint çok karmaşık ve yavaş olduğunu görünüyor: sadece belirli bir rengin bir pikseli yazdırmak istediğiniz, o kadar.

    Sorunumda bir çözüm bulmuş olabilirsiniz: QImage (tuval widget'ımın bir üye değişkeni olarak), tuvalimin görüntülenmesini istediğim her şeyi temsil eder ve onu tamamen paintEvent pikselimde piksel olarak tanımlayabilirim, ve QPainter::drawImage ile paintEvent'un sonunda bir kerede çizin. Bunun çok daha hızlı olacağına dair bir ipucum var. Ama kodumu tekrar tekrar yazmadan önce, gerçekten yapmak istediğim şeyin olup olmadığını bilmek istiyorum.

    Umarım ölüm yapmak zorunda kalmazdım! Anlayışlarınız için şimdiden çok teşekkürler.

  • +1

    Piksel piksel çizimiyorsunuz ?? (zomg !!) – UmNyobe

    cevap

    4

    Olmayan OpenGL çözeltisi:

    hedef resim için bir RGB tampon kullanın. Daha önce yaptığınız gibi ilk 3 adımda çalışın. Konumu ve piksel rengini bulduğunuzda, bu arabelleğe ayarladınız. Daha sonra, görüntüyü önceki arabelleğe göre oluşturmak için

    kullanın. sağladığınız çözümün yakınına gelir ve şu an sahip olduğunuzdan çok daha hızlı olacaktır.

    +0

    Çok teşekkürler!Muhtemelen bunu kullanacağım (bir gün OpenGL hakkında bilgi alana kadar). – Seub

    +2

    Onaylıyorum, yaptığım şey için bu iyi! (maksimum 0,04 saniyede paintEvent) – Seub

    7

    OpenGL, görüntü (doku) koordinat eşlemesini gerçekten iyi yapar. Muhtemelen bir çeşit OpenGL kullanmak istersiniz. Qt'nin size yardımcı olabilecek OpenGL'ye bazı bağlantıları vardır.

    +0

    +1 OP, bir UV haritalama uygulaması yaratıyor gibi görünüyor, OpenGL bunun için mükemmel bir araçtır. – cmannett85

    +0

    Cevabınız için teşekkür ederiz! Kesinlikle bir gün buna bakacağım. Sorun, OpenGL hakkında hiçbir şey bilmem. – Seub

    3

    Bunu yapmanın bir yolu, QGraphicsScene/QGraphicsView combo yerine QGLWidget öğesinden devralınan bir sınıf kullanmak olacaktır. Ne yazık ki, OpenGL için öğrenme eğrisi biraz dik başlıyor. Ancak, çok hızlı olacaktır çünkü doğrudan bu tür bir işlem için optimize edilmiş grafik kartında gerçekleşecektir.
    Görüntüyü QGLWidget::bindTexture() yükleyeceksiniz.
    Görüntüdeki noktaları üçgen ağınızla ilişkilendirir ve hepsini grafik kartınıza gönderirsiniz.

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

    triangle Eğer üçgen köşe tutan yaptık bazı veriler yapısı şöyledir: (Bence yeni API daha kullanmak daha kolaydır) OpenGL mirası versiyonunda, şöyle bir şey olmazdı ve ilişkili eşleştirmeleri görüntüye. Grafik kartı sizin için piksel enterpolasyonunu ele alacaktır.

    +0

    Cevabınız için çok teşekkür ederim! Bu çok ilginç. "Ne yazık ki, OpenGL için öğrenme eğrisi biraz dik başlıyor". Sanırım benim için sorun bu! Ama muhtemelen bir noktada bunu öğrenmem gerekecek. – Seub

    +0

    Bu, kullanımdan kaldırılmış kod kullanıyor. Gerçekten shader'ları ve doku örnekleyicilerini kullanmak istiyorsunuz. – doron

    +0

    @doron Kullanımdan kaldırıldı. Ancak sabit boru hattı işlevselliği işi yapacaksa, bu şekilde gitmesi çok daha kolaydır. GLSL, sadece 3d programlamayı öğrenme zorluğunu arttırır. Daha fazla esneklik sağlar, ancak montaj kodunu da yapar. Yine de çoğumuzun montajda fazla program yapmamasının bir nedeni var. – JCooper

    1

    OpenGL dışında başka bir seçenek de sizin için daha kolay olan OpenCL kullanmaktır. Sadece giriş/çıkış bitmap'ini grafik kartına bellekle eşleştirmeniz, C'ye bir üçgen çizen ve daha sonra her üçgen için bir çekirdek yürütme işlemi yürüten küçük bir çekirdek yazmanız yeterlidir. Bu, CPU üzerindeki tek bir çekirdek kadar 100x kadar hızlı çalışacaktır.

    burada OpenCL konak API için Qt sarıcı vardır:

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

    0

    farklı bir yaklaşım zaten raster boya motoru içinde verimli uygulanan kırpma ve dönüşümleri ve yükseltmektir. İki üçgen arasındaki dönüşüm 3x3 artırılmış bir dönüştürme matrisi kullanılarak ifade edilebildiği sürece, onu sadece hedef boyacıya ayarlamanız ve ardından tüm kaynak görüntüyü hedefe çizmeniz gerekir. Hedef üçgeni doldurmak için kırpılacak ve dönüştürülecektir. Profilleme bir avantaj sağladığında, yalnızca görüntünün tamamı yerine kaynak üçgeninin sınırlayıcı rimini çizebilirsiniz.

    Paralelleştirilebilir, böylece CPU çekirdeği olduğu için paralel olarak çok sayıda üçgeni işleyebilirsiniz.