QML Book 第九章 著色器渲染 2

9.5 頂點(diǎn)著色器

頂點(diǎn)著色器可用于操縱著色器效果提供的頂點(diǎn)。在正常情況下,著色效果有 4 個(gè)頂點(diǎn)(左上角 top-left,右上角 top-right,左下角 bottom-left 和右下角 bottom-righ)。報(bào)告的每個(gè)頂點(diǎn)都是來自 vec4 的類型。為了可視化頂點(diǎn)著色器,我們將編寫一個(gè)縮放效果。這種效果通常用于使矩形窗口區(qū)域縮放到一個(gè)點(diǎn)。

genieeffect

** 設(shè)置場(chǎng)景 **

首先我們將再次設(shè)置我們的場(chǎng)景。

import QtQuick 2.5

Rectangle {
    width: 480; height: 240
    color: '#1e1e1e'

    Image {
        id: sourceImage
        width: 160; height: width
        source: "assets/lighthouse.jpg"
        visible: false
    }
    Rectangle {
        width: 160; height: width
        anchors.centerIn: parent
        color: '#333333'
    }
    ShaderEffect {
        id: genieEffect
        width: 160; height: width
        anchors.centerIn: parent
        property variant source: sourceImage
        property bool minimized: false
        MouseArea {
            anchors.fill: parent
            onClicked: genieEffect.minimized = !genieEffect.minimized
        }
    }
}

這提供了一個(gè)具有深色背景和使用圖像作為源紋理的著色器效果的場(chǎng)景。原始圖像在我們的縮放效果產(chǎn)生的圖像上不可見。另外,我們?cè)谂c著色器效果相同的幾何體上添加了一個(gè)黑色矩形,因此我們可以更好地感知我們需要點(diǎn)擊以實(shí)現(xiàn)還原效果的位置。

geniescene

縮放效果是通過點(diǎn)擊圖像觸發(fā)的,這是在覆蓋縮放效果的鼠標(biāo)區(qū)域中定義的。在 onClicked 處理方法中,我們將自定義布爾屬性 minimized。稍后我們將使用此屬性實(shí)現(xiàn)切換縮放的效果。

** 最小化和恢復(fù)原樣 **

在我們?cè)O(shè)置場(chǎng)景之后,我們定義一個(gè)類型為 real 的屬性稱為 minimize,該屬性將包含我們最小化的當(dāng)前值。該值將從 0.0 到 1.0 不等,并由順序動(dòng)畫進(jìn)行控制。

        property real minimize: 0.0

        SequentialAnimation on minimize {
            id: animMinimize
            running: genieEffect.minimized
            PauseAnimation { duration: 300 }
            NumberAnimation { to: 1; duration: 700; easing.type: Easing.InOutSine }
            PauseAnimation { duration: 1000 }
        }

        SequentialAnimation on minimize {
            id: animNormalize
            running: !genieEffect.minimized
            NumberAnimation { to: 0; duration: 700; easing.type: Easing.InOutSine }
            PauseAnimation { duration: 1300 }
        }

動(dòng)畫由最小化(minimized)屬性的值引發(fā)?,F(xiàn)在我們已經(jīng)設(shè)置了所有要準(zhǔn)備的環(huán)境,我們終于可以看看我們的頂點(diǎn)著色器了。

        vertexShader: "
            uniform highp mat4 qt_Matrix;
            attribute highp vec4 qt_Vertex;
            attribute highp vec2 qt_MultiTexCoord0;
            varying highp vec2 qt_TexCoord0;
            uniform highp float minimize;
            uniform highp float width;
            uniform highp float height;
            void main() {
                qt_TexCoord0 = qt_MultiTexCoord0;
                highp vec4 pos = qt_Vertex;
                pos.y = mix(qt_Vertex.y, height, minimize);
                pos.x = mix(qt_Vertex.x, width, minimize);
                gl_Position = qt_Matrix * pos;
            }"

在我們的例子中,為每個(gè)頂點(diǎn)調(diào)用頂點(diǎn)著色器四次。提供默認(rèn)的 qt 定義參數(shù),如qt_Matrix,qt_Vertex,qt_MultiTexCoord0,qt_TexCoord0。我們?cè)缫延懻撨^的變量。 另外,我們將著色器效果的最小化,寬度和高度變量鏈接到我們的頂點(diǎn)著色器代碼中。在主函數(shù)中,我們將當(dāng)前紋理坐標(biāo)存儲(chǔ)在我們的 qt_TexCoord0 中,使其可用于片段著色器?,F(xiàn)在我們復(fù)制當(dāng)前位置并修改頂點(diǎn)的 x 和 y 位置:

highp vec4 pos = qt_Vertex;
pos.y = mix(qt_Vertex.y, height, minimize);
pos.x = mix(qt_Vertex.x, width, minimize);

mix(...) 功能在第 3 個(gè)參數(shù)提供的點(diǎn)(0.0-1.0)上的前 2 個(gè)參數(shù)之間提供線性插值。所以在我們的例子中,我們根據(jù)當(dāng)前最小化值,在當(dāng)前 y 位置和高度之間插入 y,與 x 類似。請(qǐng)記住,最小值是由我們的連續(xù)動(dòng)畫控制,并從 0.0 到 1.0(反之亦然)。

genieminimize

上面所產(chǎn)生的效果不是真正的縮放效果,但已經(jīng)邁出了我們的第一步。

** 簡(jiǎn)單的彎曲 **

至此我們完成了頂點(diǎn)的 x 和 y 分量的最小化?,F(xiàn)在我們要稍微修改 x 操作,并根據(jù)當(dāng)前的 y 值進(jìn)行修改。 所需的變化相當(dāng)小。 y 位置如前所述計(jì)算。x 位置的插值現(xiàn)在取決于頂點(diǎn) y 位置:

highp float t = pos.y / height;
pos.x = mix(qt_Vertex.x, width, t * minimize);

這導(dǎo)致當(dāng) y 位置較大時(shí)向 x 方向趨向于寬度。換句話說,上面的 2 個(gè)頂點(diǎn)根本不受影響,因?yàn)樗鼈兊?y 位置為 0,而較低的兩個(gè)頂點(diǎn) x 位置都朝向?qū)挾葟澢?,因此它們朝向相同?x 位置彎曲。

geniebending
import QtQuick 2.5

Rectangle {
    width: 480; height: 240
    color: '#1e1e1e'

    Image {
        id: sourceImage
        width: 160; height: width
        source: "assets/lighthouse.jpg"
        visible: false
    }
    Rectangle {
        width: 160; height: width
        anchors.centerIn: parent
        color: '#333333'
    }
    ShaderEffect {
        id: genieEffect
        width: 160; height: width
        anchors.centerIn: parent
        property variant source: sourceImage
        property real minimize: 0.0
        property bool minimized: false


        SequentialAnimation on minimize {
            id: animMinimize
            running: genieEffect.minimized
            PauseAnimation { duration: 300 }
            NumberAnimation { to: 1; duration: 700; easing.type: Easing.InOutSine }
            PauseAnimation { duration: 1000 }
        }

        SequentialAnimation on minimize {
            id: animNormalize
            running: !genieEffect.minimized
            NumberAnimation { to: 0; duration: 700; easing.type: Easing.InOutSine }
            PauseAnimation { duration: 1300 }
        }


        vertexShader: "
            uniform highp mat4 qt_Matrix;
            uniform highp float minimize;
            uniform highp float height;
            uniform highp float width;
            attribute highp vec4 qt_Vertex;
            attribute highp vec2 qt_MultiTexCoord0;
            varying highp vec2 qt_TexCoord0;
            void main() {
                qt_TexCoord0 = qt_MultiTexCoord0;
                // M1>>
                highp vec4 pos = qt_Vertex;
                pos.y = mix(qt_Vertex.y, height, minimize);
                highp float t = pos.y / height;
                pos.x = mix(qt_Vertex.x, width, t * minimize);
                gl_Position = qt_Matrix * pos;

** 更好的彎曲效果 **

由于目前的彎曲情況并不令人滿意,我們會(huì)增加幾個(gè)部分來改善情況。首先我們?cè)鰪?qiáng)我們的動(dòng)畫來支持自己的彎曲屬性。這是必要的,因?yàn)閺澢鷳?yīng)該立即發(fā)生,并且 y 最小化應(yīng)該被延遲。兩個(gè)動(dòng)畫的總和相同(300 + 700 + 1000 和 700 + 1300)。

        property real bend: 0.0
        property bool minimized: false


        // change to parallel animation
        ParallelAnimation {
            id: animMinimize
            running: genieEffect.minimized
            SequentialAnimation {
                PauseAnimation { duration: 300 }
                NumberAnimation {
                    target: genieEffect; property: 'minimize';
                    to: 1; duration: 700;
                    easing.type: Easing.InOutSine
                }
                PauseAnimation { duration: 1000 }
            }
            // adding bend animation
            SequentialAnimation {
                NumberAnimation {
                    target: genieEffect; property: 'bend'
                    to: 1; duration: 700;
                    easing.type: Easing.InOutSine }
                PauseAnimation { duration: 1300 }
            }
        }

