青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

  C++博客 :: 首頁 :: 聯(lián)系 ::  :: 管理
  163 Posts :: 4 Stories :: 350 Comments :: 0 Trackbacks

常用鏈接

留言簿(48)

我參與的團隊

搜索

  •  

積分與排名

  • 積分 - 402860
  • 排名 - 59

最新評論

閱讀排行榜

評論排行榜

歡迎來到充滿趣味的另一課。這次我將向你展示怎樣在單個窗口內(nèi)顯示多個視口。這些視口在窗口模式下能正確的調(diào)整大小。其中有兩個窗口起用了光照。窗口之一用的是正交投影而其他三個則是透視投影。為了保持教程的趣味性,在本例子中我們同樣需要學(xué)習(xí)迷宮代碼,怎么渲染到一張紋理以及怎么得到當前窗口的分辨率。
一旦你明白了本教程,制作分屏游戲以及多視圖的3D程序就很簡單了。接下來,讓我們投入到代碼中來吧!!!
你可以利用最近的NeHeGL或者IPicture代碼作為主要基本代碼。我們需要看的第一個文件就是NeHeGL.cpp,其中有三節(jié)代碼已經(jīng)被修改了。我將只列出那些被修改了的代碼。
第一個且最重要的被修改了的代碼就是ReshapeGL()函數(shù)。這是我們設(shè)置屏幕(主視口)分辨率的地方。現(xiàn)在所有的主視口設(shè)置都在畫循環(huán)里完成了。因此這兒所有我們能做的就是設(shè)置我們的主窗口。  
   

void ReshapeGL (int width, int height)                                // 當窗口移動或者大小改變時重新調(diào)整窗口
{
    glViewport (0, 0, (GLsizei)(width), (GLsizei)(height));                    // 重置當前視口
}

  
 下一步我們添加一些代碼用于監(jiān)視擦除窗口背景的Windows消息(WM_ERASEBKGND).如果它被調(diào)用,我們截取它并返回0,這樣就阻止了窗口背景被擦除,并讓我們自己來調(diào)整主窗口大小,這樣就沒有了我們以前常見的那種惱人的閃爍。如果你還不明白我的意思,刪掉 case WM_ERASEBKGND: 和 return 0; 你自己比較就能知道有何不同。 
  

LRESULT CALLBACK WindowProc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    DWORD        tickCount;                                // 保存當前的時間
    __int64        timer;                                    // 記錄時間

    // 返回窗口結(jié)構(gòu)
    GL_Window* window = (GL_Window*)(GetWindowLong (hWnd, GWL_USERDATA));

    switch (uMsg)                                        // 處理消息
    {
        case WM_ERASEBKGND:                                // 檢測Windows是否去擦除背景
            return 0;                                // 跳過直接返回

  
 在WinMain函數(shù)中,我們需要修改窗口標題并設(shè)置分辨率至1024x768.如果由于某種原因你的顯示器不能支持到1024x768,你可以設(shè)置低一點的分辨率,但是犧牲了一些細節(jié)。 
  

    window.init.width        = 1024;                            // 寬
    window.init.height        = 768;                            // 高

  
 現(xiàn)在該是對lesson42.cpp文件動手術(shù)的時候了(主要代碼)...
我們以包含標準頭文件和庫文件作為開始吧. 
  

#include <windows.h>                                       
#include <gl\gl.h>                                       
#include <gl\glu.h>                                       

#include "NeHeGL.h"                                   

#pragma comment( lib, "opengl32.lib" )                           
#pragma comment( lib, "glu32.lib" )                           

GL_Window*    g_window;                                   
Keys*        g_keys;                               
  
 然后我們聲明一些我們打算在整個程序中都要用到的全局變量。
mx和my紀錄了當前所在迷宮中的房間。每個房間都被墻隔開(因此房間都是2個單元大小的部分)。
with和height是用來建立紋理需要的。它也是迷宮的寬和高。讓迷宮和貼圖的大小一致的原因是使迷宮中的象素和紋理中的象素一一對應(yīng)。我傾向于把寬和高都設(shè)成256,盡管這要花更長的時間來建立迷宮。
如果你的顯卡能支持處理大型貼圖。可以試著以2次冪增加這個值(256, 512, 1023)。確保這個值不至于太大。如果這個主窗口的寬度有1024個象素,并且每個視口的大小都是主窗口的一半,相應(yīng)的你應(yīng)該設(shè)置你的貼圖寬度也是窗口寬度的一半。如果你使貼圖寬度為1024象素,但你的視口大小只有512,空間不足于容納貼圖中所有得象素,這樣每兩個象素就會重疊在一起。貼圖的高度也作同樣處理:高度是窗口高度的1/2. 當然你還必須四舍五入到2的冪。 
  

int    mx,my;                                            // 循環(huán)變量

const    width    = 128;                                        // 迷宮大小
const    height    = 128;                                       
  
 dong用來跟蹤迷宮是否被建完,后面有這個更詳細的解釋。
sp用來確認空格鍵是否處于按下狀態(tài)。通過按空格健,迷宮就會被重置,然后程序?qū)⒅匦麻_始畫一個新的迷宮。如果我們不去檢測空格鍵是否處于按下狀態(tài),迷宮會在空格鍵按下的瞬間被重置很多次。這個值確保迷宮只被重置一次。 
  

BOOL    done;                                            // 迷宮是否被建完
BOOL    sp;                                       
  
 r[4]保存了4個隨機的紅色分量值,g[4]保存了4個隨機的綠色分量值,b[4]保存了4個隨機的蘭色分量值。這些值賦給各個視口不同的顏色。第一個視口顏色為r[0],g[0],b[0]。請注意每一個顏色都是一個字節(jié)的值,而不是常用的浮點值。我這里用字節(jié)是因為產(chǎn)生0-255的隨機值比產(chǎn)生0.0f-1.0f的浮點值更容易。
tex_data指向我們的貼圖數(shù)據(jù)。 
  

BYTE    r[4], g[4], b[4];                                        // 隨機的顏色
BYTE    *tex_data;                                        // 保存紋理數(shù)據(jù)

  
 xrot,yrot和zrot是旋轉(zhuǎn)3d物體用到的變量。
最后,我們聲明一個二次曲面物體,這樣我們可以用gluCylinder和gluSphere來畫圓柱和球體,這比手工繪制這些物體容易多了。 
  

GLfloat    xrot, yrot, zrot;                                    // 旋轉(zhuǎn)物體

GLUquadricObj *quadric;                                    // 二次幾何體對象
  
 下面的小段代碼設(shè)置紋理中位置dmx,dmy的顏色值為純白色。tex_data是指向我們的紋理數(shù)據(jù)的指針。每一個象素都由3字節(jié)組成(1字節(jié)紅色分量,1字節(jié)綠色分量,一字節(jié)蘭色分量). 紅色分量的偏移為0,我們要修改的象素的在紋理數(shù)據(jù)中的偏移為dmx(象素的x坐標)加上dmy(象素y坐標)與貼圖寬度的乘積,最后的結(jié)果乘3(3字節(jié)每象素)。
下面第一行代碼設(shè)置red(0)顏色分量為255, 第二行設(shè)置green(1)顏色分量為255,最后一行設(shè)置blue(2)顏色分量為255,最后的結(jié)果為在dmx,dmy處的象素顏色為白色。 
  

void UpdateTex(int dmx, int dmy)                                // 更新紋理
{
    tex_data[0+((dmx+(width*dmy))*3)]=255;                        // 設(shè)置顏色為白色
    tex_data[1+((dmx+(width*dmy))*3)]=255;                           
    tex_data[2+((dmx+(width*dmy))*3)]=255;                           
}

  
 重置有相當多的工作量。它清空紋理,給每一個視口設(shè)置隨機顏色,刪除迷宮中的墻并為迷宮的生成設(shè)置新的隨機起點。
第一行代碼清空tex_data指向的貼圖數(shù)據(jù)。我們需要清空width(貼圖寬)*height(貼圖高)*3(紅,綠,蘭)。 (代碼已經(jīng)夠清楚了,嗚呼,干嗎要翻譯這段?) 清空內(nèi)存空間就是設(shè)置所有的字節(jié)為0。如果3個顏色分量都清零,那么整個貼圖就完全變黑了! 
  

void Reset (void)                                       
{
    ZeroMemory(tex_data, width * height *3);                        // 清空紋理數(shù)據(jù)

  
 現(xiàn)在我們來給每一個視口設(shè)置隨機的顏色。對于不了解這些的人來說,這里的隨機并不是真正那種隨機! 如果你寫了一個簡單的程序來打印出10個隨機數(shù)字,每次你運行程序,你都會得到同樣的10個數(shù)字。為了使事情(看起來)更加隨機,我們可以設(shè)置隨機數(shù)種子。同樣的,如果你設(shè)置種子為1,你總是會得到同樣的結(jié)果。然而,如果我們設(shè)置srand為開機后當前時鐘計數(shù)(這可能是任意的數(shù)),我們的程序每次運行都會有不同的結(jié)果。
我們有四個視口,因此我們需要從0-3的循環(huán)來處理。我們給每一個顏色(red,green,blue)從128-255中間的隨機值。要加128的目的是需要更亮的顏色。最小值為0,最大值為255,而128則表示大約有50%的亮度。  
  

    srand(GetTickCount());                                    // 初始化隨機向量

    for (int loop=0; loop<4; loop++)                            // 循環(huán)隨機生成顏色
    {
        r[loop]=rand()%128+128;                               
        g[loop]=rand()%128+128;                               
        b[loop]=rand()%128+128;                           
    }

  
 下一步,我們設(shè)置一個隨機的起點。我們的起點必須是一個房間。在紋理中每兩個象素就是一個房間。為確保起點是房間而不是墻,我們在0至貼圖寬度一半的范圍內(nèi)挑選一個數(shù),并和2相乘。通過這種方法我們只能得到如0,2,6,8之類的數(shù),也就是說我們總是得到一個隨機的房間,決不會著陸到一堵墻上如1,3,5,7,9等等。 
  

    mx=int(rand()%(width/2))*2;                               
    my=int(rand()%(height/2))*2;                               
}

  
 初始化的第一行代碼非常重要。它分配了足夠的內(nèi)存來保存我們的紋理(width*height*3). 如果你不分配內(nèi)存,你很可能使你的系統(tǒng)崩潰。 
  

