為了對著色器編程,Qt Quick提供了兩個元素。ShaderEffectSource與ShaderEffect。ShaderEffect將會使用自定義的著色器,ShaderEffectSource可以將一個QML元素渲染為一個紋理然後再渲染這個紋理。由于ShaderEffect能夠應用自定義的著色器到它的矩形幾何形狀,並且能夠使用在著色器中操作資源。一個資源可以是一個圖片,它被作為一個紋理或者著色器資源。
默認下著色器使用這個資源並且不作任何改變進行渲染。
import QtQuick 2.0
Rectangle {
width: 480; height: 240
color: '#1e1e1e'
Row {
anchors.centerIn: parent
spacing: 20
Image {
id: sourceImage
width: 80; height: width
source: 'assets/tulips.jpg'
}
ShaderEffect {
id: effect
width: 80; height: width
property variant source: sourceImage
}
ShaderEffect {
id: effect2
width: 80; height: width
// the source where the effect shall be applied to
property variant source: sourceImage
// default vertex shader code
vertexShader: "
uniform highp mat4 qt_Matrix;
attribute highp vec4 qt_Vertex;
attribute highp vec2 qt_MultiTexCoord0;
varying highp vec2 qt_TexCoord0;
void main() {
qt_TexCoord0 = qt_MultiTexCoord0;
gl_Position = qt_Matrix * qt_Vertex;
}"
// default fragment shader code
fragmentShader: "
varying highp vec2 qt_TexCoord0;
uniform sampler2D source;
uniform lowp float qt_Opacity;
void main() {
gl_FragColor = texture2D(source, qt_TexCoord0) * qt_Opacity;
}"
}
}
}
在上邊這個例子中,我們在一行中顯示了3張圖片,第一張是原始圖片,第二張使用默認的著色器渲染出來的圖片,第三張使用了Qt5源碼中默認的頂點與片段著色器的代碼進行渲染的圖片。
注意
如果你不想看到原始圖片,而只想看到被著色器渲染後的圖片,你可以設置Image為不可見(visible:false)。著色器仍然會使用圖片數據,但是圖像元素(Image Element)將不會被渲染。
讓我們仔細看看著色器代碼。
vertexShader: "
uniform highp mat4 qt_Matrix;
attribute highp vec4 qt_Vertex;
attribute highp vec2 qt_MultiTexCoord0;
varying highp vec2 qt_TexCoord0;
void main() {
qt_TexCoord0 = qt_MultiTexCoord0;
gl_Position = qt_Matrix * qt_Vertex;
}"
著色器代碼來自Qt這邊的一個字符串,綁定了頂點著色器(vertexShader)與片段著色器(fragmentShader)屬性。每個著色器代碼必須有一個main(){....}函數,它將被GPU執行。Qt已經默認提供了以qt_開頭的變量。
下面是這些變量簡短的介紹:
uniform-在處理過程中不能夠改變的值。
attribute-連接外部數據
varying-著色器之間的共享數據
highp-高精度值
lowp-低精度值
mat4-4x4浮點數(float)矩陣
vec2-包含兩個浮點數的向量
sampler2D-2D紋理
float-浮點數
可以查看OpenGL ES 2.0 API Quick Reference Card獲得更多信息。
現在我們可以更好的理解下面這些變量:
qt_Matrix:model-view-projection(模型-視圖-投影)矩陣
qt_Vertex:當前頂點坐標
qt_MultiTexCoord0:紋理坐標
qt_TexCoord0:共享紋理坐標
我們已經有可以使用的投影矩陣(projection matrix),當前頂點與紋理坐標。紋理坐標與作為資源(source)的紋理相關。在main()函數中,我們保存紋理坐標,留在後面的片段著色器中使用。每個頂點著色器都需要賦值給gl_Postion,在這裡使用項目矩陣乘以頂點,得到我們3D坐標系中的點。
片段著色器從頂點著色器中接收我們的紋理坐標,這個紋理仍然來自我們的QML資源屬性(source property)。在著色器代碼與QML之間傳遞變量是如此的簡單。此外我們的透明值,在著色器中也可以使用,變量是qt_Opacity。每 個片段著色器需要給gl_FragColor變量賦值,在這裡默認著色器代碼使用資源紋理(source texture)的像素顏色與透明值相乘。
fragmentShader: "
varying highp vec2 qt_TexCoord0;
uniform sampler2D source;
uniform lowp float qt_Opacity;
void main() {
gl_FragColor = texture2D(source, qt_TexCoord0) * qt_Opacity;
}"
在後面的例子中,我們將會展示一些簡單的著色器例子。首先我們會集中在片段著色器上,然後在回到頂點著色器上。