Qt5官方demo解析集12——Qt Quick Particles Examples - CustomParticles

來源:互聯網
上載者:User

標籤:qt5官方demo解析集   qml   qml粒子   qtquick   customparticle   

本系列所有文章可以在這裡查看http://blog.csdn.net/cloud_castle/article/category/2123873

接上文Qt5官方demo解析集11——Qt Quick Particles Examples - Affectors

使用Emitter和Affectors強大的功能,我們已經可以構造出豐富多彩的粒子特效,但當這些功能還不能滿足我們的需要時,我們可以轉而採用CustomParticle取代ImageParticle,在CustomParticle中我們可以使用基於GLSL的渲染技術來建立自訂的粒子。

這個demo依然由一些小例子組成,不過比前兩個demo中的都要少,只有三個:



(1)Blur Particles

在這個例子中我們可以看到使用CustomParticle建立模糊化粒子的方法。運行效果如下:



個人感覺在這個示範中模糊效果並不是很清楚,於是換了一張圖片,並類似地使用不帶模糊化的ImageParticle來展示它們的不同:


可以看到,左邊圖為模糊化的CustomParticle,右邊為沒有模糊效果的ImageParticle,區別還是很明顯的。

由於筆者對OpenGL並不是很熟悉,如果有錯誤的地方,還請各位指正。

需要指明的是,對於這種內嵌的GLSL代碼,Qt為我們提供了一些提前定義好的變數。用uniform和attribute類型定義,作為vertexShader的輸入。比如uniform mat4 qt_Matrix —— 提供了一個從根項目到ShaderEffect的變換矩陣;uniform float qt_Opacity —— 提供項目的透明度;attribute vec4 qt_Vertex ——提供頂點座標,左上方為(0,0),後下角為(width,height);attribute vec2 qt_MultiTexCoord0 —— 紋理座標,左上方是(0,0),右下角為(1,1)。


另外,QML中任何類型的屬性都可以映射到GLSL的uniform類型中去。也就是說,當我們在QML中定義了一個4維RGBA的QColor color,然後再GLSL中聲明一個uniform vec4 color,就可以直接使用它了。我們也可以將型別宣告為var,Qt會自動幫我們完成轉換。下面的例子中大量使用了這種方法。詳細的類型映射可以參考Manual中的ShaderEffect

看源碼時,我們先看一段額外的代碼,我們將其命名為vertexShader_addin:

attribute highp vec2 qt_ParticlePos;   // 這些是預定義好的屬性attribute highp vec2 qt_ParticleTex;attribute highp vec4 qt_ParticleData; //  x = time,  y = lifeSpan, z = size,  w = endSizeattribute highp vec4 qt_ParticleVec; // x,y = constant velocity,  z,w = accelerationattribute highp float qt_ParticleR;uniform highp mat4 qt_Matrix;uniform highp float qt_Timestamp;varying highp vec2 qt_TexCoord0;       // 預設的紋理座標void defaultMain() {    qt_TexCoord0 = qt_ParticleTex;    highp float size = qt_ParticleData.z;    highp float endSize = qt_ParticleData.w;    highp float t = (qt_Timestamp - qt_ParticleData.x) / qt_ParticleData.y;    highp float currentSize = mix(size, endSize, t * t);    if (t < 0. || t > 1.)        currentSize = 0.;    highp vec2 pos = qt_ParticlePos                   - currentSize / 2. + currentSize * qt_ParticleTex   // adjust size                   + qt_ParticleVec.xy * t * qt_ParticleData.y         // apply velocity vector..                   + 0.5 * qt_ParticleVec.zw * pow(t * qt_ParticleData.y, 2.);    gl_Position = qt_Matrix * vec4(pos.x, pos.y, 0, 1);}
至於它是什麼,我們後面再說。

整個例子的源碼如下,blurparticle.qml:

import QtQuick 2.0import QtQuick.Particles 2.0Rectangle {    color: "white"    width: 240    height: 360    ParticleSystem {        id: sys    }    Emitter {                     // Emitter就不再多做介紹了,不熟悉的朋友可以查看前兩篇博文        system:sys        height: parent.height        emitRate: 1        lifeSpan: 12000        velocity: PointDirection {x:20;}        size: 128    }    ShaderEffectSource {                     // 著色效果源,用來指明需要著色的對象        id: theSource        sourceItem: theItem                  // 指明來源物件        hideSource: true                     // 隱藏原圖    }    Image {        id: theItem        source: "../../images/starfish_1.png"    }    CustomParticle {                        // CustomParticle內部僅有兩個屬性,分別是vertexShader和fragmentShader(頂點著色器與片元著色器),如果你熟悉OpenGL以及GLSL應該對這兩個東西不陌生。這兩個屬性參數為"string",實際也就是GLSL的代碼。我們可以使用這兩個屬性來將CustomParticle渲染成各種自訂的效果        system: sys         //! [vertex]        vertexShader:"                       // 這裡是我們的第一個頂點著色器,當我們編寫完vertexShader代碼後,Qt會為我們將vertexShader_addin中的代碼添加到這一行之後。這些代碼定義了一些基本的變數,以及一個defaultMain()函數,vertexShader中最基本的gl_Position的輸出也由該函數段完成,它為我們實現了基本的粒子功能。            uniform lowp float qt_Opacity;   // 定義一個唯讀低精度浮點型變數qt_Opacity            varying lowp float fFade;        // 定義一個由vertex寫入,fragment讀出的低精度浮點型變數fFade            varying lowp float fBlur;            void main() {                   // GLSL中規定的程式入口                                                         defaultMain();              // 該函數在vertexShader_addin中定義,我們需要先調用它                highp float t = (qt_Timestamp - qt_ParticleData.x) / qt_ParticleData.y;  // 可以看到qt_XX也是在vertexShader_addin中定義的,這裡計算了粒子存在時間占生命週期的比例                highp float fadeIn = min(t * 10., 1.);                       // 比例t 從0變化到1時,fadeIn也從0變化到1                highp float fadeOut = 1. - max(0., min((t - 0.75) * 4., 1.));  // t 從0.75變化到1時,fadOut從1變化到0                fFade = fadeIn * fadeOut * qt_Opacity;           // 該值越小,映像透明度越高                fBlur = max(0.2 * t, t * qt_ParticleR);          // 模糊係數            }        "        //! [vertex]        property variant source: theSource                       // 這裡回到QML代碼,定義了類型為variant的屬性source以及blurred,為了下面的映射        property variant blurred: ShaderEffectSource {           // 這裡再次使用了ShaderEffectSource,並將傳回值賦給我們的自訂屬性blurred        sourceItem: ShaderEffect {                               // sourceItem是其屬性成員之一,參數類型為Item,而ShaderEffect繼承於Item            width: theItem.width                                 // 定義為映像的高寬            height: theItem.height             property variant delta: Qt.size(0.0, 1.0 / height)   // 定義變數增量,與高度負相關            property variant source: ShaderEffectSource {        // 定義屬性source指向另一個ShaderEffectSource                sourceItem: ShaderEffect {                    width: theItem.width                    height: theItem.height                    property variant delta: Qt.size(1.0 / width, 0.0)  // 該增量與寬度相關                    property variant source: theSource                                     fragmentShader: "                                  // 片元著色器                        uniform sampler2D source;                      // 從圖片源採集紋理資料,這裡的source在QML代碼中定義                        uniform lowp float qt_Opacity;           // qt_Opacity由Qt預定義,從vertexShader傳入                        uniform highp vec2 delta;                        varying highp vec2 qt_TexCoord0;                        void main() {                     // 然後我們在main()函數中定義每個像素點的像素值,下面的算式將每個像素點周圍的的值進行疊加來得到新的像素值,從而形成模糊的效果                            gl_FragColor =(0.0538 * texture2D(source, qt_TexCoord0 - 3.182 * delta)  // 使用第二個參數中的左邊對source中的紋理進行採樣                                         + 0.3229 * texture2D(source, qt_TexCoord0 - 1.364 * delta)                                         + 0.2466 * texture2D(source, qt_TexCoord0)                                         + 0.3229 * texture2D(source, qt_TexCoord0 + 1.364 * delta)                                         + 0.0538 * texture2D(source, qt_TexCoord0 + 3.182 * delta)) * qt_Opacity;                        }"                }            }            fragmentShader: "                uniform sampler2D source;                uniform lowp float qt_Opacity;                uniform highp vec2 delta;                varying highp vec2 qt_TexCoord0;                void main() {                   // 第一次混合以寬度作為增量,第二次以高度作為增量。這裡的source是已經被處理過一次的紋理源                    gl_FragColor =(0.0538 * texture2D(source, qt_TexCoord0 - 3.182 * delta)                                 + 0.3229 * texture2D(source, qt_TexCoord0 - 1.364 * delta)                                 + 0.2466 * texture2D(source, qt_TexCoord0)                                 + 0.3229 * texture2D(source, qt_TexCoord0 + 1.364 * delta)                                 + 0.0538 * texture2D(source, qt_TexCoord0 + 3.182 * delta)) * qt_Opacity;                }"            }        }        //! [fragment]        fragmentShader: "                   // 最後將vertexShader中定義的資料送入fragmentShader中進行處理            uniform sampler2D source;                     uniform sampler2D blurred;                      varying highp vec2 qt_TexCoord0;            varying highp float fBlur;            varying highp float fFade;            void main() {                gl_FragColor = mix(texture2D(source, qt_TexCoord0), texture2D(blurred, qt_TexCoord0), min(1.0,fBlur*3.0)) * fFade;  // 在每個像素點對源紋理紋理與模糊化紋理之間進行插值,並乘以透明度。            }"        //! [fragment]    }}


(2)Fragment Shader

在上個例子我們對使用CustomParticle渲染一個外部png映像有了一個大致印象,在這個例子中,Qt 向我們介紹了如何直接使用片元著色器來繪製粒子。


螢幕下方的話也揭示了這個例子的主題。


這個例子的層次的結構沒有上一個例子那麼複雜,來看看吧,fragmentshader.qml:

import QtQuick 2.0import QtQuick.Particles 2.0ParticleSystem {                             // ParticleSystem作為根目錄    id: root    width: 320    height: 480    Rectangle {                                   // 矩形背景        z: -1        anchors.fill: parent        color: "black"        Text {                                      // 文字            anchors.bottom: parent.bottom            anchors.horizontalCenter: parent.horizontalCenter            font.pixelSize: 14            color: "white"            text: "It's all in the fragment shader."        }    }    Emitter {        emitRate: 400        lifeSpan: 8000        size: 24        sizeVariation: 16        velocity: PointDirection {x: root.width/10; y: root.height/10;}        acceleration: PointDirection {x: -root.width/40; y: -root.height/40; xVariation: -root.width/20; yVariation: -root.width/20}    }    CustomParticle {                                vertexShader:"                              // 頂點著色器            uniform lowp float qt_Opacity;          // 變數定義            varying lowp float fFade;            varying highp vec2 fPos;            void main() {                        // 還記得上面的vertexShader_addin嗎,下面的只是將defaltMain()中的代碼提出來了                qt_TexCoord0 = qt_ParticleTex;                highp float size = qt_ParticleData.z;                highp float endSize = qt_ParticleData.w;                highp float t = (qt_Timestamp - qt_ParticleData.x) / qt_ParticleData.y;                highp float currentSize = mix(size, endSize, t * t);                if (t < 0. || t > 1.)                currentSize = 0.;                highp vec2 pos = qt_ParticlePos                - currentSize / 2. + currentSize * qt_ParticleTex          // adjust size                + qt_ParticleVec.xy * t * qt_ParticleData.y         // apply velocity vector..                + 0.5 * qt_ParticleVec.zw * pow(t * qt_ParticleData.y, 2.);                gl_Position = qt_Matrix * vec4(pos.x, pos.y, 0, 1);     // 以上都是defaultMain()的代碼,之所以不用defaultMain(),是因為該函數中定義的一些變數在下面還要被繼續使用                highp float fadeIn = min(t * 20., 1.);                  // 與上一個例子類似的定義了與t相關的fadeIn與fadeOut                highp float fadeOut = 1. - max(0., min((t - 0.75) * 4., 1.));                fFade = fadeIn * fadeOut * qt_Opacity;               // 得到粒子透明度的動態變化數值                fPos = vec2(pos.x/320., pos.y/480.);                 // 這裡得到位置的二維向量            }        "        //! [0]        fragmentShader: "                                     // 片元著色器            varying highp vec2 fPos;                          // 傳參            varying lowp float fFade;            varying highp vec2 qt_TexCoord0;            void main() {//*2 because this generates dark colors mostly  // 官方注釋                highp vec2 circlePos = qt_TexCoord0*2.0 - vec2(1.0,1.0);  // qt_TexCoord0是一個內建的渲染座標,這個算式將座標原點轉移到了矩形中心,並放大了一倍                highp float dist = length(circlePos);                    // GLSL內建函數,用來求向量長度                highp float circleFactor = max(min(1.0 - dist, 1.0), 0.0); // 在一個長度為2矩形框內,以長度為1在中心劃一個地區,那就是一個內切圈                gl_FragColor = vec4(fPos.x*2.0 - fPos.y, fPos.y*2.0 - fPos.x, fPos.x*fPos.y*2.0, 0.0) * circleFactor * fFade; // 最後將每個像素點的像素值乘上這個圓因子,使繪製出來的圓形越靠近中心RGB值越大,遠端小的RGB值被繪製為黑色,從而得到了顏色越來越淡的"圓形粒子"。由四維向量vec4()的4個參數可以知道,x值越大R值越大,y值越大G值越大,x,y同時影響B的值。那麼上方的粒子應該偏紅色,下方的粒子偏綠色,右下角的粒子為RGB值都大的紫色,湛藍色,深灰色等。啟動並執行實際效果也如我們猜想的一樣。            }"        //! [0]    }}


(3)Image Color

在這個例子中,Qt向我們展示了一副映像被“粒子化”的過程,與我們之前用shape覆蓋映像不同,這裡的粒子顏色會隨著圖片中當前像素的變化而變化。



在螢幕上點擊過後,映像會以粒子的形態展現出來,然後向四周發散。imagecolor.qml:

import QtQuick 2.0import QtQuick.Particles 2.0Rectangle {                                          // 一個矩形用來作為視窗邊界    width: 400    height: 400    Rectangle {                                        // 一個矩形又來限制映像尺寸        id: root        color: "white"        width: 310        height: 300        anchors.centerIn: parent        ParticleSystem { id: sys }        CustomParticle {            system: sys            property real maxWidth: root.width              // 定義了最大寬高            property real maxHeight: root.height            ShaderEffectSource {                          // 使用這個類型提供映像的紋理資料                id: pictureSource                sourceItem: picture                hideSource: true            }            Image {                                        // 海星星,我們也可以換成其他的圖片                id: picture                source: "qrc:/images/starfish_3.png"            }            ShaderEffectSource {                           // 第二個ShaderEffectSource用來支援粒子                id: particleSource                sourceItem: particle                hideSource: true            }            Image {                                        // ImageParticle中常用的fuzzydot,光點                id: particle                source: "qrc:///particleresources/fuzzydot.png"            }            //! [vertex]            vertexShader:"                uniform highp float maxWidth;              // 向GLSL傳參                uniform highp float maxHeight;                varying highp vec2 fTex2;                  // 該參數用來計算點在當前映像上的位置                varying lowp float fFade;                  // 這個參數應該不陌生了,提供漸隱效果                uniform lowp float qt_Opacity;                             void main() {                    fTex2 = vec2(qt_ParticlePos.x, qt_ParticlePos.y);                    //Uncomment this next line for each particle to use full texture, instead of the solid color at the center of the particle.                    //fTex2 = fTex2 + ((- qt_ParticleData.z / 2. + qt_ParticleData.z) * qt_ParticleTex); //Adjusts size so it's like a chunk of image.                    fTex2 = fTex2 / vec2(maxWidth, maxHeight);                    highp float t = (qt_Timestamp - qt_ParticleData.x) / qt_ParticleData.y;                    fFade = min(t*4., (1.-t*t)*.75) * qt_Opacity;                    defaultMain();                }            "            //! [vertex]            property variant particleTexture: particleSource            property variant pictureTexture: pictureSource            //! [fragment]            fragmentShader: "                uniform sampler2D particleTexture;             // 然後對粒子紋理採樣                uniform sampler2D pictureTexture;              // 對映像紋理採樣                varying highp vec2 qt_TexCoord0;  // 該向量相當於包含了(0,0)到(1,1)的所有像素點,如果基於它採樣,每個粒子都被渲染成這個映像的樣子                varying highp vec2 fTex2;        // 因此引入這個比例向量,用來僅僅提供一個像素點的座標                varying lowp float fFade;                void main() {                    gl_FragColor = texture2D(pictureTexture, fTex2) * texture2D(particleTexture, qt_TexCoord0).w * fFade; // 因此第一個因子用來提供顏色,第二個因子用來提供形狀,最後一個因子提供漸隱的效果            }"            //! [fragment]        }        Emitter {            id: emitter            system: sys            enabled: false              // 先關閉            lifeSpan: 8000            maximumEmitted: 4000            anchors.fill: parent            size: 16            acceleration: PointDirection { xVariation: 12; yVariation: 12 }  // 向四周發散        }        MouseArea {            anchors.fill: parent           // 點擊使能            onClicked: emitter.burst(4000);        }    }}


相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.