qt5_qml_Opengl_shader 第一彈----------------------openglunderqml的簡化及介紹


最近得知opengl可以通過紋理貼圖來渲染yuv的數據,這就免去了yuv-rgb,這個過程在arm上還是很耗時間的,於是就接觸了opengl.

寫這篇文章的目的是方便初學者使用qml來調用opengl顯示,而qt自帶的例程過於復雜,這里將之改成了一個簡單的三角形的顯示。(初學opengl,有錯誤請大家提出)

初學opengl,感覺不是很難,但是就是封裝版本太多了,下邊分三個來介紹:

Glut:

用的最多,大致步驟如下

1>    初始化:glutInit(),opengl的初始化很復雜的,但是經過glut的封裝就變得很簡單。

2>     顯示模式:glutInitDisplayMode:以rgb顯示或者其他,然后用雙緩沖來顯示,這是opengl很好的地方,加入有兩幀數據,普通的顯示:讀第一張到內存里-顯示-讀第二張到內存-顯示,opengl的雙緩沖顯示,在顯示第一針的同時讀入第二針,這樣就不會造成卡頓,也增加了效率。

3>     創建窗口。

4>     加載顯示函數:glutDisplayFunc(&myDisplay);這個是最重要的,關於顯示的東西都在一個函數里,這樣代碼就變得很清晰了。

5>     主循環

