OpenGL學習進程(11)第八課:顏色繪制的詳解


    本節是OpenGL學習的第八個課時,下面將詳細介紹OpenGL的顏色模式,顏色混合以及抗鋸齒。

 

    (1)顏色模式:

    OpenGL支持兩種顏色模式:一種是RGBA,一種是顏色索引模式。

    RGBA模式與索引模式的區別:

    計算機必須為每個像素保存一些數據,在RGBA模式中數據就代表了顏色;而顏色索引模式中數據代表了一個索引,要獲取真正的顏色值還需要查索引表。數據的數量是由幀緩存中的位面決定的。一個位面為一個像素的一個位的數據。假如是8位面的顏色,每個像素就有8個顏色位,因此就有2的8次方位,也就是256種不同的顏色值,或者說每個像素可以有256種顏色。位面通常分為RGB三個組成部分。

    1)RGBA:

    RGBA模式中,每一個像素會保存以下數據:R值(紅色分量)、G值(綠色分量)、B值(藍色分量)和A值(alpha分量)。其中紅、綠、藍三種顏色相組合,就可以得到我們所需要的各種顏色。而alpha不直接影響顏色,alpha通道一般用作不透明度參數。如果一個像素的alpha通道數值為0%,那它就是完全透明的,而數值為100%則意味着一個完全不透明的像素。在0%和100%之間的值則使得像素可以透過背景顯示出來,就像透過玻璃(半透明性),這種效果是簡單的二元透明性(透明或不透明)做不到的,它使數碼合成變得容易。alpha通道值可以用百分比、整數或者像RGB參數那樣用0到1的實數表示。

    PNG是一種使用RGBA的圖像格式。

    glColor*系列函數可以用於設置顏色:(常用顏色參考表:http://blog.sina.com.cn/s/blog_4e20daf60102dydy.html)

void glColor3f(GLfloat red, GLfloat green, GLfloat blue);
void glColor4f(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha);

    注意:glColor系列函數,在參數類型不同時,表示“最大”顏色的值也不同:

1.采用f和d做后綴的函數,以1.0表示最大的使用。
2.采用b做后綴的函數,以127表示最大的使用。
3.采用ub做后綴的函數,以255表示最大的使用。
4.采用s做后綴的函數,以32767表示最大的使用。
5.采用us做后綴的函數,以65535表示最大的使用。

    2)索引顏色:

    在索引顏色模式中,OpenGL需要一個顏色表。顏色表的大小是很有限的,一般在256~4096之間,且總是2的整數次冪。在使用索引顏色方式進行繪圖時,總是先設置顏色表,然后選擇顏色。顏色表中的每一項保存了一種顏色。

    使用glIndex*系列函數可以在顏色表中選擇顏色:

void glIndexi(GLint c);

    1.2.1設置顏色表

    OpenGL並直接沒有提供設置顏色表的方法,因此設置顏色表需要使用操作系統的支持。

    我們在這里僅僅做一個簡單測試,需要使用另一個OpenGL工具包:aux(已經過時)(下載:http://files.cnblogs.com/files/MenAngel/glAux.rar)

    這個工具包需要就行如下配置:

1.glaux.dll放在C:\Windows\SysWOW64;
2.GLAUX.H放(E:\VS\VC\include)下;
3.GLAUX.lib放在(E:\VS\VC\lib)下;
具體還可以參考學習進程(2)

    最后還要在VS的項目中進行如下配置:

    1.2.2設置索引顏色繪制圖形的實例:

#include <GL/glut.h>
#include <glaux.h>
#pragma comment(linker,"/subsystem:\"windows\" /entry:\"mainCRTStartup\"")

void paint(void);
void InitPalette(void);
void CALLBACK autoshape(GLsizei w, GLsizei h);
void CALLBACK display();
void DrawColorFans(void);

void paint(void)
{
    glClearColor(0.4, 0.4, 0.4, 0.0);
    glClear(GL_COLOR_BUFFER_BIT);
    glShadeModel(GL_FLAT);
}

//重載的函數,用於設置自定義索引顏色表
void InitPalette(void)
{
    GLint i;
    static GLfloat color[][3] = { { 0.0, 0.0, 1.0 }, { 0.0, 1.0, 0.0 }, { 1.0, 0.0, 0.0 },
                                { 1.0, 0.0, 1.0 }, { 1.0, 1.0, 0.0 }, { 0.0, 1.0, 1.0 },
                                { 1.0, 0.5, 0.5 }, { 0.0, 0.5, 0.5 } };
    for (i = 0; i<8; i++)
        auxSetOneColor(i + 1, color[i][0], color[i][1], color[i][2]);
}

void CALLBACK autoshape(GLsizei w, GLsizei h)
{
    glViewport(0, 0, w, h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    if (w <= h)
        glOrtho(-12.0, 12.0, -12.0*(GLfloat)h / (GLfloat)w, 12.0*(GLfloat)h / (GLfloat)w, -30.0, 30.0);
    else
        glOrtho(-12.0*(GLfloat)h / (GLfloat)w, 12.0*(GLfloat)h / (GLfloat)w, -12.0, 12.0, -30.0, 30.0);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
}

void CALLBACK display(void)
{
    InitPalette();
    DrawColorFans();
    glFlush();
}

void DrawColorFans(void)
{
    GLint n;
    //這里設置點的方式使用近似7*7*2=10*10
    GLfloat point[8][2] = { { 7.0, -7.0 }, { 0.0, -10.0 }, { -7.0, -7.0 }, { -10.0, 0.0 }, { -7.0, 7.0 }, { 0.0, 10.0 }, { 7.0, 7.0 }, { 10.0, 0.0 } };
    //繪制圖形:三角形帶
    glBegin(GL_TRIANGLE_FAN);
    glVertex2f(0.0, 0.0);
    glVertex2f(10.0, 0.0);
    for (n = 0; n<8; n++)
    {
        //從顏色表中取色
        glIndexi(n + 1);
        glVertex2fv(point[n]);
    }
    glEnd();
}

void main()
{
    //初始化繪圖模式2D無交互,和Index顏色模式
    auxInitDisplayMode(AUX_SINGLE | AUX_INDEX);
    auxInitPosition(200, 200, 500, 500);
    auxInitWindow("顏色索引");
    paint();
    //當繪制的窗體的大小發生改變時
    auxReshapeFunc(autoshape);
    //持續回調函數
    auxMainLoop(display);
}

    1.2.3清除屏幕用的顏色:

1.RGB模式:glClearColor(GL_COLOR_BUFFER_BIT)
2.索引顏色模式:glClearIndex(GLfloat i).(用法與glindexi類似)

    1.2.4指定顏色模型:

    在默認情況下,OpenGL會計算兩點頂點之間的其它點,並為它們填上“合適”的顏色,使相鄰的點的顏色值都比較接近。如果使用的是RGB模式,看起來就具有漸變的效果。如果是使用顏色索引模式,則其相鄰點的索引值是接近的,如果將顏色表中接近的項設置成接近的顏色,則看起來也是漸變的效果。但如果顏色表中接近的項顏色卻差距很大,則看起來可能是很奇怪的效果。使用glShadeModel函數可以關閉這種計算,如果頂點的顏色不同,則將頂點之間的其它點全部設置為與某一個點相同。(直線以后指定的點的顏色為准,而多邊形將以任意頂點的顏色為准,由實現決定。)為了避免這個不確定性,盡量在多邊形中使用同一種顏色。

    glShadeModel的使用方法:

glShadeModel(GL_SMOOTH);   // 平滑方式,這也是默認方式
glShadeModel(GL_FLAT);         // 單色方式

 

    (2)顏色混合:

    混合就是把兩種顏色混在一起。具體一點,就是把某一像素位置原來的顏色和將要畫上去的顏色,通過某種方式混在一起,從而實現特殊的效果。

    為什么引入顏色混合

    材料屬性和光照參數可以極大地增加圖形的逼真度,但除此之外,我們在對現實世界進行建模時,有許多效果是通過混合顏色的方式實現的。透明的物體,像是玻璃水杯,在它后面發射過來的光會與透明物體的顏色混合在一起。這種透明在OpenGL中的實現方式,是通過首先繪制背景物體,然后把前景物體(比如水杯)與顏色緩沖區中已經存在的顏色進行混合而實現的。在這一過程中,顏色的alpha值成分發揮了重要作用。

    1)實現原理:

    在一般情況下,OpenGL在渲染時把顏色值存放在顏色緩沖區中,把每個片段(像素)的深度值存放在深度緩沖區中。當深度檢測被關閉時,新的顏色值簡單地覆蓋顏色緩沖區中已經存在的顏色值;當深度檢測被打開時,新的顏色值只有當它比原來的顏色更接近臨近的裁剪平面時才會替換原來的顏色。當然,這是在OpenGL的混合功能被關閉的情況下。當混合功能被啟用時,新的顏色會與顏色緩沖區中原有的顏色進行組合。通過對這些顏色進行不同的組合,可以產生許多種不同的效果。

    注意:只有在RGBA模式下,才可以使用混合功能,顏色索引模式下是無法使用混合功能的。

目標顏色:已經存儲在顏色緩沖區中的顏色稱為目標顏色;
源顏色:當前渲染命令的結果進入顏色緩沖區中的顏色稱為源顏色;
我們正是通過對目標顏色和源顏色進行不同的組合操作,來實現顏色混合的功能

    當混合功能被啟用時,源顏色和目標顏色的組合方式是由混合方程式來控制的。在默認情況下,使用的混合方程式如下所示:

    

Cf: 是最終計算產生的顏色
Cs: 是源顏色
Cd: 是目標顏色
S:  是源混合因子
D:  是目標混合因子

    這兩個混合因子可以通過下面的這個函數進行設置:

glBlendFunc(GLenum S, GLenum D);
枚舉類型可能的值為:
GL_ZERO: 表示使用0.0作為因子,實際上相當於不使用這種顏色參與混合運算。
GL_ONE: 表示使用1.0作為因子,相當於完全的使用了這種顏色參與混合運算。
GL_SRC_ALPHA:表示使用源顏色的alpha值來作為因子。
GL_DST_ALPHA:表示使用目標顏色的alpha值來作為因子。
GL_ONE_MINUS_SRC_ALPHA:表示用1.0減去源顏色的alpha值來作為因子。
GL_ONE_MINUS_DST_ALPHA:表示用1.0減去目標顏色的alpha值來作為因子。

    示例:

    假設的四個分量(指紅色,綠色,藍色,alpha值) 源顏色是(Rs, Gs, Bs, As),目標顏色是(Rd, Gd, Bd, Ad), 又設源因子為(Sr, Sg, Sb, Sa),目標因子為(Dr, Dg, Db, Da)。 則混合產生的新顏色可以表示為:

    (Rs*Sr+Rd*Dr, Gs*Sg+Gd*Dg, Bs*Sb+Bd*Db, As*Sa+Ad*Da)

    注意:如果顏色的某一分量超過了1.0,則它會被自動截取為1.0,不需要考慮越界的問題

    2)開啟或關閉:

要使用OpenGL的混合功能:glEnable(GL_BLEND);
要關閉OpenGL的混合功能:glDisable(GL_BLEND);

    顏色混合模式繪制圖形的示例:

#include <GL/glut.h>
#include <stdlib.h>

static int leftFirst = GL_TRUE;
#pragma comment(linker,"/subsystem:\"windows\" /entry:\"mainCRTStartup\"")
/*  Initialize alpha blending function.
*/
static void init(void)
{
    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    //glBlendFunc(GL_ONE, GL_ONE);
    glShadeModel(GL_FLAT);
    glClearColor(0.0, 0.0, 0.0, 0.0);
}

static void drawLeftTriangle(void)
{
    /* draw yellow triangle on LHS of screen */

    glBegin(GL_TRIANGLES);
    glColor4f(1.0, 0.0, 0.0, 0.55);
    glVertex3f(0.1, 0.9, 0.0);
    glVertex3f(0.1, 0.1, 0.0);
    glVertex3f(0.7, 0.5, 0.0);
    glEnd();
}

static void drawRightTriangle(void)
{
    /* draw cyan triangle on RHS of screen */

    glBegin(GL_TRIANGLES);
    glColor4f(0.0, 1.0, 0.0, 0.55);
    glVertex3f(0.9, 0.9, 0.0);
    glVertex3f(0.3, 0.5, 0.0);
    glVertex3f(0.9, 0.1, 0.0);
    glEnd();
}

void display(void)
{
    glClear(GL_COLOR_BUFFER_BIT);

    if (leftFirst) {
        drawLeftTriangle();
        drawRightTriangle();
    }
    else {
        drawRightTriangle();
        drawLeftTriangle();
    }

    glFlush();
}

void reshape(int w, int h)
{
    glViewport(0, 0, (GLsizei)w, (GLsizei)h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    if (w <= h)
        gluOrtho2D(0.0, 1.0, 0.0, 1.0*(GLfloat)h / (GLfloat)w);
    else
        gluOrtho2D(0.0, 1.0*(GLfloat)w / (GLfloat)h, 0.0, 1.0);
}

void keyboard(unsigned char key, int x, int y)
{
    switch (key) {
    case 't':
    case 'T':
        leftFirst = !leftFirst;
        glutPostRedisplay();
        break;
    case 27:  /*  Escape key  */
        exit(0);
        break;
    default:
        break;
    }
}

/*  Main Loop
*  Open window with initial window size, title bar,
*  RGBA display mode, and handle input events.
*/
int main(int argc, char** argv)
{
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);
    glutInitWindowSize(400, 400);
    glutInitWindowPosition(200,200);
    glutCreateWindow("顏色混合簡單示例");
    init();
    glutReshapeFunc(reshape);
    glutKeyboardFunc(keyboard);
    glutDisplayFunc(display);
    glutMainLoop();
    return 0;
}

    3)三維混合:(http://blog.sina.com.cn/s/blog_6c8369530100m0x8.html)

    深度緩沖是這樣一段數據,它記錄了每一個像素距離觀察者有多近。在啟用深度緩沖測試的情況下,如果將要繪制的像素比原來的像素更近,則像素將被繪制。否則,像素就會被忽略掉,不進行繪制。顯示結果總是近的物體遮住遠的物體。

    3D世界的繪制順序:

1.首先繪制所有不透明的物體。如果兩個物體都是不透明的,則誰先誰后沒有關系。然后,將深度緩沖區設置為只讀。
2.接下來,繪制所有半透明的物體。如果兩個物體都是半透明的,則誰先誰后只需要根據自己的意願(注意了,先繪制的將成為“目標顏色”,后繪制的將成為“源顏色”,所以繪制的順序將會對結果造成一些影響)。
3.最后,將深度緩沖區設置為可讀可寫形式。
調用glDepthMask(GL_FALSE);可將深度緩沖區設置為只讀形式。
調用glDepthMask(GL_TRUE);可將深度緩沖區設置為可讀可寫形式。

    4)抗鋸齒的處理:

    混合功能的另一個用途是抗鋸齒的處理。

    為什么引入抗鋸齒:

    在絕大多數情況下,一個渲染片段映射到計算機屏幕上的一個像素。因為這些像素都是正方形的,所以通常我們可以清晰的看到兩種不同顏色的分界,它們就是我們通常所說的鋸齒。鋸齒的出現會讓人覺得圖像是不自然的,極為影響觀感。這種鋸齒現象是計算機所產生的圖像的弱點,這對於我們的許多要求盡可能逼真的渲染任務,都帶來了消極的影響。

    消除抗鋸齒的原理:

    為了消除圖元之間的鋸齒狀邊緣,OpenGL使用混合功能來混合片段的顏色,也就是把像素的目標顏色與周圍相鄰像素的顏色進行混合。從本質上說,在任何圖元的邊緣上,像素顏色都會稍微延伸到相鄰的像素,以此來達到平滑像素顏色的效果。

    2.4.1開啟抗鋸齒功能:

    打開抗鋸齒功能十分簡單,首先我們必須啟用混合功能,並對混合函數進行一些設置:

// 設置混合因子
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
 // 啟用混合
glEnable(GL_BLEND);

    我們還需要確保把混合方程式設置為GL_ADD的模式,這也是默認的設置。

    在啟用混合功能並選擇正確的混合方程式后,便可以調用glEnable函數對點、直線或多邊形(任何實心圖元)進行抗鋸齒處理:

 // 啟用點平滑處理
glEnable(GL_POINT_SMOOTH);
// 設置為畫質最優的方式
glHint(GL_POINT_SMOOTH_HINT, GL_NICEST);
// 啟用直線平滑處理
glEnable(GL_LINE_SMOOTH);
 // 設置為畫質最優的方式
glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
 // 啟用多邊形平滑處理
