1. 程式人生 > >OpenGL入門知識介紹

OpenGL入門知識介紹

Opengl最大的特點莫過於堅持做到在加入新特性的同時儘量少地與舊程式碼發生衝突。
1.API特性
(1)資料型別
GL開頭表示OpengL,函式後面使它們的最小位寬和相關描述。
注意OpenGL並沒有對指標和陣列做特殊的考慮,我們可以像下面這樣宣告一個包含10個GLshort變數的陣列:

GLshort shorts[10]

而下面這行程式碼則聲明瞭一個長度為10的指向GLdouble型別變數的指標陣列。

GLdouble * doubles[10];

(2)OpenGL錯誤
OpenGL在內部保留了一組錯誤標誌,其中每一個標誌都代表一種不同型別的錯誤。當一個錯誤發生的時候,與這個錯誤相應的狀態就會被設定。為了觀察哪些標誌被設定,可以呼叫:

Glenum glGetError(void);

該函式被呼叫的時候,這個值隨後被刪除。然後在glGetError再次被呼叫的時候將返回一個錯誤標誌或者GL_NO _ERROR。

GL_INVALID_ENUM:列舉引數超出範圍。
GL_INVALID_VALUE:數值引數超出範圍。
GL_INVALID_OPERATION:在當前狀態操作非法。
GL_OUT_OF_MEMORY:沒有足夠的記憶體執行命令。
GL_NO_ERROR:沒有錯誤出現。

(3)確認版本

const GLubyte* glGetString(GLenum name);

這個函式返回一個靜態的字串,描述GL函式庫中所請求的資訊。

(4)使用glHint獲取線索
gHint函式允許我們指定偏重視覺質量還是速度,以適應各種不同型別的操作需求。

void glHint(GLnum target, GLenum mode);

target: 希望進行修改的行為型別。mode引數告訴OpenGL我們最為關心的是什麼。

(5)Opengl狀態機
我們把這類變數的集合成為管線的狀態,狀態機是一個抽象的模型,表示一組狀態變數的集合。每個狀態變數可以有不同的值,或者只能可以開啟或者關閉。
為了開啟這型別的狀態變數,可以使用下面這個Opengl函式:

void glEnable(GLenum capability);

可以使用下面這個對應的函式,把這些變數的狀態設定為關閉。

void glDisable(GLenum capability);

如果希望對一個狀態變數進行測試,以判斷它是否已經被開啟,OpenGL還提供了一種方便的機制:

Glboolean glIsEnabled(GLenum capability);

但是並不是所有的狀態變數都只是簡單的開啟和關閉。許多OpenGL函式專門用於設定變數值。所以需要對其進行查詢的話需要以下函式:

void glGetBolleanv(GLenum pname,GLboolean *params);
void glGetDoublev(GLenum pname,GLdouble *params);
void glGetFloatv(GLenum pname,GLfloat *params);
void glGetIntegerv(GLenum pname,GLinteger *params);

以上函式都會返回單個值,或者返回一個數組把一些值存入我們指定的地址當中。

Opengl示例程式:來說明基礎的框架。


//Opengl所需要包含的引用
#pragma comment(lib,"GLTools.lib")
#include <GLTools.h>
#include <GLShaderManager.h>
#include <GL\freeglut.h>

GLBatch triangleBatch;
//使用一些簡單的著色器。可以用著色器管理器進行管理。
GLShaderManager shaderManager;


//由於在不同的環境下視窗大小變化的檢測和處理方式不同。
//使用glutReshapeFunc註冊一個回撥,供glut庫在視窗維度改變的時候使用。
void ChangeSize(int w, int h)
{
    glViewport(0, 0, w, h);
    // 四個引數:x,y,width,height。

}


//設定當前的渲染環境
void SetupRC()
{
    // 用來進行視窗清除的顏色。
    glClearColor(0.0f, 0.0f, 1.0f, 1.0f);
    //著色器管理器需要編譯和連結他自己的著色器。
    shaderManager.InitializeStockShaders();

    // 其中一個簡單的GLToole封裝類會將三角形頂點批次進行封裝。
    GLfloat vVerts[] = { -0.5f, 0.0f, 0.0f,
        0.5f, 0.0f, 0.0f,
        0.0f, 0.5f, 0.0f };
    //建立了一個三角形批次,僅僅包含三個頂點。
    triangleBatch.Begin(GL_TRIANGLES, 3);
    triangleBatch.CopyVertexData3f(vVerts);
    triangleBatch.End();
}



//真正的渲染。
void RenderScene(void)
{
    // 清除一個或者一組特定的緩衝區。
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);

    //設定一組浮點表示顏色,並且將它傳遞到儲存著色器GLT_SHADER_IDENTITY。
    GLfloat vRed[] = { 1.0f, 0.0f, 0.0f, 1.0f };
    shaderManager.UseStockShader(GLT_SHADER_IDENTITY, vRed);
    triangleBatch.Draw();
    //前面的GLUT_DOUBLE
    // 指定要一個雙緩衝區渲染環境,意味在後臺緩衝區進行渲染,然後在結束的時候交換到前臺。  
    //這種形式能夠防止觀察者看到可能伴隨著動畫幀與動畫幀之間閃爍的渲染過程。
    glutSwapBuffers();
}


///////////////////////////////////////////////////////////////////////////////  
// Main entry point for GLUT based programs  
int main(int argc, char* argv[])
{
    gltSetWorkingDirectory(argv[0]);//設定當前工作目錄。非必要。預設就是程式可執行程式相同的目錄。

    glutInit(&argc, argv);//傳輸命令列引數並且初始化GLUT庫。
    //建立視窗的時候需要使用哪種型別的顯示模式。
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH | GLUT_STENCIL);
    //GLUT_DOUBLE:雙緩衝視窗,繪圖命令實際上是在離屏緩衝區執行的,然後迅速轉換成視窗檢視。
    //GLUT_DEPTH:將一個深度緩衝區分配為顯示的一部分。這種方式用來生成動畫效果。
    glutInitWindowSize(800, 600);//設定視窗大小

    glutCreateWindow("Triangle");//設定視窗的表頭。


    //GLUT內部執行一個本地訊息迴圈,攔截適當的資訊。然後呼叫我們為不同時間註冊的回撥函式。
    //這裡必須為改變視窗大小設定一個回撥函式。
    glutReshapeFunc(ChangeSize);
    //註冊一個函式以包含OPENGL渲染程式碼。
    glutDisplayFunc(RenderScene);


    //以下程式碼需要解決兩件事情:初始化GLEW庫。重新呼叫GLEW初始化OPENGL驅動程式。
    //以確保Opengl的API對我們完全可用。
    //還要檢查確定驅動程式的初始化過程中有沒有出現任何問題。
    GLenum err = glewInit();
    if (GLEW_OK != err) {
        fprintf(stderr, "GLEW Error: %s\n", glewGetErrorString(err));
        return 1;
    }

    SetupRC();//這是一個執行中的Opengl狀態機的控制代碼。在任何Opengl函式起作用
    //前必須建立一個渲染環境。

    glutMainLoop();//開始主訊息迴圈並且結束main函式。
    return 0;
}

針對上面的程式,繼續修改一些位置,完成可以使用上下左右鍵來完成對一個正方體的控制。並且控制其碰撞檢測。同時總是更新其點的位置。

//Opengl所需要包含的引用
#pragma comment(lib,"GLTools.lib")
#include <GLTools.h>
#include <GLShaderManager.h>
#include <GL\freeglut.h>

GLBatch squareBatch;
//使用一些簡單的著色器。可以用著色器管理器進行管理。
GLShaderManager shaderManager;

//注意公開聲明當前點。
GLfloat blockSize = 0.1f;
GLfloat vVerts[] =
{ -blockSize, -blockSize, 0.0f,
blockSize, -blockSize, 0.0f,
blockSize,  blockSize, 0.0f,
-blockSize,  blockSize, 0.0f };


//由於在不同的環境下視窗大小變化的檢測和處理方式不同。
//使用glutReshapeFunc註冊一個回撥,供glut庫在視窗維度改變的時候使用。
void ChangeSize(int w, int h)
{
    glViewport(0, 0, w, h);
    // 四個引數:x,y,width,height。

}


//設定當前的渲染環境
void SetupRC()
{
    // 用來進行視窗清除的顏色。
    glClearColor(0.0f, 0.0f, 1.0f, 1.0f);
    //著色器管理器需要編譯和連結他自己的著色器。
    shaderManager.InitializeStockShaders();


    //建立了一個正方形批次。包含四個點。
    squareBatch.Begin(GL_TRIANGLE_FAN, 4);
    squareBatch.CopyVertexData3f(vVerts);
    squareBatch.End();
}



//真正的渲染。
void RenderScene(void)
{
    // 清除一個或者一組特定的緩衝區。
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);

    //設定一組浮點表示顏色,並且將它傳遞到儲存著色器GLT_SHADER_IDENTITY。
    GLfloat vRed[] = { 1.0f, 0.0f, 0.0f, 1.0f };
    shaderManager.UseStockShader(GLT_SHADER_IDENTITY, vRed);
    squareBatch.Draw();
    //前面的GLUT_DOUBLE
    // 指定要一個雙緩衝區渲染環境,意味在後臺緩衝區進行渲染,然後在結束的時候交換到前臺。  
    //這種形式能夠防止觀察者看到可能伴隨著動畫幀與動畫幀之間閃爍的渲染過程。
    glutSwapBuffers();
}

//用箭頭控制操縱正方形在螢幕範圍移動
void SpecialKeys(int key, int x, int y)
{
    GLfloat stepSize = 0.025f;

    GLfloat blockX = vVerts[0];
    GLfloat blockY = vVerts[7];

    //基本移動方式
    if (key == GLUT_KEY_UP)
    {
        blockY += stepSize;
    }
    if (key == GLUT_KEY_DOWN)
    {
        blockY -= stepSize;
    }
    if (key==GLUT_KEY_LEFT)
    {
        blockX -= stepSize;
    }
    if (key == GLUT_KEY_RIGHT)
    {
        blockX += stepSize;
    }

    //碰撞檢測,注意其正方形點的設定,所以設定成 1.0f - blockSize * 2
    if (blockX < -1.0f) blockX = -1.0f;
    if (blockX > (1.0f - blockSize * 2)) blockX = 1.0f - blockSize * 2;
    if (blockY < -1.0f + blockSize * 2)  blockY = -1.0f + blockSize * 2;
    if (blockY > 1.0f) blockY = 1.0f;

    //對正方體點的重新計算。
    //注意其中的第三個座標為z軸座標
    //並沒有什麼用,所以忽略掉。
    vVerts[0] = blockX;
    vVerts[1] = blockY - blockSize * 2;

    vVerts[3] = blockX + blockSize * 2;
    vVerts[4] = blockY - blockSize * 2;

    vVerts[6] = blockX + blockSize * 2;
    vVerts[7] = blockY;

    vVerts[9] = blockX;
    vVerts[10] = blockY;

    //注意該函式呼叫兩次。
    squareBatch.CopyVertexData3f(vVerts);

    glutPostRedisplay();

}

注意最後一個SpecialKey函式的最後一行當中的glutPostRedisplay函式。
預設情況下,在視窗建立,改變大小或者需要重新繪製的時候,GLUT通過RenderScene更新視窗。只要視窗最小化,恢復,最大化,覆蓋或者重新顯示等變化,就會產生更新。或者可以使用glutPostRedisplay函式手動更新。能夠對其進行手動渲染。