[cpp]  view plain copy
 
  1. #include <GL/glut.h>  
  2. void myDisplay(void)  
  3. {  
  4.      glClear(GL_COLOR_BUFFER_BIT);  
  5.      glRectf(-0.5f, -0.5f, 0.5f, 0.5f);  
  6.      glFlush();  
  7. }  
  8. int main(int argc, char *argv[])  
  9. {  
  10.      glutInit(&argc, argv);  
  11.      glutInitDisplayMode(GLUT_RGB | GLUT_SINGLE);  
  12.      glutInitWindowPosition(100, 100);  
  13.      glutInitWindowSize(400, 400);  
  14.      glutCreateWindow("第一個OpenGL程序");  
  15.      glutDisplayFunc(&myDisplay);  
  16.      glutMainLoop();  
  17.      return 0;  

 

關於glut的描述,有興趣的同學可以移步:

http://www.cnblogs.com/crazyxiaom/articles/2073586.html,講的很詳細。

另外在qt里邊也可以調用glut。

Glwidget

Qt有自己對opengl的封裝,在qt5之后叫QOpenGLFunctions,在qt中用opengl最多的是GLwidget,主要通過三個函數實現:

voidinitializeGL();--初始化

void paintGL(); --顯示

void resizeGL(int width, int height );--自動縮放

 

詳細介紹請參考nehe的opengl:http://www.yakergong.net/nehe/

結合網上的例子,我自己用最新的glwidget,實現了簡單的三角形的繪制,會在最后附上代碼,這里就不多講了。

Scene Graph - OpenGL Under QML 

因為本人酷愛qml,所以決定將現實的東西以qml插件的形式來顯示。

有兩個可以參考:分別是qt自帶的例子:SceneGraph - OpenGL Under QML,還有彩陽大神的例子:http://blog.csdn.net/gamesdev/article/details/38024327

我主要參考了qt自帶的例子,在其基礎上結合qt幫助文檔中的QOpenGLShaderProgram Class中的幾段代碼,略微懂了一些shader的知識,然后實現了用qml來顯示簡單的三角形。

首先需要定義兩個類,一個供顯示,另一個提供邏輯和線程的相應(個人認為是因為顯示的類要調用gpu來處理),

classSquircleRenderer:publicQObject:供顯示的類

classSquircle:publicQQuickItem:提供邏輯的類

 

然后主要的信號都是由qquickwindow.h等提供,

win,SIGNAL(beforeSynchronizing()———This signal is emittedbefore the scene graph is synchronized with the QML state.:

此信號與qml的狀態同步,個人理解當qml中的參數改變或者窗口大小改變等狀態改變都會觸發這個信號。

win,SIGNAL(sceneGraphInvalidated()———This signal is emittedwhen the scene graph has been invalidated.

                                       這個信號在scene grarh失效的時候觸發

window(),SIGNAL(beforeRendering()———This signal is emittedbefore the scene starts rendering.

                                      這個信號在scren graph渲染之前觸發,在改變窗口大小的時候也會觸發

 

然后我們來介紹幾個槽函數:

[cpp]  view plain copy
 
  1. void Squircle::sync()  
  2. {  
  3.     if (!m_renderer) {  
  4.         m_renderer = new SquircleRenderer();  
  5.         connect(window(), SIGNAL(beforeRendering()), m_renderer, SLOT(paint()), Qt::DirectConnection);  
  6.     }  
  7.     m_renderer->setViewportSize(window()->size() * window()->devicePixelRatio());  
  8. }  

 

比較多哈,其實前一部分只用了一次,如果沒有實例化顯示的類SquircleRenderer,則實例化一個,然后將paint()函數綁定信號:beforeRendering

后一部分很簡單,調用顯示類的setViewportSize函數:

voidsetViewportSize(constQSize&size)

 {

 m_viewportSize=size;

}

這個函數的作用就是將當前的窗口尺寸給m_viewportSize,而m_viewportSize就是opengl函數glViewport,所用到的長和寬,這個函數知道opengl的都明白的。

好,下一個槽函數:

[cpp]  view plain copy
 
  1. void Squircle::cleanup()  
  2. {  
  3.     if (m_renderer) {  
  4.         delete m_renderer;  
  5.         m_renderer = 0;  
  6.     }  
  7. }  

 

這個函數,很簡單,就是清除畫面,什么時候執行呢:看這個:

        connect(win,SIGNAL(sceneGraphInvalidated()),this,SLOT(cleanup()),Qt::DirectConnection);

sceneGraphInvalidated:這個信號在scene grarh失效的時候觸發,意思就是你關閉了窗口了,scene grarh失效,然后畫面清除。

 
 

下邊是最重要的槽函數了:paint函數:(此函數連接了beforerendering信號,這個信號在改變窗口大小的時候觸發,這樣就可以保證在改變窗口是重新調用paint函數來重繪)

[cpp]  view plain copy
 
  1. void SquircleRenderer::paint()  
  2. {  
  3.     if (!m_program) {  
  4.         m_program = new QOpenGLShaderProgram();  
  5.   
  6.         m_program->addShaderFromSourceCode(QOpenGLShader::Vertex,  
  7.                                            "attribute highp vec4 vertices;"  
  8.                                            "varying highp vec2 coords;"  
  9.                                            "void main() {"  
  10.                                            "    gl_Position = vertices;"  
  11.                                            "    coords = vertices.xy;"  
  12.                                            "}");  
  13.         m_program->addShaderFromSourceCode(QOpenGLShader::Fragment,  
  14.                                            "uniform mediump vec4 color;\n"  
  15.                                            "void main(void)\n"  
  16.                                            "{\n"  
  17.                                            "   gl_FragColor = color;\n"  
  18.                                            "}");  
  19.         m_program->bindAttributeLocation("vertices", 0);  
  20.         m_program->link();  
  21.     }  
  22.     m_program->bind();  
  23.     {  
  24.   
  25.         m_program->enableAttributeArray(0);  
  26.   
  27.         float values[] = {  
  28.             -1, -1,  
  29.             1, -1,  
  30.             -1, 1,  
  31.             1, 1  
  32.         };  
  33.         m_program->setAttributeArray(0, GL_FLOAT, values, 2);  
  34.   
  35.         int colorLocation = m_program->uniformLocation("color");  
  36.         QColor color(255, 0, 0, 100);  
  37.         m_program->setUniformValue(colorLocation, color);  
  38.   
  39.         glViewport(0, 0, m_viewportSize.width(), m_viewportSize.height());  
  40.         glDisable(GL_DEPTH_TEST);  
  41.         glDrawArrays(GL_TRIANGLES, 0, 3);  
  42.   
  43.         m_program->disableAttributeArray(0);  
  44.     }  
  45.     m_program->release();  
  46. }  
這些我也有很多不懂,先實例化一個QOpenGLShaderProgram:m_program,然后將shader的代碼綁定,第一段Vertex與位置相關,第二段Fragment與顏色相關,由於我們要將顯示做到qml插件上,所以,直接套用了qt例程中的代碼,其中我搞懂的是       
 floatvalues[]={
            -1,-1,
            1,-1,
            -1,1,
            1,1
        };
        m_program->setAttributeArray(0,GL_FLOAT,values,2);

這個values[],表示渲染的對象在viewport的位置,如下圖


 

按照這個設置,就是以viewport的大小和位置為渲染對象的大小和位置;

但是如果是
floatvalues[]={
         1,0,
            0,0,
         0,1,
            1,1       
 };
則表示,畫在viewport的右上角,如圖

 

而且點的順序的不同,形狀也不同,類似於紋理貼圖,將對象的點與viewport的點進行映射。

然后是顏色:

intcolorLocation=m_program->uniformLocation("color");

       QColorcolor(255,0,0,100);

        m_program->setUniformValue(colorLocation,color);

 

這里將colorLocation指向shader着色器中的color變量,
uniformmediumpvec4color;             
gl_FragColor=color;
然后定義一個qcolor,通過setUniformValue函數將定義的qcolor變量給colorLocation變量(這個變量傳遞真是惡心,但是必須這樣)
 
然后glViewport(0,0,m_viewportSize.width(),m_viewportSize.height());
設置viewport。
最后glDisable(GL_DEPTH_TEST);這個很重要,不這個在改變窗口大小的時候三角形會閃爍消失。
主要的就介紹到這里了。下邊通過qt消息相應將流程說明一下:
 
1>  初始化Squircle類:
SIGNAL(windowChanged(QQuickWindow*)àSLOT(handleWindowChanged(QQuickWindow*):
handleWindowChanged函數用於綁定信號:
connect(win,SIGNAL(beforeSynchronizing()),this,SLOT(sync()),Qt::DirectConnection);
        connect(win,SIGNAL(sceneGraphInvalidated()),this,SLOT(cleanup()),Qt::DirectConnection);
2>  qml狀態改變-觸發SIGNAL(beforeSynchronizing()-調用槽函數sync()綁定        connect(window(),SIGNAL(beforeRendering()),m_renderer,SLOT(paint()),Qt::DirectConnection);
3>  要渲染時候觸發beforeRendering信號,調用paint函數來畫
4>  在改變窗口大小時,qml狀態改變,觸發SIGNAL(beforeSynchronizing(),調用sync()函數,設置viewport的size,(我認為只要viewport的大小改變了,就會自動調用paint函數來重繪,這里確實沒有update函數。)

代碼地址:http://download.csdn.net/detail/u010423298/8699493

http://blog.csdn.net/u010423298/article/details/44889523


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM