Qt 3D的研究(五):Gooch Shader

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.0
import Qt3D.Render 2.0

Entity
{
    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函数(线性插值函数)最终得出基本的颜色,紧接着我们计算镜面反射光,让模型飞机具有光泽效果,最后将这些值添加起来,得到的是片断颜色。

       演示程序截图如下:

Qt 3D的研究(五):Gooch Shader_第1张图片

       嗯,通过调节参数来获取更好的效果,不过呢,更好的方法是定义一个颜色版纹理来得到一些更有趣的渲染效果:

Qt 3D的研究(五):Gooch Shader_第2张图片

       方法是:首先我们定义一个颜色调色版的纹理:

       然后在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: texPalette

	TextureImage
	{
		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

你可能感兴趣的:(Qt3D,探索着色器)