BOOL Initialize (GL_Window* window, Keys* keys)                            //初始化
{
    tex_data=new BYTE[width*height*3];                            // 分配保存紋理的空間

    g_window    = window;                               
    g_keys        = keys;                               
  
 一分配完內(nèi)存,我們就調(diào)用Reset()函數(shù),Reset會清空貼圖,設(shè)置所需顏色,并為迷宮選取隨機起點。
一旦所有的東西都設(shè)置好了。我們建立我們的初始紋理。前兩個紋理參數(shù)將紋理坐標截斷在 [0,1]范圍內(nèi),當把一個單獨的圖像映射到一個物體上時,這種方式可以避免纏繞時人為因素的影響(?本句翻譯不爽,請指正). 為了看到CLAMP參數(shù)的重要性,可以移掉這兩行代碼看看。如果沒有Clamping,你會注意到在紋理的頂部和右邊的細小線條。這些線條的出現(xiàn)是因為線性過濾想使整個紋理平滑,包括紋理邊界。如果一個靠近邊界的點被畫了,在紋理的對邊上就會出現(xiàn)一條線。
我們打算用線性過濾來使紋理變的更平滑一點。 用什么類型的過濾是由你來決定的。如果它使程序跑起來很慢,那就換成過濾類型為GL_NEAREST
最后,我們利用tex_data數(shù)據(jù)(并沒有利用alpha通道)建立了一個二維的RGB紋理。 
  

    Reset();                                        // 重置紋理貼圖

    // 設(shè)置紋理參數(shù)
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, tex_data);

  
 我們設(shè)置用于清空顏色緩沖區(qū)的顏色為黑色,清空深度緩沖區(qū)的值為1.0f. 設(shè)置深度函數(shù)為less than或者equal to, 然后激活深度測試。
激活GL_COLOR_MATERIAL可以讓你在激活光照的情況下用glColor函數(shù)給物體上色。這個方法又稱為顏色追蹤, 常常是性能殺手的glMaterial的代替品。我收到?磯鄀mail問如何修改物體的顏色...,希望這些信息對這個有幫助!對于那些發(fā)email問我為什么紋理的顏色如此怪異或者問紋理顏色受當前glColor影響的人,請確認一下你沒有激活GL_COLOR_MATERIAL.
*多謝James Trotter對GL_COLOR_MATERIAL功能的解釋。我曾說過它會對你的紋理上色...實際上,它是對你的物體上色。
最后我們激活2維紋理映射。 
  

    glClearColor (0.0f, 0.0f, 0.0f, 0.0f);                           
    glClearDepth (1.0f);                                   

    glDepthFunc (GL_LEQUAL);                               
    glEnable (GL_DEPTH_TEST);                               

    glEnable(GL_COLOR_MATERIAL);

    glEnable(GL_TEXTURE_2D);

  
 下面的代碼建立了一個二次曲面物體并得到指向它的指針。一旦我們有這個指針后,我們設(shè)置它的法線類型為平滑類型,然后要求生成紋理坐標。這樣我們的光照才能正確的工作,并且我們的紋理能自動的映射到二次曲面物體。 
  

    quadric=gluNewQuadric();                               
    gluQuadricNormals(quadric, GLU_SMOOTH);                   
    gluQuadricTexture(quadric, GL_TRUE);                       
  
 Light0被激活,但是如果我們不激活光照,它不會起任何作用。Light0是預(yù)定義的燈光,方向指向屏幕內(nèi)。如果你不喜歡的話,可以手工自己設(shè)置 
  

    glEnable(GL_LIGHT0);                               

    return TRUE;
}

  
 只要你分配了內(nèi)存,記住釋放它是很重要的。不管你在全屏切換或者程序退出時,下面的代碼都將釋放了tex_data的內(nèi)存空間。 
  

void Deinitialize (void)                                   
{
    delete [] tex_data;                               
}

  
 大部分迷宮建設(shè),以及鍵盤檢測,旋轉(zhuǎn)處理等工作都是在Update()函數(shù)中完成的。
我們需要設(shè)置一個變量dir, 用它來表示記錄隨機的向上,向右,向下或向左值。
我們需要檢測空格鍵是否被按下,如果是,并且不處于按下狀態(tài),我們就重置迷宮。如果按鍵被釋放,我們設(shè)置sp為false,這樣程序就知道它不再是按下狀態(tài)。 
  

void Update (float milliseconds)                                // 更新各個參數(shù)
{
    int    dir;                                    // 保存當前的方向

    if (g_keys->keyDown [VK_ESCAPE])                            // 處理鍵盤信息
        TerminateApplication (g_window);                       

    if (g_keys->keyDown [VK_F1])                           
        ToggleFullscreen (g_window);                           

    if (g_keys->keyDown [' '] && !sp)                           
    {
        sp=TRUE;                                   
        Reset();                                   
    }

    if (!g_keys->keyDown [' '])                               
        sp=FALSE;                                   
  
 xrot,yrot和zrot通過和一些小浮點數(shù)相乘而隨著時間的消逝而增加。這樣我們可以讓物體繞x軸,y軸和z軸旋轉(zhuǎn)。每個變量都增加不同的值使旋轉(zhuǎn)好看一點 
  

    xrot+=(float)(milliseconds)*0.02f;                           
    yrot+=(float)(milliseconds)*0.03f;                           
    zrot+=(float)(milliseconds)*0.015f;                           
  
 下面的代碼用來檢測我們是否畫完了迷宮。我們開始設(shè)置done值為true, 然后循環(huán)檢查每一個房間去看是否需要增加一面墻,如果有一間房還有被訪問到,我們設(shè)置done為false.
如果tex_data[(x + (width*y))*3]的值為0, 我們就明白這個房間還沒被訪問到,而且沒有在里面沒有畫一個象素。如果這兒有一個象素,那么它的值為255。我們只需要檢查它的顏色紅色分量值。因為我們知道這個值只能為0(空)或者255(更新過) 
  

    done=TRUE;                                        // 循環(huán)所有的紋理素,如果為0則表示沒有繪制完所有的迷宮,返回
    for (int x=0; x<width; x+=2)                               
    {
        for (int y=0; y<height; y+=2)                           
        {
            if (tex_data[((x+(width*y))*3)]==0)                   
                done=FALSE;                       
        }
    }

  
 檢查完所有的房間之后,如果done為true.那么迷宮就算建完了,SetWindowsText就會改變窗口的標題。我們改變標題為"迷宮建造完成!"。然后我們停頓5000毫秒使看這個例子的人有時間來看標題欄上的字(如果在全屏狀態(tài),他們會看到動畫停頓了)。 
  

    if (done)                                        //如果完成停止五秒后重置
    {
        SetWindowText(g_window->hWnd,"Lesson 42: Multiple Viewports... 2003 NeHe Productions... Maze Complete!");
        Sleep(5000);
        SetWindowText(g_window->hWnd,"Lesson 42: Multiple Viewports... 2003 NeHe Productions... Building Maze!");
        Reset();
    }

  
 下面的代碼也許讓人看著糊涂,但其實并不難懂。我們檢查當前房間的右邊房間是否被訪問過或者是否當前位置的右邊是迷宮的右邊界(當前房間右邊的房間就不存在),同樣檢查左邊的房間是否訪問過或者是否達到左邊界。其它方向也作如此檢查。
如果房間顏色的紅色分量的值為255,就表示這個房間已經(jīng)被訪問過了(因為它已經(jīng)被函數(shù)UpdateTex更新過)。如果mx(當前x坐標)小于2, 就表示我們已經(jīng)到了迷宮最左邊不能再左了。
如果往四個方向都不能移動了或以已經(jīng)到了邊界,就給mx和my一個隨機值,然后檢查這個值對應(yīng)點是否被訪問,如果沒有,我們就重新尋找一個新的隨機變量,直到該變量對應(yīng)的單元早已經(jīng)被訪問。因為需要從舊的路徑中分叉出新的路徑,所以我們必須保持搜素知道發(fā)覺有一老的路徑可以從那里開始新的路徑。
為了使代碼盡量簡短,我沒有打算去檢查mx-2是否小于0。如果你想有100%的錯誤檢測,你可以修改這段代碼阻止訪問不屬于當前貼圖的內(nèi)存。 
  

    // 檢測是否走過這里
    if (((tex_data[(((mx+2)+(width*my))*3)]==255) || mx>(width-4)) && ((tex_data[(((mx-2)+(width*my))*3)]==255) || mx<2) &&
        ((tex_data[((mx+(width*(my+2)))*3)]==255) || my>(height-4)) && ((tex_data[((mx+(width*(my-2)))*3)]==255) || my<2))
    {
        do                                   
        {
            mx=int(rand()%(width/2))*2;                       
            my=int(rand()%(height/2))*2;                   
        }
        while (tex_data[((mx+(width*my))*3)]==0);                   
    }                                           
  
 下面這行代碼賦給dir變量0-3之間的隨機值,這個值告訴我們該往右,往上,往左還是往下畫迷宮。
在得到隨機的方向之后,我們檢查dir的值是否為0(往右移),如果是并且我們不在迷宮的右邊界,然后檢查當前房間的右邊房間,如果沒被訪問,我們就調(diào)用UpdateTex(mx+1,my)在兩個房間之間建立一堵墻,然后mx增加2移到新的房間. 
  

    dir=int(rand()%4);                                    // 隨機一個走向

    if ((dir==0) && (mx<=(width-4)))                            // 向右走,更新數(shù)據(jù)
    {
        if (tex_data[(((mx+2)+(width*my))*3)]==0)                   
        {
            UpdateTex(mx+1,my);                           
            mx+=2;                                   
        }
    }

  
 如果dir的值為1(往下),并且我們不在迷宮底部,我們檢查當前房間的下面房間是否被訪問過。如果沒被訪問過,我們就在兩個房間(當前房間和當前房間下面的房間)建立一堵墻。然后my增加2移到新的房間. 
  

    if ((dir==1) && (my<=(height-4)))                            //  向下走,更新數(shù)據(jù)
    {
        if (tex_data[((mx+(width*(my+2)))*3)]==0)                   
        {
            UpdateTex(mx,my+1);                       
            my+=2;                               
        }
    }

  
 如果dir的值為2(向左)并且我們不在左邊界,我們就檢查左邊的房間是否被訪問,如果沒被訪問,我們也在兩個房間(當前房間和左邊的房間)之間建立一堵墻,然后mx減2移到新的房間. 
  

    if ((dir==2) && (mx>=2))                                // 向左走,更新數(shù)據(jù)
    {
        if (tex_data[(((mx-2)+(width*my))*3)]==0)                   
        {
            UpdateTex(mx-1,my);                       
            mx-=2;                                   
        }
    }

  
 如果dir的值為3并且不在迷宮的最頂部,我們檢?櫚鼻胺考淶納廈媸欠癖環(huán)夢剩綣揮校蛟諏礁齜考?(當前房間和當前房間上面?zhèn)€房間)之間建立一堵墻,然后my增加2移到新的房間。 
  

    if ((dir==3) && (my>=2))                                // 向上走,更新數(shù)據(jù)
    {
        if (tex_data[((mx+(width*(my-2)))*3)]==0)                     
        {
            UpdateTex(mx,my-1);                           
            my-=2;                                   
        }
    }

  
 移到新的房間后,我們必須標志當前房間為正在訪問狀態(tài)。我們通過調(diào)用以當前位置mx, my為參數(shù)的UpdateTex()函數(shù)來達到這個目的。 
  

    UpdateTex(mx,my);                                    // 更新紋理
}

  
 這段代碼我們開始講一些新的東西...我們必須知道當前窗口的大小以便正確的調(diào)整視口的大小。為了的到當前窗口的寬和高,我們需要獲取窗口上下左右坐標值。得到這些值后我們通過窗口右邊的坐標減去左邊的坐標得到寬度值。底部坐標減去頂部坐標得到窗口的高度值。
