Qt 3D的研究(五):Gooch Shader,goochshader
Qt 3D的研究(五):Gooch Shader
Qt 3D的一個很大的優勢就是採用資料驅動的方式,將C++和GLSL使用QML來表示,動態語言的優勢盡顯。在上一次的研究中,我實現了一個非常簡單的著色器,接下來,我們可以在此基礎上,通過設定著色器的資料,製作出更加絢麗的著色效果。作為開始,我們先從幾個非真實渲染(Non-Photorealistic Rendering,NPR)開始吧。
蔣彩陽原創文章,首發地址:http://blog.csdn.net/gamesdev/article/details/44007495。歡迎同行前來探討。
首先我們開始研究Gooch著色器。Gooch著色器是Phong著色器的一個變種,一般用於技術插圖中,比如說下面這張插圖的效果就可以使用Gooch著色器完成。
接下來我們將上一篇文章的QML代碼稍微修改一下,以支援我們的Gooch著色器。下面是新增的代碼:
import Qt3D 2.0import Qt3D.Render 2.0Entity{ id: root Camera { id: camera position: Qt.vector3d( 0.0, 0.0, 40.0 ) projectionType: CameraLens.PerspectiveProjection fieldOfView: 45 aspectRatio: 16.0 / 9.0 nearPlane : 0.1 farPlane : 1000.0 upVector: Qt.vector3d( 0.0, 1.0, 0.0 ) viewCenter: Qt.vector3d( 0.0, 0.0, 0.0 ) } components: FrameGraph { ForwardRenderer { clearColor: Qt.rgba( 1.0, 0.8, 0.2, 1 ) camera: camera } } Entity { Mesh { id: mesh source: "qrc:/toyplane.obj" } Material { id: material effect: effect Effect { id: effect techniques: [ technique ] Technique { id: technique openGLFilter { api: OpenGLFilter.Desktop profile: OpenGLFilter.None majorVersion: 2 minorVersion: 0 } renderPasses: [ renderPass ] //! [5] parameters: [ Parameter { name: "lightPosition" value: camera.position }, Parameter { name: "surfaceColor" value: Qt.rgba( 0.9, 0.9, 0.9, 1 ) }, Parameter { name: "warmColor" value: Qt.rgba( 0.8, 0.3, 0.0, 1 ) }, Parameter { name: "coolColor" value: Qt.rgba( 0.0, 0.3, 0.2, 1 ) }, Parameter { name: "diffuseWarm" value: 0.4 }, Parameter { name: "diffuseCool" value: 0.6 } ] //! [5] RenderPass { id: renderPass shaderProgram: goochSP ShaderProgram { id: goochSP vertexShaderCode: loadSource( "qrc:/Gooch.vert" ) fragmentShaderCode: loadSource( "qrc:/Gooch.frag" ) } } } } } components: [ mesh, material ] } Configuration { controlledCamera: camera }}
在//! [5]中我們定義了一些需要傳入的參數。它們是lightPosition、surfaceColor、warmColor、coolColor、diffuseWarm和diffuseCool。這些是要在著色器中定義的uniform參數,所以我們必須要在QML中顯示地指定它。其實Parameter定義在哪裡,Qt 3D是有講究的。Qt 3D是這樣一個規律:Parameter定義在外層的,會覆蓋在內層的同名Parameter,也就是說,內層的Parameter作為一個預設的參數,可以通過外層的Parameter進行修改。目前,Qt3D的覆蓋優先順序是:Material→Effect→Technique→RenderPass→GLSL預設值。
介紹完了這些,讓我們看看GLSL是如何的吧。
// Gooch.vert#version 100// Qt 3D預設提供的參數attribute vec3 vertexPosition;attribute vec3 vertexNormal;uniform mat4 modelView;uniform mat4 modelNormalMatrix;uniform mat4 mvp;// 自己提供的參數uniform vec3 lightPosition;varying vec3 reflectVec;varying vec3 viewVec;varying float NdotL;void main( void ){ vec3 ecPos = ( modelView * vec4( vertexPosition, 1.0 ) ).xyz; vec3 normal = normalize( modelNormalMatrix * vec4( vertexNormal, 1.0 ) ).xyz; vec3 lightVec = normalize( lightPosition - ecPos ); reflectVec = normalize( reflect( -lightVec, normal ) ); viewVec = normalize( -ecPos ); NdotL = ( dot( lightVec, normal ) + 1.0 ) * 0.5; gl_Position = mvp * vec4( vertexPosition, 1.0 );}
頂點著色器中我們仍然像Phong光照模型一樣,求出NdotL,在片斷著色器中使用;然後我們算出reflectVec,和viewVec,作為反射和視向量的單位向量,留到片斷著色器中使用。
// Gooch.frag#version 100// 自己提供的參數uniform vec3 surfaceColor;uniform vec3 warmColor;uniform vec3 coolColor;uniform float diffuseWarm;uniform float diffuseCool;varying vec3 reflectVec;varying vec3 viewVec;varying float NdotL;void main( void ){ vec3 kcool = min( coolColor + diffuseCool * surfaceColor, 1.0 ); vec3 kwarm = min( warmColor + diffuseWarm * surfaceColor, 1.0 ); vec3 kfinal = mix( kcool, kwarm, NdotL ); float spec = max( dot( reflectVec, viewVec ), 0.0 ); spec = pow( spec, 32.0 ); gl_FragColor = vec4( min( kfinal + spec, 1.0 ), 1.0 );}
在片斷著色器中,我們定義了平面顏色、暖色調顏色、冷色調顏色以及他們相應的參數。這些參數需要我們通過mix函數(線性插值函數)最終得出基本的顏色,緊接著我們計算鏡面反射光,讓模型飛機具有光澤效果,最後將這些值添加起來,得到的是片斷顏色。
示範程式如下:
嗯,通過調節參數來擷取更好的效果,不過呢,更好的方法是定義一個顏色版紋理來得到一些更有趣的渲染效果:
方法是:首先我們定義一個顏色調色版的紋理:
然後在QML中添加這樣一個參數以及相應的紋理對象:
parameters:[Parameter{name: "lightPosition"value: camera.position},Parameter{name: "surfaceColor"value: Qt.rgba( 0.9, 0.9, 0.9, 1 )},Parameter{name: "warmColor"value: Qt.rgba( 0.8, 0.3, 0.0, 1 )},Parameter{name: "coolColor"value: Qt.rgba( 0.0, 0.3, 0.2, 1 )},Parameter{name: "diffuseWarm"value: 0.4},Parameter{name: "diffuseCool"value: 0.6},//! [6]Parameter{name: "texPalette"value: texPalette}]Texture2D{id: texPaletteTextureImage{source: "qrc:/texturePalette.png"}}//! [6]
這裡定義了Texture2D類以及TextureImage類。TextureImage類作為紋理的載入者,可以接受QImage能夠載入的圖片類型,接著作為Texture2D類的預設屬性被產生一個紋理採樣器(texture sampler2D),作為一個uniform變數載入到GLSL中。
GLSL中頂點著色器不變,片斷著色器作如下的修改:
// Gooch.frag#version 100// 自己提供的參數uniform vec3 surfaceColor;uniform vec3 warmColor;uniform vec3 coolColor;uniform float diffuseWarm;uniform float diffuseCool;varying vec3 reflectVec;varying vec3 viewVec;varying float NdotL;uniform sampler2D texPalette;void main( void ){ vec3 kcool = min( coolColor + diffuseCool * surfaceColor, 1.0 ); vec3 kwarm = min( warmColor + diffuseWarm * surfaceColor, 1.0 ); //! [6] vec3 kfinal = texture2D( texPalette, vec2( NdotL, 1.0 ) ).xyz; //! [6] float spec = max( dot( reflectVec, viewVec ), 0.0 ); spec = pow( spec, 32.0 ); gl_FragColor = vec4( min( kfinal + spec, 1.0 ), 1.0 );}
這裡添加了texturePalette這個sampler2D變數,並且修改了kfinal的值。
參考文獻:
http://www.jonatron.ca/tag/shader/
http://shiba.hpe.sh.cn/jiaoyanzu/wuli/showArticle.aspx?articleId=333&classId=4