另外,為了使彎曲成為平滑曲線,x 位置上的 y 效應(yīng)不會(huì)由 0..1 的彎曲函數(shù)修改,pos.x 現(xiàn)在取決于新的彎曲屬性動(dòng)畫:

highp float t = pos.y / height;
t = (3.0 - 2.0 * t) * t * t;
pos.x = mix(qt_Vertex.x, width, t * bend);

曲線以 0.0 值開始平滑曲線,然后平穩(wěn)地向 1.0 值增長(zhǎng)并停止。以下是指定范圍內(nèi)的功能圖。我們只關(guān)心 0..1 的范圍區(qū)間。

curve

最直觀的變化是增加我們的頂點(diǎn)數(shù)量??梢允褂镁W(wǎng)格來增加使用的頂點(diǎn):

mesh: GridMesh { resolution: Qt.size(16, 16) }

著色器效果現(xiàn)在具有 16x16 頂點(diǎn)的相等分布式網(wǎng)格,而不是之前使用的 2×2 個(gè)頂點(diǎn)。這使得頂點(diǎn)之間的插值看起來更加平滑。

geniesmoothbending

我們可以看到正在使用的曲線的影響,因?yàn)閺澢Y(jié)束時(shí)很好地平滑。這是彎曲效果最強(qiáng)的地方。

** 選擇一邊 **

作為最終的增強(qiáng),我們希望能夠切換側(cè)面。一方面,縮放效果消失了。到目前為止,它總是朝著 width 方向消失。通過添加一個(gè) side 屬性,我們可以將其修改成 0 和 width 之間的點(diǎn)。

ShaderEffect {
    ...
    property real side: 0.5

    vertexShader: "
        ...
        uniform highp float side;
        ...
        pos.x = mix(qt_Vertex.x, side * width, t * bend);
    "
}
geniehalfside

** 打包縮放效果 **

最后要做的是很好地打包我們的效果。為此,我們將縮放效果代碼提取到一個(gè)名為 GenieEffect 的組件中。它具有著色器作為根元素。我們刪除鼠標(biāo)區(qū)域,因?yàn)檫@不應(yīng)該在組件內(nèi),因?yàn)樾Ч挠|發(fā)可以被最小化(minimized)屬性代替。

import QtQuick 2.5

ShaderEffect {
    id: genieEffect
    width: 160; height: width
    anchors.centerIn: parent
    property variant source
    mesh: GridMesh { resolution: Qt.size(10, 10) }
    property real minimize: 0.0
    property real bend: 0.0
    property bool minimized: false
    property real side: 1.0


    ParallelAnimation {
        id: animMinimize
        running: genieEffect.minimized
        SequentialAnimation {
            PauseAnimation { duration: 300 }
            NumberAnimation {
                target: genieEffect; property: 'minimize';
                to: 1; duration: 700;
                easing.type: Easing.InOutSine
            }
            PauseAnimation { duration: 1000 }
        }
        SequentialAnimation {
            NumberAnimation {
                target: genieEffect; property: 'bend'
                to: 1; duration: 700;
                easing.type: Easing.InOutSine }
            PauseAnimation { duration: 1300 }
        }
    }

    ParallelAnimation {
        id: animNormalize
        running: !genieEffect.minimized
        SequentialAnimation {
            NumberAnimation {
                target: genieEffect; property: 'minimize';
                to: 0; duration: 700;
                easing.type: Easing.InOutSine
            }
            PauseAnimation { duration: 1300 }
        }
        SequentialAnimation {
            PauseAnimation { duration: 300 }
            NumberAnimation {
                target: genieEffect; property: 'bend'
                to: 0; duration: 700;
                easing.type: Easing.InOutSine }
            PauseAnimation { duration: 1000 }
        }
    }

    vertexShader: "
        uniform highp mat4 qt_Matrix;
        attribute highp vec4 qt_Vertex;
        attribute highp vec2 qt_MultiTexCoord0;
        uniform highp float height;
        uniform highp float width;
        uniform highp float minimize;
        uniform highp float bend;
        uniform highp float side;
        varying highp vec2 qt_TexCoord0;
        void main() {
            qt_TexCoord0 = qt_MultiTexCoord0;
            highp vec4 pos = qt_Vertex;
            pos.y = mix(qt_Vertex.y, height, minimize);
            highp float t = pos.y / height;
            t = (3.0 - 2.0 * t) * t * t;
            pos.x = mix(qt_Vertex.x, side * width, t * bend);
            gl_Position = qt_Matrix * pos;
        }"
}