glEnable(GL_POLYGON_SMOOTH);
// 設置為畫質最優的方式
glHint(GL_POLYGON_SMOOTH_HINT, GL_NICEST);

    2.4.2抗鋸齒講解示例:(下載地址:http://files.cnblogs.com/files/MenAngel/HeadFile.zip)
    需要下載math3d.h Glee.h gltools.h,然后放在%vs%/vc/include目錄下。

    注意:此測試文件的后綴名必須為.cpp,不然math3d.h會報120個錯誤:

#include <GL/glut.h>
#include <math.h>
#include <math3d.h>
#pragma comment(linker,"/subsystem:\"windows\" /entry:\"mainCRTStartup\"")
#define SMALL_STARS 100
M3DVector2f vSmallStars[SMALL_STARS];

#define MEDIUM_STARS 40
M3DVector2f vMediumStars[MEDIUM_STARS];

#define LARGE_STARS 15
M3DVector2f vLargeStars[LARGE_STARS];

#define SCREEN_X    400
#define SCREEN_Y    300

// 渲染場景的方法
void RenderScene()
{
    int i;
    GLfloat x = 200.0f;
    GLfloat y = 150.0f;
    GLfloat r = 150.0f;
    GLfloat angle = 0.0f;

    // 清除顏色緩沖區和深度緩沖區
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    //畫分割線,分成五個視見區
    glViewport(0, 0, 400, 300);
    //多組雙頂點線段,點成對出現
    glLineWidth(2.0);
    glColor3f(1.0f, 1.0f, 0.0f);
    glBegin(GL_LINES);
    glVertex2f(0.0, 200);
    glVertex2f(400.0,200);
    glVertex2f(0.0, 100);
    glVertex2f(400.0,100);
    glVertex2f(200.0, 300);
    glVertex2f(200.0, 100);
    glEnd();
    // 設置繪圖的顏色為白色
    glColor3f(1.0f, 1.0f, 1.0f);
    //坐標范圍從(0,0)到(800,600)
    glViewport(0, 0, 400, 300);

    glViewport(0, 200, 200, 100);
    // 繪制小號的頂點
    glPointSize(1.0f);
    glBegin(GL_POINTS);
    for (i = 0; i < SMALL_STARS; ++i) {
        glVertex2fv(vSmallStars[i]);
    }
    glEnd();

    glViewport(200, 200, 200, 100);
    // 繪制中號的頂點
    glPointSize(4.0f);
    glBegin(GL_POINTS);
    for (i = 0; i< MEDIUM_STARS; i++)
        glVertex2fv(vMediumStars[i]);
    glEnd();

    glViewport(0, 100, 200, 100);
    // 繪制大號的頂點
    glPointSize(7.0f);
    glBegin(GL_POINTS);
    for (i = 0; i < LARGE_STARS; i++)
        glVertex2fv(vLargeStars[i]);
    glEnd();

    glViewport(200, 100, 200, 100);
    // 繪制三角形帶
    glBegin(GL_TRIANGLE_FAN);
    glVertex2f(x, y);//圖形中點
    for (angle = 0; angle < 2.0f * 3.141592f; angle += 0.1f){
        glVertex2f(x + (float)cos(angle) * r, y + (float)sin(angle) * r);//這些點都在以(x,y)為圓心,r為半徑的圓上
        glVertex2f(x + r, y);
    }
    glEnd();

    glViewport(0, 0, 400, 100);
    // 繪制一條線帶
    glLineWidth(4.0f);
    glBegin(GL_LINE_STRIP);
    glVertex2f(0.0f, 25.0f+100.0f);
    glVertex2f(50.0f, 100.0f + 100.0f);
    glVertex2f(100.0f, 25.0f + 100.0f);
    glVertex2f(225.0f, 125.0f + 100.0f);
    glVertex2f(300.0f, 50.0f + 100.0f);
    glVertex2f(375.0f, 100.0f + 100.0f);
    glVertex2f(460.0f, 25.0f + 100.0f);
    glVertex2f(525.0f, 100.0f + 100.0f);
    glVertex2f(600.0f, 20.0f + 100.0f);
    glVertex2f(675.0f, 70.0f + 100.0f);
    glVertex2f(750.0f, 25.0f + 100.0f);
    glVertex2f(800.0f, 90.0f + 100.0f);
    glEnd();

    // 交換緩沖區,顯示畫面
    glutSwapBuffers();
}

// 設置渲染狀態的方法
void SetupRC()
{
    int i;
    // 初始化小號頂點的位置數組
    for (i = 0; i < SMALL_STARS; ++i) {
        vSmallStars[i][0] = (GLfloat)(rand() % SCREEN_X);
        vSmallStars[i][1] = (GLfloat)(rand() % (SCREEN_Y - 100)) + 50.0f;
    }
    // 初始化中號頂點的位置數組
    for (i = 0; i < MEDIUM_STARS; i++)
    {
        vMediumStars[i][0] = (GLfloat)(rand() % SCREEN_X * 10) / 10.0f;
        vMediumStars[i][1] = (GLfloat)(rand() % (SCREEN_Y - 100)) + 50.0f;
    }
    // 初始化大號頂點的位置數組
    for (i = 0; i < LARGE_STARS; i++)
    {
        vLargeStars[i][0] = (GLfloat)(rand() % SCREEN_X * 10) / 10.0f;
        vLargeStars[i][1] = (GLfloat)(rand() % (SCREEN_Y - 100)*10.0f) / 10.0f + 50.0f;
    }
    // 設置清除顏色為黑色
    glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
}

// 處理右鍵菜單的方法
void ProcessMenu(int value)
{
    switch (value)
    {
    case 1:
        // 設置混合因子
        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
        // 啟用混合
        glEnable(GL_BLEND);
        // 啟用點平滑處理
        glEnable(GL_POINT_SMOOTH);
        // 設置為畫面最優的方式
        glHint(GL_POINT_SMOOTH_HINT, GL_NICEST);
        // 啟用直線平滑處理
        glEnable(GL_LINE_SMOOTH);
        // 設置為畫面最優的方式
        glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
        // 啟用多邊形平滑處理
        glEnable(GL_POLYGON_SMOOTH);
        // 設置為畫面最優的方式
        glHint(GL_POLYGON_SMOOTH_HINT, GL_NICEST);
        break;
    case 2:
        // 關閉混合
        glDisable(GL_BLEND);
        // 關閉點平滑處理
        glDisable(GL_POINT_SMOOTH);
        // 關閉直線平滑處理
        glDisable(GL_LINE_SMOOTH);
        // 關閉多邊形平滑處理
        glDisable(GL_POLYGON_SMOOTH);
        break;
    default:
        break;
    }
    glutPostRedisplay();
}

void ChangeSize(int w, int h)
{
    if (h == 0)
        h = 1;
    glViewport(0, 0, w, h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    //把窗體投影到寬度為400,高度為300的裁剪平面
    gluOrtho2D(0.0, SCREEN_X, 0.0, SCREEN_Y);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
}

int main(int argc, char* argv[])
{
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
    glutInitWindowSize(400, 300);
    glutCreateWindow("抗鋸齒效果示例");
    // 創建右鍵菜單
    glutCreateMenu(ProcessMenu);
    glutAddMenuEntry("Open", 1);
    glutAddMenuEntry("Close", 2);
    glutAttachMenu(GLUT_RIGHT_BUTTON);
    glutReshapeFunc(ChangeSize);
    glutDisplayFunc(RenderScene);
    SetupRC();
    glutMainLoop();

    return 0;
}

 

    (3)用燈光和混合顏色模式模擬地板反射效果:(僅作了解)

    在進行場景的繪制時,首先以上下顛倒的方式,最先繪制地板下方的球體;然后打開混合,在球體的上面繪制一層透明的地板;最后恢復顛倒后的坐標系,繪制地板上方的球體。這三部分完成后,一幅地板反射球體的幻覺畫面就完成了:

#include <GL/glut.h> 
#include <math3d.h>

#pragma comment(linker,"/subsystem:\"windows\" /entry:\"mainCRTStartup\"")
GLfloat fLightPos[4] = { -100.0f, 100.0f, 50.0f, 1.0f };
GLfloat fLightPosMirror[4] = { -100.0f, -100.0f, 50.0f, 1.0f };
GLfloat fNoLight[4] = { 0.0f, 0.0f, 0.0f, 0.0f };
GLfloat fLowLight[4] = { 0.5f, 0.5f, 0.5f, 1.0f };
GLfloat fBrightLight[4] = { 1.0f, 1.0f, 1.0f, 1.0f };

static GLfloat yRot = 0.0f;

void SetupRC()
{
    glClearColor(fLowLight[0], fLowLight[1], fLowLight[2], fLowLight[3]);

    glCullFace(GL_BACK);
    glFrontFace(GL_CCW);
    glEnable(GL_CULL_FACE);
    glEnable(GL_DEPTH_TEST);

    glLightModelfv(GL_LIGHT_MODEL_AMBIENT, fNoLight);
    glLightfv(GL_LIGHT0, GL_AMBIENT, fNoLight);
    glLightfv(GL_LIGHT0, GL_DIFFUSE, fLowLight);
    glLightfv(GL_LIGHT0, GL_SPECULAR, fBrightLight);
    glEnable(GL_LIGHTING);
    glEnable(GL_LIGHT0);

    glEnable(GL_COLOR_MATERIAL);
    glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE);
    glMateriali(GL_FRONT, GL_SHININESS, 128);
}

void DrawGround()
{
    GLfloat fExtent = 20.0f;
    GLfloat fStep = 0.5f;
    GLfloat y = 0.0f;
    GLfloat fColor;
    GLfloat iStrip, iRun;
    GLint iBounce = 0;

    glShadeModel(GL_FLAT);
    for (iStrip = -fExtent; iStrip <= fExtent; iStrip += fStep)
    {
        glBegin(GL_TRIANGLE_STRIP);
        for (iRun = fExtent; iRun >= -fExtent; iRun -= fStep)
        {
            if ((iBounce % 2) == 0)
                fColor = 1.0f;
            else
                fColor = 0.0f;

            glColor4f(fColor, fColor, fColor, 0.5f);
            glVertex3f(iStrip, y, iRun);
            glVertex3f(iStrip + fStep, y, iRun);

            iBounce++;
        }
        glEnd();
    }
    glShadeModel(GL_SMOOTH);
}

void DrawSphere()
{
    glColor3f(0.0f, 0.0f, 1.0f);
    glPushMatrix();
    glTranslatef(0.0f, 0.5f, -3.5f);
    glPushMatrix();
    //glRotatef(-yRot * 2.0f, 0.0f, 1.0f, 0.0f);
    glutSolidSphere(0.32f, 32, 9);
    glPopMatrix();


    glRotatef(yRot, 0.0f, 1.0f, 0.0f);

    glPopMatrix();
}

void RenderScene()
{
    // 清空顏色緩沖區和深度緩沖區
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    // ----------------------- 首先繪制地板下方的球體 --------------------
    // 保存矩陣狀態
    glPushMatrix();
    // 將光源0的位置擺放到地板下方
    glLightfv(GL_LIGHT0, GL_POSITION, fLightPosMirror);
    // 保存矩陣狀態
    glPushMatrix();
    // 設置順時針環繞的一面為多邊形的正面,對多邊形進行鏡像
    glFrontFace(GL_CW);
    // 在Y軸使用-1的縮放因子來反轉Y軸
    glScalef(1.0f, -1.0f, 1.0f);
    // 繪制地板下方的球體
    DrawSphere();
    // 設置逆時針環繞的一面為多邊形的正面
    glFrontFace(GL_CCW);
    // 恢復矩陣狀態
    glPopMatrix();

    // ----------------- 通過對地板設置透明的顏色混合效果,來實現反射的幻覺 -----------------
    // 關閉光照計算
    glDisable(GL_LIGHTING);
    // 打開顏色混合
    glEnable(GL_BLEND);
    // 設置混合因子
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    // 繪制地板
    DrawGround();
    // 關閉顏色混合
    glDisable(GL_BLEND);
    // 打開光照計算
    glEnable(GL_LIGHTING);

    // -------------------- 最后繪制地板上方的球體 ---------------------------
    // 將光源0的位置擺放到地板上面
    glLightfv(GL_LIGHT0, GL_POSITION, fLightPos);
    // 繪制地板上方的球體
    DrawSphere();
    // 恢復矩陣狀態
    glPopMatrix();

    // 執行緩沖區的交換
    glutSwapBuffers();
}

void TimerFunction(int value)
{
    yRot += 1.0f;
    glutPostRedisplay();
    glutTimerFunc(1, TimerFunction, 1);
}

void ChangeSize(int w, int h)
{
    GLfloat fAspect;

    if (h == 0)
        h = 1;

    glViewport(0, 0, w, h);
    fAspect = (GLfloat)w / (GLfloat)h;

    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();

    gluPerspective(35.0f, fAspect, 1.0f, 50.0f);

    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    glTranslatef(0.0f, -0.4f, 0.0f);
}

int main(int argc, char* argv[])
{
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
    glutInitWindowSize(800, 600);
    glutCreateWindow("使用混合模式模擬反射效果");
    glutReshapeFunc(ChangeSize);
    glutDisplayFunc(RenderScene);
    //glutTimerFunc(10, TimerFunction, 1);
    SetupRC();
    glutMainLoop();

    return 0;
}

 

    (4)相關知識:

    1)void glCullFace(GLenum mode):

    mode:指明多邊形的前面或后面是否被剔除。允許的符號常量有:GL_FRONT,GL_BACK和GL_FRONT_AND_BACK。初始值為GL_BACK。

    本函數可以禁用多邊形正面或背面上的光照、陰影和顏色計算及操作,消除不必要的渲染計算是因為無論對象如何進行旋轉或變換,都不會看到多邊形的背面。用GL_CULL_FACE參數調用glEnable和glDisable可以啟用或禁用剔除。

    注意:如果mode是GL_FRONT_AND_BACK,多邊形不會被繪出,但是其他圖元比如點、線會被繪出。

    用法:表示禁用多邊形正面或者背面上的光照、陰影和顏色計算及操作,消除不必要的渲染計算。 例如某對象無論如何位置變化,我們都只能看到構成其組成的多邊形的某一面時,可使用該函數。

    2)void glFrontFace(GLenum mode):

    mode指明前面多邊形的方向。可選的值有GL_CW和GL_CCW。默認值是GL_CCW。

    用法:在一個全部由不透明封閉表面組成的場景中,背面多邊形是永遠看不見的。剔除這些不可見的多邊形對於加速圖形的渲染有很大的益處。如果一個虛構的對象的頂點是按照多邊形內部順時針的方向進行繪制的,那么可以稱這個多邊形基於窗口坐標的投影是順時針的。反之,則為逆時針。glFrontFace就是用來指定多邊形在窗口坐標中的方向是逆時針還是順時針的。GL_CCW說明逆時針多邊形為正面,而GL_CW說明順時針多邊形為正面。默認是逆時針多邊形為正面。

    開啟和關閉剔除功能可以調用帶GL_CULL_FACE參數的glEnable和glDisable函數。默認剔除功能是關閉的。

    3)void glLightModelf(GLenum pname, GLfloat param)和void glLightModelx(GLenum pname, GLfixed param):

    glLightModel —— 用於設置光照模型參數。

    4.3.1pname:

    指定一個單值光照模型參數。必須是GL_LIGHT_MODEL_TWO_SIDE或者GL_LIGHT_MODEL_AMBIENT。

    GL_LIGHT_MODEL_AMBIENT:

    params包含四個定點或浮點值來指定整個場景的環境光強度。初始值是(0.2,0.2,0.2,1.0)。

    GL_LIGHT_MODEL_TWO_SIDE:

    params是一個單定點或浮點數來指定多邊形要不要進行一邊或兩邊的光照計算。它對點和線的光照計算沒有任何作用。如果params的值是0,一邊的光照被指定,前面和后面的多邊形將被賦予相同的計算顏色。如果params的值不是0,那么兩邊的光照都被指定。

    4.3.2param:

    指定要設置的參數指針。

    4)glLightfv(GL_LIGHT0, GL_POSITION, light_position):

    指定第0號光源的位置。一般支持OpenGL支持8個光照。

    這兩句是告訴OPENGL在后面的渲染中使用光照,默認情況下是關閉的:

glEnable(GL_LIGHTING); 啟用燈光
glEnable(GL_LIGHT0);啟用0號光源
glEnable(GL_COLOR_MATERIAL);啟用顏色追蹤

    5)如何使用一個光源:

glLightfv(GL_LIGHT0, GL_POSITION, light_position);//指定光源位置
glLightfv(GL_LIGHT0,GL_AMBIENT,light_ambient);    //光源中的環境光強度 
glLightfv(GL_LIGHT0,GL_DIFFUSE,white_light);      //光源中的散射光強度 
glLightfv(GL_LIGHT0,GL_SPECULAR,white_light);     //光源中的鏡面反射光強度

    6)void WINAPI glMateriali( GLenum face, GLenum pname, GLint param ):

    The glMateriali function specifies material parameters for the lighting model.

    glLightfv是光源glLightModelfv是材質。

glMaterialfv(GL_FRONT,GL_AMBIENT,mat_ambient); //材質屬性中的環境光 
glMaterialfv(GL_FRONT,GL_DIFFUSE,mat_diffuse); //材質屬性中的散射光 
glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular); //材質屬性中的鏡面反射光 
glMaterialfv(GL_FRONT, GL_SHININESS, mat_shininess); //材質屬性的鏡面反射指數 
glMaterialfv(GL_FRONT,GL_EMISSION,mat_emission); //材質屬性中的發射光

    7)void glScalef(GLfloat x, GLfloat y, GLfloat z):

    模型變換函數,用作縮放。

    模型變換的目的是設置模型的位置和方向,例如可以對模型進行旋轉、移動和縮放,或者聯合這幾種操作。

    glScalef (1.0, 2.0, 1.0);//表示y坐標值擴大兩倍,這樣原本方的物體就變成長的了。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM