Данная статья является кратким переводом статьи "Qt Quick 2 QML Scene Graph GLSL fragment shader tutorial".
Сборка Qt с Scene Graph
Чтобы получить работающий scene graph, вы должны собрать специальную версию QT. Итак, выкачиваем и собираем:
$ git clone git://gitorious.org/qt/staging.git $ cd staging $ git checkout -b qtquick2 origin/qml-team/qtquick2 $ ./configure -developer-build -opensource -confirm-license -fast \ -nomake demos -nomake examples \ && nice make -j5
Когда это будет сделано, вы найдете qmlscene binary в staging/bin. Это просмотрщик для файлов Scene Graph QML,
Прописываем переменные окружения:
$ env -i LD_LIBRARY_PATH=$QTDIR/lib DISPLAY=:0 $QTDIR/bin/qmlscene \ whatever.qml
Первые шаги с Qt Quick 2
Написание кода в Qt Quick 2 очень похоже на написание кода в Qt Quick 1 и это неудивительно. Конечно, мы импортируем QtQuick 2.0 в начале вместо QtQuick 1.x, но кроме которого все выглядит одинаково. Давайте идти вперед и создадим новый "Qt Quick UI” проект. Получаем:
import Qt Quick 1.0
Rectangle { width: 360 height: 360 Text { anchors.centerIn: parent text: "Hello World" } MouseArea { anchors.fill: parent onClicked: { Qt.quit(); } } }
Измените импорт на версию 2.0 и запустите этот код, используя qmlscene. Он должен работать нормально. Теперь начинается самое интересное Удалите элемент Text, найдите где-нибудь веселую картинку и добавьте в код элементы Image, Item и Text, как показано ниже:
import QtQuick 2.0
Rectangle { width: 512 height: 512
MouseArea { anchors.fill: parent onClicked: { Qt.quit(); } }
Image { id: kitty source: "kitty.png" }
Item { id: scrollercontainer anchors.fill: parent
Text { id: scroller text: "Hello QtQuick world!" color: "white" font.pixelSize: 80 anchors.verticalCenter: parent.verticalCenter } } }
ShaderEffectSource
Добавим элементы ShaderEffectSource для картинки и для текста:
ShaderEffectSource { id: kitty_source sourceItem: kitty }
ShaderEffectSource { id: scroller_source sourceItem: scrollercontainer }
ShaderEffectSource - это невидимый элемент, который связывает исходный элемент с шейдером.
Первый эффект
Итак, исходные элементы подготовлены, можно подготавливать первый эффект. Для этого мы создадим ShaderEffectItem. Это - видимый элемент, который фактически является визуальным представлением оригинальной, невидимой, картинки. Реальная работа будет сделана посредством GLSL, т.е. содержанием свойства fragmentShader:
ShaderEffectItem { id: kitty_effect anchors.fill: parent
property variant source : kitty_source
fragmentShader: " varying highp vec2 qt_TexCoord0; uniform lowp sampler2D source; void main() { gl_FragColor = texture2D(source, qt_TexCoord0); }" } Шэйдер выше - простая операция передачи, в которой он показывает исходное неизменное изображение. Синатаксис схож с C, выполнение начинается с main(). То, что реально делает shader, это выбор цвета "фрагмента" (то есть пиксела) посредством чтения переменной gl_FragColor. gl_FragColor - это вектор с четырьмя компонентами в формате RGBA, все компоненты имеют значения в пределах от от 0 до 1. Для отображения оригинальной картинки фильтр должен выбирать соответствующие пиксели из исходного изображения. Это делается посредством функции texture2D(). Функция имеет два параметра: текстура и двукомпонентный вектор (x, y), указывающий на точку исходного изображения (значение - от 0 до 1).
Подробную информацию о GLSL вы можете получить здесь:
Немного движения.
ShaderEffectItem { id: kitty_effect anchors.fill: parent
property real angle : 0.0 PropertyAnimation on angle { to: 360.0 duration: 800 loops: Animation.Infinite }
property variant source : kitty_source
fragmentShader: " varying highp vec2 qt_TexCoord0; uniform lowp sampler2D source; uniform highp float angle; void main() { highp float texAngle = 0.0; if (qt_TexCoord0.x != 0.0 || qt_TexCoord0.y != 0.0) { texAngle = atan(qt_TexCoord0.y - 0.5, qt_TexCoord0.x - 0.5); } gl_FragColor = vec4(sin(texAngle + radians(angle)), sin(texAngle + radians(angle - 120.0)), sin(texAngle + radians(angle - 240.0)), 1.0) * texture2D(source, qt_TexCoord0); }" }
Усложним эффект.
ShaderEffectItem { ... fragmentShader: " ... void main() { highp float texAngle = 0.0; if (qt_TexCoord0.x != 0.0 || qt_TexCoord0.y != 0.0) { texAngle = atan(qt_TexCoord0.y - 0.5, qt_TexCoord0.x - 0.5); } highp float skew = sqrt(pow(qt_TexCoord0.x - 0.5, 2.0) + pow(qt_TexCoord0.y - 0.5, 2.0)) * 10.0; highp vec4 colorwheel = vec4(sin(texAngle + radians(angle) - skew), sin(texAngle + radians(angle - 120.0) - skew), sin(texAngle + radians(angle - 240.0) - skew), 1.0); highp vec4 texpixel = texture2D(source, qt_TexCoord0); gl_FragColor = colorwheel * texpixel; }" }
Колебания бэкграунда.
ShaderEffectItem { ... fragmentShader: " ... void main() { ... highp float wavefactor = 0.03; highp float wave_x = qt_TexCoord0.x + wavefactor * sin(radians(angle + qt_TexCoord0.x * 360.0)); highp float wave_y = qt_TexCoord0.y + wavefactor * cos(radians(angle + qt_TexCoord0.y * 360.0)); highp vec4 texpixel = texture2D(source, vec2(wave_x, wave_y)); gl_FragColor = colorwheel * texpixel; }" } Бегущая строка.
Добавим текст и заставим его скроллиться:
Text { id: scroller text: "Hello QtQuick world! Hello QtQuick world! " + "Hello QtQuick world! Hello QtQuick world! " + "Hello QtQuick world! Hello QtQuick world! " + "Hello QtQuick world! Hello QtQuick world! " color: "white" font.pixelSize: 80 anchors.verticalCenter: parent.verticalCenter PropertyAnimation on x { from: scrollercontainer.width to: -scroller.width duration: 800 * (scroller.width / 100) loops: Animation.Infinite } }
В результате должно получится примерно следующее:
Источник: http://ilkka.github.com/blog/2011/03/04/qtquick_2_scenegraph_glsl_fragment_shader_tutorial/ |