我們現(xiàn)在可以這樣使用該效果:

import QtQuick 2.5

Rectangle {
    width: 480; height: 240
    color: '#1e1e1e'

    GenieEffect {
        source: Image { source: 'assets/lighthouse.jpg' }
        MouseArea {
            anchors.fill: parent
            onClicked: parent.minimized = !parent.minimized
        }
    }
}

我們通過刪除我們的背景矩形來簡(jiǎn)化代碼,我們將圖像直接分配給效果,而不是將其加載到獨(dú)立的圖像元素中。

9.6 帷幕效果

在自定義著色效果的最后一個(gè)例子中,我們想給你帶來帷幕效果。2011年5月首次發(fā)布此效果作為 Qt 著色器效果實(shí)驗(yàn)室 的一部分。

curtain

當(dāng)時(shí)我(原作者)真的很喜歡這些效果,帷幕效果是我最喜歡的。我特別帷幕怎樣打開和怎樣隱藏背景物體。

只是一個(gè)機(jī)器人的背景,窗簾實(shí)際上是一個(gè)名為fabric.jpg的圖像,它是著色器效果的來源。效果使用頂點(diǎn)著色器擺動(dòng)窗簾,并使用片段著色器提供一些陰影。下面是一個(gè)簡(jiǎn)單的圖表,讓我們更好地了解代碼。

curtain_diagram

窗簾的波浪色調(diào)通過簾幕寬度上的 7 個(gè)上/下(7 * PI = 21.99 ...)的 sin 曲線計(jì)算。另一個(gè)重要的部分是秋千。 當(dāng)窗簾打開或關(guān)閉時(shí),窗簾的頂部寬度是動(dòng)畫的。bottomWidth 遵循 topWidth 與 SpringAnimation。通過這一點(diǎn),我們創(chuàng)造了窗簾底部擺動(dòng)的效果。計(jì)算的擺動(dòng)提供了在頂點(diǎn)的 y 分量上內(nèi)插的這種擺動(dòng)的強(qiáng)度。

窗簾效果位于 CurtainEffect.qml 組件中,其中織物圖像用作紋理源。在這里使用著色器沒有任何新意義,只是在片段著色器中處理頂點(diǎn)著色器中的 gl_Position 和 gl_FragColor 的不同方法。

import QtQuick 2.5

ShaderEffect {
    anchors.fill: parent

    mesh: GridMesh {
        resolution: Qt.size(50, 50)
    }

    property real topWidth: open?width:20
    property real bottomWidth: topWidth
    property real amplitude: 0.1
    property bool open: false
    property variant source: effectSource

    Behavior on bottomWidth {
        SpringAnimation {
            easing.type: Easing.OutElastic;
            velocity: 250; mass: 1.5;
            spring: 0.5; damping: 0.05
        }
    }

    Behavior on topWidth {
        NumberAnimation { duration: 1000 }
    }


    ShaderEffectSource {
        id: effectSource
        sourceItem: effectImage;
        hideSource: true
    }

    Image {
        id: effectImage
        anchors.fill: parent
        source: "assets/fabric.png"
        fillMode: Image.Tile
    }

    vertexShader: "
        attribute highp vec4 qt_Vertex;
        attribute highp vec2 qt_MultiTexCoord0;
        uniform highp mat4 qt_Matrix;
        varying highp vec2 qt_TexCoord0;
        varying lowp float shade;

        uniform highp float topWidth;
        uniform highp float bottomWidth;
        uniform highp float width;
        uniform highp float height;
        uniform highp float amplitude;

        void main() {
            qt_TexCoord0 = qt_MultiTexCoord0;

            highp vec4 shift = vec4(0.0, 0.0, 0.0, 0.0);
            highp float swing = (topWidth - bottomWidth) * (qt_Vertex.y / height);
            shift.x = qt_Vertex.x * (width - topWidth + swing) / width;

            shade = sin(21.9911486 * qt_Vertex.x / width);
            shift.y = amplitude * (width - topWidth + swing) * shade;

            gl_Position = qt_Matrix * (qt_Vertex - shift);

            shade = 0.2 * (2.0 - shade ) * ((width - topWidth + swing) / width);
        }"

    fragmentShader: "
        uniform sampler2D source;
        varying highp vec2 qt_TexCoord0;
        varying lowp float shade;
        void main() {
            highp vec4 color = texture2D(source, qt_TexCoord0);
            color.rgb *= 1.0 - shade;
            gl_FragColor = color;
        }"
}

該效果用于 curtaindemo.qml 文件。

import QtQuick 2.5

Item {
    id: root
    width: background.width; height: background.height


    Image {
        id: background
        anchors.centerIn: parent
        source: 'assets/background.png'
    }

    Text {
        anchors.centerIn: parent
        font.pixelSize: 48
        color: '#efefef'
        text: 'Qt5 Cadaques'
    }

    CurtainEffect {
        id: curtain
        anchors.fill: parent
    }

    MouseArea {
        anchors.fill: parent
        onClicked: curtain.open = !curtain.open
    }
}

窗簾通過窗簾效果的定制 open 屬性打開。我們使用 MouseArea 來觸發(fā)窗簾的打開和關(guān)閉。

9.7 Qt GraphicsEffect 庫

圖形效果庫是著色器效果的集合。準(zhǔn)備由 Qt 開發(fā)商制作。這是一個(gè)很好的工具集,可用于我們的應(yīng)用程序,但也是學(xué)習(xí)如何構(gòu)建著色器的重要來源。

圖形效果庫帶有一個(gè)所謂的手動(dòng)測(cè)試平臺(tái),這是一個(gè)交互式發(fā)現(xiàn)不同效果的好工具。

測(cè)試程序位于 $QTDIR/qtgraphicaleffects/tests/manual/testbed 目錄下。

graphicseffectstestbed

效果庫包含約 20 種效果。效果列表和簡(jiǎn)短描述可以在下面找到。

** 圖形效果列表 **

Graphics Effects List

以下是使用 Blur 類別中的 FastBlur 效果的示例:

import QtQuick 2.5
import QtGraphicalEffects 1.0

Rectangle {
    width: 480; height: 240
    color: '#1e1e1e'

    Row {
        anchors.centerIn: parent
        spacing: 16

        Image {
            id: sourceImage
            source: "assets/tulips.jpg"
            width: 200; height: width
            sourceSize: Qt.size(parent.width, parent.height)
            smooth: true
        }

        FastBlur {
            width: 200; height: width
            source: sourceImage
            radius: blurred?32:0
            property bool blurred: false

            Behavior on radius {
                NumberAnimation { duration: 1000 }
            }

            MouseArea {
                id: area
                anchors.fill: parent
                onClicked: parent.blurred = !parent.blurred
            }
        }
    }
}

左邊的圖像是原始圖像。單擊右側(cè)的圖像會(huì)切換模糊屬性,并在 1 秒內(nèi)將模糊半徑從 0 到 32 的動(dòng)畫效果。

fastblur

本章使用的圖片資源:

background
background@2x
bug
butterfly
coastline
fabric
fabric@2x
lighthouse
longroad
tulips

本章完,歡迎提出建議和指正翻譯問題。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 9.著色器渲染(Shader Effects) 本章的作者:jryannel ** 注意: **最新的構(gòu)建時(shí)間:2...
    趙者也閱讀 1,057評(píng)論 0 0
  • 第三章 管線一覽 本章我們會(huì)學(xué)到什么 OpenGL管線的每個(gè)階段做什么的 如果連接著色器和固定功能管線階段 如果創(chuàng)...
    葭五閱讀 6,503評(píng)論 2 18
  • 模具工程內(nèi)部銜接鉗工,發(fā)號(hào)指令。外部對(duì)接項(xiàng)目與客戶,接收任務(wù)。它是模具部的一個(gè)樞紐,也如整個(gè)模房作戰(zhàn)指揮部。 那么...
    止戈魏閱讀 448評(píng)論 0 0
  • 國(guó)王(一) 大家可不要誤會(huì),現(xiàn)代社會(huì)可不存在什么國(guó)王,英國(guó)還有,不過一般是女王,在下是一個(gè)純爺們,只是很不巧姓了一...
    郵差國(guó)閱讀 339評(píng)論 0 0

友情鏈接更多精彩內(nèi)容