現代OpenGL+Qt學習筆記之六:繪製可旋轉、帶光照效果的三維物體

現代OpenGL+Qt學習筆記之六:繪製帶光照效果的三維物體

主要內容

  本文僅考慮最簡單的光照,即漫射光,同時在前面程序的基礎上加入多模型的鼠標控制功能。此外,爲了現實真正的三維渲染效果,本文將繪製的物體是一個如圖所示的三維的圓環體。


圓環體

漫射光

  僅僅考慮漫射光是假設物體表面只會進行漫反射,即對於到達物體表面的入射光,其反射光的強度在各個方向上都是相同的,和觀察者的位置無關。那麼怎樣計算漫射光強(也叫輻射量)呢?

漫反射

  如圖,漫反射光強的計算只和兩個向量有關:光到達曲面的法向n 和入射點到光源的方向s 。一個常識問題是,當入射光是沿着法向方向入射到物體表面上時,其其入射光是最強的;而當入射光是垂直於法向方向入射到物體表面時,其入射光強是0。實際上,當入射光的方向介於這兩個極端方向之間時,入射光強是和ns 之間的餘弦值成正比的。而餘弦值又和兩個向量之間的點乘成正比,且當兩個向量都是單位向量時,比例爲1,即兩者相等。在計算入射光強時,我們令ns 都是單位向量,假設光源的光強爲Ld 則入射光強可以表示爲:
Ldsn

  其中Ld 是一個vec3類型的向量(r,g,b) ,分別表示光的紅綠藍3個分量。3個量的取值都在0.0到1.0之間。

  當一定強度的入射光到達物體表面時,物體會對一部分的光進行吸收再反射,其反射的比例和物體本身的顏色有關,如一個純紅色的物僅會反射紅光,吸收入射光中的綠色和藍色成分。可以將該反射的比例設爲Kd ,也是一個vec3類型的向量,分別表示對光的3個成分的反射比例,通常叫做漫反射率,這是物體本身的材料屬性,所以通常也會叫做材料反射係數。那麼一定強度的光到達物體表面後,經過吸收反射的光強計算公式爲:

L=KdLdsn

示例程序

  下面是一個程序的示例,本章的程序是基於現代OpenGL+Qt學習筆記之四:使用Uniform變量實現對模型的旋轉的,但是因爲修改幅度比較大,就不做詳細介紹。這裏僅僅介紹幾個關鍵點。

圓環體

  首先在我們的程序中因爲要用到光照,再繪製前面的平面三角形就無法顯示真正的光照效果了,因此從本文開始,以後繪製的三維物體要稍微複雜一點,如圓環體。

  這裏主要定義了一個VBOTorus類,創建該類的對象,再在OpenGL部件類的渲染函數paintGL()中調用該類的render()函數即可。比較方便,還可以將模型數據和場景數據分開。有關該類的詳細實現可以查看文後的源碼。

#ifndef VBOTORUS_H
#define VBOTORUS_H
#include <QOpenGLFunctions>
#include <QOpenGLVertexArrayObject>

class QOpenGLVertexArrayObject;
class VBOTorus : protected QOpenGLFunctions
{
public:
    VBOTorus(float outerRadius, float innerRadius, int nrings, int nsides);
    ~VBOTorus();
    void render();
    int getVertexArrayHandle();
protected:
    QOpenGLVertexArrayObject vao;
private:
    int faces, rings, sides;
    void generateVerts(float * verts, float * norms, float * tex,
                       unsigned int * el,
                       float outerRadius, float innerRadius);
};

#endif // VBOTORUS_H

鼠標事件

  爲了從多個角度觀察物體的光照效果,程序還將實現對模型的控制,包括旋轉和縮放模型。實現該功能,主要是要再OpenGLWidget類中添加下列3個事件處理函數:

    void mousePressEvent(QMouseEvent *event);
    void mouseMoveEvent(QMouseEvent *event);
    void wheelEvent(QWheelEvent *event);

其中,在mousePressEvent()中我們要記錄鼠標按下時的位置,而在mouseMoveEvent()中,我們要實現在鼠標左鍵按下且鼠標移動時,控制模型旋轉,最後在wheelEvent()是在鼠標滾輪滾動時,實現物體的縮放(其實就是沿着z軸的平移,遠小近大)。再添加和旋轉、縮放相關的私有數據成員如下:

QMatrix4x4 model;
    QMatrix4x4 view;
    QMatrix4x4 projection;

    GLfloat xtrans, ytrans, ztrans; // translation on x,y,z-axis
    QVector2D mousePos;
    QQuaternion rotation;

  其中model, view, projection對應現代OpenGL+Qt學習筆記之五:OpenGL矩陣變換中介紹的模型矩陣,視圖矩陣和投影矩陣。有關函數詳細的實現和變量使用,還是要結合代碼閱讀了。這裏只介紹一個比較重要的類QQuaternion。這是Qt提供的一個4元祖類,專門用來實現常用到的旋轉操作。爲什麼是4元組呢?因爲三維物體的旋轉的確定包含旋轉角度和旋轉軸兩個屬性,而旋轉軸具有3個分量,這就是爲什麼和旋轉相關的類是4元組的原因。本示例代碼中主要用到了該類的fromAxisAndAngle()函數,該函數是通過傳遞一個QVector3D類型的旋轉軸和scalar類型的旋轉角度計算得到和該旋轉對應的4元組。

  有關該控制過程的實現,可以參考文後源碼。

光照計算

  在OpenGL主程序提供了幾何體的幾何數據、光源光強和材料反射係數數據後,光照計算在頂點着色器或片元着色器中實現。

  先看片元着色器:

#version 430

in vec3 LightIntensity;

layout( location = 0 ) out vec4 FragColor;

void main() {
    FragColor = vec4(LightIntensity, 1.0);
}

很簡單,就是通過頂點着色器輸入一個反射光強(其實就是顏色了),再在最後添加alpha分量後輸出即可。那麼具體的模型旋轉、縮放和光照計算是怎樣的呢?

#version 430

layout (location = 0) in vec3 VertexPosition;
layout (location = 1) in vec3 VertexNormal;

out vec3 LightIntensity;

uniform vec4 LightPosition; // Light position in eye coords.
uniform vec3 Kd;            // Diffuse reflectivity
uniform vec3 Ld;            // Diffuse light intensity

uniform mat4 ModelViewMatrix;
uniform mat3 NormalMatrix;
uniform mat4 ProjectionMatrix;
uniform mat4 MVP;

void main()
{
    vec3 tnorm = normalize( NormalMatrix * VertexNormal);
    vec4 eyeCoords = ModelViewMatrix * vec4(VertexPosition,1.0);
    vec3 s = normalize(vec3(LightPosition - eyeCoords));

    LightIntensity = Ld * Kd * max( dot( s, tnorm ), 0.0 );

    gl_Position = MVP * vec4(VertexPosition,1.0);
}

  
 可以看到,輸入信息必須包含頂點的位置和法向,其輸出就是計算得到的漫發射光強。有關光源信息包含光源位置LightPosition、材料反射係數Kd和光源光強Ld。和旋轉縮放變換相關的矩陣主要有模型視圖矩陣ModelViewMatrix,法向矩陣NormalMatrix和投影矩陣ProjectionMatrix,有關這些矩陣的更詳細信息參考現代OpenGL+Qt學習筆記之五:OpenGL矩陣變換。矩陣MVP是在客戶端計算的projection*view*model得到的,這樣做事爲了減少重複計算,提高效率。

  在main()函數中,首先要對法向和物體位置進行變換,然後計算點到光源的方向,注意ns 在計算漫反射光強之前都進行了單位化。

    LightIntensity = Ld * Kd * max( dot( s, n ), 0.0 );

  這是最終的漫反射光強的計算公式,對於維度相同的兩個向量的相乘,GLSL自動實現了逐元素相乘,這裏的餘弦值的計算用的是max( dot( s, n ), 0.0 ),即取sn 和0.0中的較大者,因爲當sn 爲負時,說明該點在背光處,是沒有漫反射發生的。

  這裏寫圖片描述

小結

  本文主要介紹了一種最簡單的光照理論,以及其在現代OpenGL中的實現方式。同時爲了從不同角度觀察物體的光照效果,還實現了用鼠標控制物體的旋轉和縮放。後面會介紹更加複雜一點的光照模型,使得渲染結果更加真實,還有逐片元渲染技術,可以令曲面表現更加平滑。

源碼地址:http://download.csdn.net/download/chaojiwudixiaofeixia/9988568

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章