我們用RECT結(jié)構(gòu)來得到窗口的那些值。RECT保存了一個矩形的坐標。也即矩形的左,右,頂部,底部的坐標。
為獲取窗口的屏幕坐標,我們用GetClientRect()函數(shù)。我們傳進去的第一個參數(shù)是當前窗口的句柄。第二個參數(shù)是一個結(jié)構(gòu)用于保存返回的窗口位置信息. 
  

void Draw (void)                                        // 繪制
{
    RECT    rect;                                        // 保存長方形坐標

    GetClientRect(g_window->hWnd, &rect);                            // 獲得窗口大小
    int window_width=rect.right-rect.left;                           
    int window_height=rect.bottom-rect.top;                       
  
 我們在每一幀都需要更新紋理并且要在映射紋理之前更新。更新紋理最快的方法是用命令glTexSubImage2D(). 它能把內(nèi)存中的紋理的全部或部分和屏幕中的物體建立映射。下面的代碼我們表明用的??2維紋理,紋理細節(jié)級別為0,沒有x方向(0)或y方向(0)的偏移,我們需要利用整張紋理的每一部分,圖像為GL_RGB類型,對應(yīng)的數(shù)據(jù)類型為GL_UNSIGNED_BYTE. tex_data是我們需要映射的具體數(shù)據(jù)。
這是一個非非常快的不用重建紋理而更新紋理的方法。同樣需要注意的是這個命令不會為你建立一個紋理。你必須在更新紋理前把紋理建立好。 
  

    // 設(shè)置更新的紋理
    glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_RGB, GL_UNSIGNED_BYTE, tex_data);

  
 這行代碼非常重要,它將清空整個屏幕。
……
一次性清空整個屏幕,然后在畫每一個視口前清空它們的深度存非常重要。 
  

    glClear (GL_COLOR_BUFFER_BIT);                               
  
 現(xiàn)在是主畫循環(huán)。我們要畫四個視口,所以建立了一個0到3的循環(huán)。
