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

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

常用鏈接

留言簿(48)

我參與的團(tuán)隊(duì)

搜索

  •  

積分與排名

  • 積分 - 402386
  • 排名 - 59

最新評(píng)論

閱讀排行榜

評(píng)論排行榜

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

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

  
 下一步我們添加一些代碼用于監(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;                                // 保存當(dāng)前的時(shí)間
    __int64        timer;                                    // 記錄時(shí)間

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

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

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

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

  
 現(xiàn)在該是對(duì)lesson42.cpp文件動(dòng)手術(shù)的時(shí)候了(主要代碼)...
我們以包含標(biāo)準(zhǔn)頭文件和庫(kù)文件作為開始吧. 
  

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

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

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

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

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

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

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

GLUquadricObj *quadric;                                    // 二次幾何體對(duì)象
  
 下面的小段代碼設(shè)置紋理中位置dmx,dmy的顏色值為純白色。tex_data是指向我們的紋理數(shù)據(jù)的指針。每一個(gè)象素都由3字節(jié)組成(1字節(jié)紅色分量,1字節(jié)綠色分量,一字節(jié)蘭色分量). 紅色分量的偏移為0,我們要修改的象素的在紋理數(shù)據(jù)中的偏移為dmx(象素的x坐標(biāo))加上dmy(象素y坐標(biāo))與貼圖寬度的乘積,最后的結(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;                           
}

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

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

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

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

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

  
 下一步,我們?cè)O(shè)置一個(gè)隨機(jī)的起點(diǎn)。我們的起點(diǎn)必須是一個(gè)房間。在紋理中每?jī)蓚€(gè)象素就是一個(gè)房間。為確保起點(diǎn)是房間而不是墻,我們?cè)?至貼圖寬度一半的范圍內(nèi)挑選一個(gè)數(shù),并和2相乘。通過這種方法我們只能得到如0,2,6,8之類的數(shù),也就是說我們總是得到一個(gè)隨機(jī)的房間,決不會(huì)著陸到一堵墻上如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會(huì)清空貼圖,設(shè)置所需顏色,并為迷宮選取隨機(jī)起點(diǎn)。
一旦所有的東西都設(shè)置好了。我們建立我們的初始紋理。前兩個(gè)紋理參數(shù)將紋理坐標(biāo)截?cái)嘣?[0,1]范圍內(nèi),當(dāng)把一個(gè)單獨(dú)的圖像映射到一個(gè)物體上時(shí),這種方式可以避免纏繞時(shí)人為因素的影響(?本句翻譯不爽,請(qǐng)指正). 為了看到CLAMP參數(shù)的重要性,可以移掉這兩行代碼看看。如果沒有Clamping,你會(huì)注意到在紋理的頂部和右邊的細(xì)小線條。這些線條的出現(xiàn)是因?yàn)榫€性過濾想使整個(gè)紋理平滑,包括紋理邊界。如果一個(gè)靠近邊界的點(diǎn)被畫了,在紋理的對(duì)邊上就會(huì)出現(xiàn)一條線。
我們打算用線性過濾來使紋理變的更平滑一點(diǎn)。 用什么類型的過濾是由你來決定的。如果它使程序跑起來很慢,那就換成過濾類型為GL_NEAREST
最后,我們利用tex_data數(shù)據(jù)(并沒有利用alpha通道)建立了一個(gè)二維的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);

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

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

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

    glEnable(GL_LIGHT0);                               

    return TRUE;
}

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

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

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

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

    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通過和一些小浮點(diǎn)數(shù)相乘而隨著時(shí)間的消逝而增加。這樣我們可以讓物體繞x軸,y軸和z軸旋轉(zhuǎn)。每個(gè)變量都增加不同的值使旋轉(zhuǎn)好看一點(diǎn) 
  

    xrot+=(float)(milliseconds)*0.02f;                           
    yrot+=(float)(milliseconds)*0.03f;                           
    zrot+=(float)(milliseconds)*0.015f;                           
  
 下面的代碼用來檢測(cè)我們是否畫完了迷宮。我們開始設(shè)置done值為true, 然后循環(huán)檢查每一個(gè)房間去看是否需要增加一面墻,如果有一間房還有被訪問到,我們?cè)O(shè)置done為false.
如果tex_data[(x + (width*y))*3]的值為0, 我們就明白這個(gè)房間還沒被訪問到,而且沒有在里面沒有畫一個(gè)象素。如果這兒有一個(gè)象素,那么它的值為255。我們只需要檢查它的顏色紅色分量值。因?yàn)槲覀冎肋@個(gè)值只能為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就會(huì)改變窗口的標(biāo)題。我們改變標(biāo)題為"迷宮建造完成!"。然后我們停頓5000毫秒使看這個(gè)例子的人有時(shí)間來看標(biāo)題欄上的字(如果在全屏狀態(tài),他們會(huì)看到動(dòng)畫停頓了)。 
  

    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();
    }

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

    // 檢測(cè)是否走過這里
    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之間的隨機(jī)值,這個(gè)值告訴我們?cè)撏遥希筮€是往下畫迷宮。
在得到隨機(jī)的方向之后,我們檢查dir的值是否為0(往右移),如果是并且我們不在迷宮的右邊界,然后檢查當(dāng)前房間的右邊房間,如果沒被訪問,我們就調(diào)用UpdateTex(mx+1,my)在兩個(gè)房間之間建立一堵墻,然后mx增加2移到新的房間. 
  

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

    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(往下),并且我們不在迷宮底部,我們檢查當(dāng)前房間的下面房間是否被訪問過。如果沒被訪問過,我們就在兩個(gè)房間(當(dāng)前房間和當(dāng)前房間下面的房間)建立一堵墻。然后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(向左)并且我們不在左邊界,我們就檢查左邊的房間是否被訪問,如果沒被訪問,我們也在兩個(gè)房間(當(dāng)前房間和左邊的房間)之間建立一堵墻,然后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)夢(mèng)剩綣揮校蛟諏礁齜考?(當(dāng)前房間和當(dāng)前房間上面?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;                                   
        }
    }

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

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

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

void Draw (void)                                        // 繪制
{
    RECT    rect;                                        // 保存長(zhǎng)方形坐標(biāo)

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

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

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

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

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

如果主窗口為1024x768, 結(jié)果就是一個(gè)起點(diǎn)坐標(biāo)為0,384,寬512,高384的視口。

這個(gè)視口看起來象下面這張圖


 

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

這個(gè)平行投影視圖的左上角的坐標(biāo)為0,0,右下角坐標(biāo)為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, 我們是在畫第二個(gè)視口了。它在屏幕的右上部分。寬度和高度都跟前一個(gè)視圖一樣。唯一不同的是glViewport()函數(shù)的第一個(gè)參數(shù)為window_width/2.這告訴程序視口起點(diǎn)是從窗口左起一半的地方。

第二個(gè)視口看起來象下面這樣:

 

同樣的,我們?cè)O(shè)置當(dāng)前矩陣為投影矩陣并重置它。但這次我們?cè)O(shè)置透視投影參數(shù)為FOV為45度,并且近截面值為0.1f,遠(yuǎn)截面值為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,我們畫第三個(gè)視口。它將在主窗口的右下部分。寬度和高度與第二個(gè)視口一樣。跟第二個(gè)視口不同的是glViewport()函數(shù)的第二個(gè)參數(shù)為0.這告訴程序我們想讓視口位于主窗口的右下部分。

第三個(gè)視口看起來如下:


 

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

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

第四個(gè)視口看起來如下:

 

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

        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 );
        }

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

        glMatrixMode (GL_MODELVIEW);                           
        glLoadIdentity ();                           

        glClear (GL_DEPTH_BUFFER_BIT);

  
 我們要畫的第一副圖為一個(gè)平坦的2維紋理方塊。這個(gè)方塊是在平行投影模式下畫的,并且將會(huì)覆蓋整個(gè)視口。因?yàn)槲覀冇昧似叫型队巴队澳J剑@兒沒有第三維了,因此沒必要在z軸進(jìn)行變換。
記住我們第一個(gè)視口的左上角坐標(biāo)維0,0,右下部分坐標(biāo)為window_width/2,window_height/2.這意味我們的四邊形的右上坐標(biāo)為window_width/2,0,左上坐標(biāo)為0,0,左下坐標(biāo)為0,window_height/2.右下坐標(biāo)為window_width/2,window_height/2. 請(qǐng)注意在平行投影投影模式下,我們能在象素級(jí)別上處理而不是單元級(jí)別(決定于我們的視口設(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();                           
        }

  
 第二個(gè)要畫的圖像是一個(gè)帶光照的平滑球體。第二個(gè)視圖是帶透視的,因此我們首先必須做的是往屏幕里平移14個(gè)單位,然后在x,y,z軸旋轉(zhuǎn)物體。
我們激活光照,畫球體,然后關(guān)閉光照。這個(gè)球體半徑為4個(gè)單元長(zhǎng)度,圍繞z軸的細(xì)分度為32,沿z軸的細(xì)分度也為32. 如果你還在犯迷糊,可以試著改變stacks或者slices的值為更小。通過減小stacks/slices的值,你就減少了球體的平滑度。
紋理坐標(biāo)是自動(dòng)產(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)。
我們把它往屏幕里移動(dòng)2個(gè)單位。然后往后傾斜那個(gè)方塊45度角。這讓方塊的頂部遠(yuǎn)離我們,而方塊的底部則更靠近我們。
然后在z軸方向上旋轉(zhuǎn)方塊。畫方塊時(shí),我們需要手工設(shè)置貼圖坐標(biāo)。 
  

        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();                           
        }

  
 如果我們?cè)诋嫷谒母眻D,我們往屏幕里移動(dòng)7個(gè)單位。然后把物體繞x,y,z軸旋轉(zhuǎn)。
我們激活光照給物體一些不錯(cuò)的陰影效果,然后在z軸上平移-2個(gè)單位。我們這樣做的原因是讓物體繞自己的中心旋轉(zhuǎn)而不是繞某一端。這圓柱體兩端寬1.5個(gè)單位。長(zhǎng)度為4個(gè)單位并且繞軸上細(xì)分32個(gè)面片,沿軸細(xì)分16個(gè)面片。
為了能繞中心旋轉(zhuǎn),我們需要平移柱體長(zhǎng)度的一半,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 ();                                   
}

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

我希望你們喜歡這個(gè)教程...如果你發(fā)現(xiàn)代碼中的任何錯(cuò)誤,或者你感覺你能讓這個(gè)教程更好,請(qǐng)通知我(同樣的,如果你看過我的翻譯,發(fā)現(xiàn)有不當(dāng)之處,請(qǐng)通知我)
 
 
posted on 2008-01-04 21:22 sdfasdf 閱讀(2890) 評(píng)論(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黄| 午夜精品一区二区三区电影天堂| 麻豆成人av| 麻豆久久久9性大片| 国产亚洲精品aa午夜观看| 亚洲精品小视频| 亚洲另类在线视频| 欧美网站在线观看| 欧美在线观看一区二区| 欧美日韩国语| 一区二区三区鲁丝不卡| 欧美在线黄色| 狠狠做深爱婷婷久久综合一区 | 亚洲图色在线| 久久成人综合网| 亚洲国产网站| 国产精品色网| 免费亚洲电影| 亚洲在线观看视频网站| 美女视频网站黄色亚洲| 在线中文字幕日韩| 激情欧美一区二区三区| 免费成人av| 亚洲精品乱码久久久久| 国产精品久久久久久久久久ktv| 欧美一级成年大片在线观看| 亚洲高清毛片| 欧美怡红院视频| 正在播放亚洲一区| 在线观看国产精品网站| 国产精品一区亚洲| 欧美日韩在线播放三区| 欧美成人精品不卡视频在线观看| 一区二区三区高清在线观看| 欧美高清免费| 久久一区二区三区av| 午夜精品影院| 亚洲综合欧美| 亚洲你懂的在线视频| 一区二区国产在线观看| 亚洲国产cao| 永久免费精品影视网站| 国产裸体写真av一区二区| 亚洲视频你懂的| 日韩午夜激情av| 亚洲最新色图| 亚洲精品美女久久久久| 激情欧美亚洲| 亚洲精品一区二| 亚洲手机视频| 欧美在线视频免费| 欧美一区二区精品在线| 久久精品主播| 老色鬼精品视频在线观看播放| 欧美一区二区观看视频| 卡一卡二国产精品| 欧美国产三区| 亚洲最新色图| 欧美一区在线视频| 媚黑女一区二区| 欧美日韩情趣电影| 国产精品久久一区二区三区| 国产精品一二三视频| 欧美在线免费观看| 欧美精品日日鲁夜夜添| 国产精品腿扒开做爽爽爽挤奶网站| 国产日韩欧美在线| 日韩午夜在线播放| 性8sex亚洲区入口| 亚洲丰满在线| 亚洲宅男天堂在线观看无病毒| 久久精品亚洲一区二区三区浴池| 女同一区二区| 国产日韩精品视频一区| 日韩一级成人av| 欧美va天堂va视频va在线| 99在线精品视频| 久久精品最新地址| 国产欧美69| 一区二区三区精品| 91久久精品日日躁夜夜躁欧美| 亚洲欧美日韩视频一区| 欧美另类99xxxxx| 亚洲韩国日本中文字幕| 久热精品视频在线观看一区| 亚洲午夜久久久| 欧美尤物巨大精品爽| 亚洲欧美成aⅴ人在线观看| 欧美精品www| 怡红院精品视频| 久久精品国产精品亚洲综合| 99日韩精品| 国产精品黄色| 亚洲一区在线视频| 亚洲视频在线视频| 欧美小视频在线| 亚洲综合欧美| 欧美一区二区视频观看视频| 国产欧美日韩综合一区在线观看 | 性欧美长视频| 国产亚洲精品久久久久婷婷瑜伽| 欧美制服第一页| 久久精品女人的天堂av| 91久久精品国产91性色| 一区二区欧美日韩| 亚洲永久精品大片| 国内精品久久久久久久97牛牛| 久久免费少妇高潮久久精品99| 新狼窝色av性久久久久久| 亚洲国产成人av| 一卡二卡3卡四卡高清精品视频| 国产精品av一区二区| 国产欧美一区二区三区视频| 久久综合精品国产一区二区三区| 免费日韩av| 久久大综合网| 欧美日韩一区二区视频在线 | 久久久欧美精品| 媚黑女一区二区| 亚洲欧美日韩在线综合| 免费成人高清视频| 久久精品人人做人人爽| 免费高清在线一区| 快播亚洲色图| 国产精品一区二区久激情瑜伽| 亚洲国产婷婷香蕉久久久久久| 国产一区二区三区的电影| 99精品视频网| 国产精品最新自拍| 欧美第一黄色网| 亚洲国产电影| 免费成人毛片| 亚洲高清久久| 亚洲精品在线观看视频| 欧美r片在线| 亚洲国产欧美另类丝袜| 91久久亚洲| 欧美国产一区二区三区激情无套| 蜜桃av一区二区| 91久久久久久久久| 欧美激情一区二区三区四区| 亚洲国语精品自产拍在线观看| 在线视频国产日韩| 欧美国产精品专区| 亚洲精品乱码久久久久久久久 | 99re66热这里只有精品3直播 | 日韩一级免费| 一区二区电影免费在线观看| 欧美高清在线一区| 一区二区三区免费看| 欧美中日韩免费视频| 伊人久久久大香线蕉综合直播| 久久午夜精品一区二区| 亚洲精品一线二线三线无人区| 国产精品99久久久久久www| 国产精品自拍视频| 久久男人av资源网站| 日韩午夜一区| 久久免费国产精品1| 夜夜嗨av一区二区三区中文字幕| 国产精品腿扒开做爽爽爽挤奶网站| 久久久久久一区二区| 9国产精品视频| 欧美国产视频一区二区| 午夜一区不卡| 亚洲综合99| 日韩一级免费| 91久久精品国产| 激情婷婷亚洲| 国户精品久久久久久久久久久不卡| 欧美福利一区二区| 美女91精品| 午夜精品久久久久久久久久久久久| 午夜视频在线观看一区| 国产精品第2页| 欧美福利电影网| 亚洲精品四区| 亚洲国产91色在线| 伊人色综合久久天天| 国产日产精品一区二区三区四区的观看方式 | 欧美成人激情在线| 久久亚洲综合色一区二区三区| 香蕉亚洲视频| 久久欧美中文字幕| 麻豆久久婷婷| 欧美视频二区| 国产精品女主播| 国产午夜精品久久久久久久| 国产精品视频yy9099| 国产性做久久久久久| 日韩一级大片在线| 亚洲视频在线观看三级| 亚洲自啪免费| 六月婷婷久久| 99在线精品视频在线观看| 99v久久综合狠狠综合久久| 一本综合精品| 久久激情婷婷| 欧美日韩亚洲一区二区三区| 国产精品视频网址|