首先要做的事是設(shè)置用glColor3ub(r,g,b)設(shè)置當前視口的顏色。這對某些人來說不太熟悉,它跟glColor3f(r,g,b)幾乎一樣但是用無符號字節(jié)代替浮點數(shù)為參數(shù)。記住早些時候我說過參省一個0-255的隨機顏色值會更容易。好在已經(jīng)有了該命令設(shè)置正確顏色所需要的值。
glColor3f(0.5f,0.5f,0.5f)是指顏色中紅,綠,藍具有50%的亮度值。glColor3ub(127,127,127)同樣也表示同樣的意思。
如果loop的值為0,我們將選擇r[0],b[0],b[[0],如果loop指為1, 我們選用r[1],g[1],b[1]. 這樣,每個場景都有自個的隨機顏色。 
  

    for (int loop=0; loop<4; loop++)                            // 循環(huán)繪制4個視口
    {
        glColor3ub(r[loop],g[loop],b[loop]);                       
  
 在畫之前首先要做的是設(shè)置當前視口,如果loop值為0,我們畫第一個視口。我們想把第一個視口放在屏幕的左半部分(0),并且在屏幕的上半部分(window_height/2).視口的寬度為當前主窗口的一半(window_width/2), 高度也為主窗口高度的一半(window_height/2).

如果主窗口為1024x768, 結(jié)果就是一個起點坐標為0,384,寬512,高384的視口。

這個視口看起來象下面這張圖


 

設(shè)置完視口后,我們選擇當前矩陣為投影矩陣,重置它并設(shè)置為2D平行投影視圖。我們需要以平行投影視圖來填充整個視口,因此我們給左邊的值為0,右邊的值為window_width/2(跟視口一樣),同樣給底部的值賦為window_height/2,頂部的值為0. 這樣給了視口同樣的高度。

這個平行投影視圖的左上角的坐標為0,0,右下角坐標為window_width/2,window_height/2.
 
  

        if (loop==0)                                    // 繪制左上角的視口
        {
            // 設(shè)置視口區(qū)域
            glViewport (0, window_height/2, window_width/2, window_height/2);
            glMatrixMode (GL_PROJECTION);                       
            glLoadIdentity ();                           
            gluOrtho2D(0, window_width/2, window_height/2, 0);
        }

  
 如果loop的值為1, 我們是在畫第二個視口了。它在屏幕的右上部分。寬度和高度都跟前一個視圖一樣。唯一不同的是glViewport()函數(shù)的第一個參數(shù)為window_width/2.這告訴程序視口起點是從窗口左起一半的地方。

第二個視口看起來象下面這樣:

 

同樣的,我們設(shè)置當前矩陣為投影矩陣并重置它。但這次我們設(shè)置透視投影參數(shù)為FOV為45度,并且近截面值為0.1f,遠截面值為500.0f 
  

        if (loop==1)                                    // 繪制右上角視口
        {
           
            glViewport (window_width/2, window_height/2, window_width/2, window_height/2);
            glMatrixMode (GL_PROJECTION);                   
            glLoadIdentity ();                   
            gluPerspective( 45.0, (GLfloat)(width)/(GLfloat)(height), 0.1f, 500.0 );
        }

  
 I如果loop值為2,我們畫第三個視口。它將在主窗口的右下部分。寬度和高度與第二個視口一樣。跟第二個視口不同的是glViewport()函數(shù)的第二個參數(shù)為0.這告訴程序我們想讓視口位于主窗口的右下部分。

第三個視口看起來如下:


 

透視視圖的設(shè)置同第二個視圖。 
  

        if (loop==2)                                    // 繪制右下角視口
        {
            glViewport (window_width/2, 0, window_width/2, window_height/2);
            glMatrixMode (GL_PROJECTION);                       
            glLoadIdentity ();                       
            gluPerspective( 45.0, (GLfloat)(width)/(GLfloat)(height), 0.1f, 500.0 );
        }

  
 如果loop等于3,我們就畫最后一個視口(第四個視口)。它將位于窗口的左下部分。寬度和高度跟前幾次設(shè)置一樣。唯一跟第三個視口不同的是glViewport()的第一個參數(shù)為0.這告訴程序視口將在主窗口的左下部分。

第四個視口看起來如下:

 

透視投影視圖設(shè)置同第二個視口。 
  

        if (loop==3)                                    // 繪制右下角視口
        {
            glViewport (0, 0, window_width/2, window_height/2);
            glMatrixMode (GL_PROJECTION);                       
            glLoadIdentity ();                           
            gluPerspective( 45.0, (GLfloat)(width)/(GLfloat)(height), 0.1f, 500.0 );
        }

  
 下面的代碼選擇模型視圖矩陣為當前矩陣真,并重置它。然后清空深度緩存。我們在每個視口畫之前清空深度緩存。注意到我們沒有清除屏幕顏色,只是深度緩存!如果你沒有清除深度緩存,你將看到物體的部分消失了,等等,很明顯不美觀! 
  

        glMatrixMode (GL_MODELVIEW);                           
        glLoadIdentity ();                           

        glClear (GL_DEPTH_BUFFER_BIT);

  
 我們要畫的第一副圖為一個平坦的2維紋理方塊。這個方塊是在平行投影模式下畫的,并且將會覆蓋整個視口。因為我們用了平行投影投影模式,這兒沒有第三維了,因此沒必要在z軸進行變換。
記住我們第一個視口的左上角坐標維0,0,右下部分坐標為window_width/2,window_height/2.這意味我們的四邊形的右上坐標為window_width/2,0,左上坐標為0,0,左下坐標為0,window_height/2.右下坐標為window_width/2,window_height/2. 請注意在平行投影投影模式下,我們能在象素級別上處理而不是單元級別(決定于我們的視口設(shè)置) 
  

        if (loop==0)                                    // 繪制左上角的視圖
        {
            glBegin(GL_QUADS);                           
                glTexCoord2f(1.0f, 0.0f); glVertex2i(window_width/2, 0              );
                glTexCoord2f(0.0f, 0.0f); glVertex2i(0,              0              );
                glTexCoord2f(0.0f, 1.0f); glVertex2i(0,              window_height/2);
                glTexCoord2f(1.0f, 1.0f); glVertex2i(window_width/2, window_height/2);
            glEnd();                           
        }

  
 第二個要畫的圖像是一個帶光照的平滑球體。第二個視圖是帶透視的,因此我們首先必須做的是往屏幕里平移14個單位,然后在x,y,z軸旋轉(zhuǎn)物體。
我們激活光照,畫球體,然后關(guān)閉光照。這個球體半徑為4個單元長度,圍繞z軸的細分度為32,沿z軸的細分度也為32. 如果你還在犯迷糊,可以試著改變stacks或者slices的值為更小。通過減小stacks/slices的值,你就減少了球體的平滑度。
紋理坐標是自動產(chǎn)生的! 
  

        if (loop==1)                                    // 繪制右上角的視圖
        {
            glTranslatef(0.0f,0.0f,-14.0f);                       

            glRotatef(xrot,1.0f,0.0f,0.0f);                   
            glRotatef(yrot,0.0f,1.0f,0.0f);                       
            glRotatef(zrot,0.0f,0.0f,1.0f);               

            glEnable(GL_LIGHTING);                           
            gluSphere(quadric,4.0f,32,32);                   
            glDisable(GL_LIGHTING);                       
        }

  
 要畫的第三幅圖跟第一幅一樣。但是是帶透視的。它貼到屏幕有一定的角度并且有旋轉(zhuǎn)。
我們把它往屏幕里移動2個單位。然后往后傾斜那個方塊45度角。這讓方塊的頂部遠離我們,而方塊的底部則更靠近我們。
然后在z軸方向上旋轉(zhuǎn)方塊。畫方塊時,我們需要手工設(shè)置貼圖坐標。 
  

        if (loop==2)                                    // 繪制右下角的視圖
        {
            glTranslatef(0.0f,0.0f,-2.0f);                       
            glRotatef(-45.0f,1.0f,0.0f,0.0f);                   
            glRotatef(zrot/1.5f,0.0f,0.0f,1.0f);                   

            glBegin(GL_QUADS);                           
                glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f,  1.0f, 0.0f);
                glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f,  1.0f, 0.0f);
                glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, -1.0f, 0.0f);
                glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f, -1.0f, 0.0f);
            glEnd();                           
        }

  
 如果我們在畫第四副圖,我們往屏幕里移動7個單位。然后把物體繞x,y,z軸旋轉(zhuǎn)。
我們激活光照給物體一些不錯的陰影效果,然后在z軸上平移-2個單位。我們這樣做的原因是讓物體繞自己的中心旋轉(zhuǎn)而不是繞某一端。這圓柱體兩端寬1.5個單位。長度為4個單位并且繞軸上細分32個面片,沿軸細分16個面片。
為了能繞中心旋轉(zhuǎn),我們需要平移柱體長度的一半,4的一半也即是2。
在平移,旋轉(zhuǎn),然后再平移之后,我們畫圓柱體,之后關(guān)閉光照。 
  

        if (loop==3)                                    // 繪制左下角的視圖
        {
            glTranslatef(0.0f,0.0f,-7.0f);                       
            glRotatef(-xrot/2,1.0f,0.0f,0.0f);                   
            glRotatef(-yrot/2,0.0f,1.0f,0.0f);                   
            glRotatef(-zrot/2,0.0f,0.0f,1.0f);                   

            glEnable(GL_LIGHTING);                           
            glTranslatef(0.0f,0.0f,-2.0f);                       
            gluCylinder(quadric,1.5f,1.5f,4.0f,32,16);           
            glDisable(GL_LIGHTING);                           
        }
    }

  
 最后要做的事就是清空渲染管道。 
  

    glFlush ();                                   
}

  
 希望這個教程能解答所有你在做多視口中碰到的任何問題。代碼并不難懂。它幾乎跟標準的基本代碼沒什么區(qū)別。我們唯一真正修改的是視口設(shè)置是在畫的主循環(huán)中。在所有視口畫之前清空一次屏幕,然后清空各自深度緩存。
你可以用這些代碼來在各自的視口中顯示各種各樣的圖片,或在多視圖中顯示特定的物體。要做什么起決于你自己

我希望你們喜歡這個教程...如果你發(fā)現(xiàn)代碼中的任何錯誤,或者你感覺你能讓這個教程更好,請通知我(同樣的,如果你看過我的翻譯,發(fā)現(xiàn)有不當之處,請通知我)
 
 
posted on 2008-01-04 21:22 sdfasdf 閱讀(2896) 評論(0)  編輯 收藏 引用 所屬分類: OPENGL
青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            欧美一区二区三区视频| 在线一区二区三区四区五区| 国产一区再线| 另类av导航| 日韩视频免费在线观看| 久久国产88| 亚洲精品一区在线| 国产精品久久久久国产a级| 一本色道久久加勒比精品| 制服丝袜亚洲播放| 国产一区二区视频在线观看 | 亚洲精品三级| 亚洲欧美亚洲| 亚洲国产精品嫩草影院| 国产精品男人爽免费视频1| 久久久国产亚洲精品| 亚洲欧洲一区二区三区久久| 性18欧美另类| 亚洲精品免费看| 国产亚洲激情视频在线| 欧美精品免费视频| 久久―日本道色综合久久| 亚洲性感激情| 一本到高清视频免费精品| 亚洲第一网站| 欧美成人一区二区在线| 久久久综合网站| 久久激情综合网| 午夜精品免费| 欧美亚洲日本一区| 欧美一区二区三区在线观看| 亚洲一区二区三区中文字幕| 亚洲久久成人| 99国产精品久久久久久久久久| 亚洲成在人线av| 91久久视频| 亚洲视频图片小说| 欧美一区二区大片| 久久免费精品日本久久中文字幕| 久久精品在线播放| 嫩草影视亚洲| 亚洲国产日韩一区| av不卡在线| 久久不射中文字幕| 欧美va天堂在线| 欧美色图天堂网| 国外精品视频| 在线视频一区观看| 久久国产精品第一页| 欧美sm视频| 亚洲制服少妇| 欧美极品在线视频| 国产日本精品| 夜夜嗨av一区二区三区中文字幕 | 亚洲理伦电影| 麻豆精品视频在线观看| 欧美天天综合网| 亚洲国产精品传媒在线观看| 性欧美在线看片a免费观看| 欧美黄色aa电影| 欧美在线黄色| 国产日韩在线看片| 亚洲制服欧美中文字幕中文字幕| 欧美va天堂| 久久本道综合色狠狠五月| 欧美午夜国产| 亚洲午夜精品| 亚洲精品久久久久久久久久久久 | 欧美不卡福利| 亚洲国产精品成人| 久久久精品国产一区二区三区| 日韩视频免费观看高清在线视频 | 女人香蕉久久**毛片精品| 欧美亚洲视频| 国产亚洲欧美激情| 久久国产精品久久w女人spa| 中国成人亚色综合网站| 国产精品视频yy9099| 欧美一区二区视频网站| 亚洲一区免费在线观看| 国产欧美一区二区三区久久人妖| 亚洲欧美自拍偷拍| 一个人看的www久久| 国产精品私房写真福利视频| 久久精品视频导航| 欧美成人日韩| 午夜国产精品视频免费体验区| 午夜精品福利视频| 91久久精品视频| 亚洲香蕉伊综合在人在线视看| 国产一区在线播放| 欧美激情 亚洲a∨综合| 欧美日韩一区在线观看| 久久精品国产免费观看| 欧美精品1区| 久久综合九色综合欧美就去吻| 欧美激情在线有限公司| 久久国产综合精品| 欧美日本在线观看| 欧美永久精品| 久久综合狠狠综合久久激情| 亚洲欧美日韩天堂一区二区| 欧美成在线观看| 猫咪成人在线观看| 国产精品视频内| 亚洲毛片一区| 亚洲精品国产拍免费91在线| 先锋影音网一区二区| 亚洲女性裸体视频| 欧美私人网站| 亚洲一区二区精品视频| 日韩视频免费观看高清完整版| 久久精品国产免费观看| 久久福利资源站| 国产啪精品视频| 久久精品91| 欧美福利视频一区| 1769国产精品| 欧美激情一区二区三级高清视频 | aa级大片欧美| 欧美在线高清视频| 国产精品成人va在线观看| 正在播放欧美视频| 久久免费视频在线| 亚洲电影免费观看高清完整版在线| 久久这里只有| 99视频精品免费观看| 久久国产精品电影| 亚洲高清在线| 国产精品sss| 久久麻豆一区二区| 亚洲精品资源| 久久综合九色| 亚洲性xxxx| 亚洲大片一区二区三区| 欧美三级午夜理伦三级中视频| 欧美在线一区二区三区| 亚洲精选视频在线| 欧美激情视频一区二区三区免费| 中文精品视频| 亚洲电影第1页| 久久视频这里只有精品| 亚洲一区欧美激情| 亚洲精品乱码久久久久久日本蜜臀| 国产精品成人免费精品自在线观看| 久久久国产亚洲精品| 亚洲一区二区在线| 一本高清dvd不卡在线观看| 亚洲国产一区二区a毛片| 久久久亚洲精品一区二区三区| 亚洲摸下面视频| 亚洲在线一区二区三区| 艳妇臀荡乳欲伦亚洲一区| 亚洲福利视频网| 亚洲成人自拍视频| 今天的高清视频免费播放成人 | 久久婷婷综合激情| 久久精品国产一区二区三区免费看| 一区二区三区免费网站| 日韩午夜在线观看视频| 亚洲精品综合久久中文字幕| 欧美电影免费观看高清| 美女啪啪无遮挡免费久久网站| 久久久爽爽爽美女图片| 久久综合色播五月| 欧美成人视屏| 99这里只有久久精品视频| 亚洲在线播放| 久久免费视频在线| 免费视频一区| 欧美日韩精品中文字幕| 国产精品久久国产愉拍| 激情久久影院| 一区二区三区四区国产精品| 新狼窝色av性久久久久久| 久久久精品日韩欧美| 亚洲国产精品久久人人爱蜜臀| av成人国产| 久久久精品五月天| 欧美日韩一区综合| 亚洲国产一区二区三区在线播| 宅男在线国产精品| 男人的天堂亚洲在线| 亚洲欧美日韩专区| 欧美精品 日韩| 在线精品国产欧美| 久久国产精品亚洲va麻豆| 亚洲韩日在线| 久久精品99久久香蕉国产色戒| 欧美视频精品一区| 亚洲免费激情| 亚洲黄色在线观看| 久久理论片午夜琪琪电影网| 国产精品久久久久久久久动漫| 亚洲免费电影在线| 男同欧美伦乱| 久久影院午夜论| 亚洲激情图片小说视频| 欧美成人网在线| 欧美高清视频在线|