乡下人产国偷v产偷v自拍,国产午夜片在线观看,婷婷成人亚洲综合国产麻豆,久久综合给合久久狠狠狠9

  • <output id="e9wm2"></output>
    <s id="e9wm2"><nobr id="e9wm2"><ins id="e9wm2"></ins></nobr></s>

    • 分享

      NeHe OpenGL第十課:3D世界

       小乙vk 2014-10-20

      NeHe OpenGL第十課:3D世界

      加載3D世界,并在其中漫游:

      在這一課中,你將學(xué)會如何加載3D世界,并在3D世界中漫游。這一課使用第一課的代碼,當(dāng)然在課程說明中我只介紹改變了代碼。

      這一課是由Lionel Brits (βtelgeuse)所寫的。在本課中我們只對增加的代碼做解釋。當(dāng)然只添加課程中所寫的代碼,程序是不會運(yùn)行的。如果您有興趣知道下面的每一行代碼是如何運(yùn)行的話,請下載完整的源碼

      ,并在瀏覽這一課的同時,對源碼進(jìn)行跟蹤。
      好了現(xiàn)在歡迎來到名不見經(jīng)傳的第十課。到現(xiàn)在為止,您應(yīng)該有能力創(chuàng)建一個旋轉(zhuǎn)的立方體或一群星星了,對3D編程也應(yīng)該有些感覺了吧?但還是請等一下!不要立馬沖動地要開始寫個Quake
      IV,好不好...:)。只靠旋轉(zhuǎn)的立方體還很難來創(chuàng)造一個可以決一死戰(zhàn)的酷斃了的對手....:)?,F(xiàn)在這些日子您所需要的是一個大一點(diǎn)的、更復(fù)雜些的、動態(tài)3D世界,它帶有空間的六自由度和花哨的效果如鏡像、入口

      、扭曲等等,當(dāng)然還要有更快的幀顯示速度。這一課就要解釋一個基本的3D世界"結(jié)構(gòu)",以及如何在這個世界里游走。
      數(shù)據(jù)結(jié)構(gòu)
      當(dāng)您想要使用一系列的數(shù)字來完美的表達(dá)3D環(huán)境時,隨著環(huán)境復(fù)雜度的上升,這個工作的難度也會隨之上升。出于這個原因,我們必須將數(shù)據(jù)歸類,使其具有更多的可操作性風(fēng)格。在程序清單頭部出現(xiàn)了sector(區(qū)段)

      的定義。每個3D世界基本上可以看作是sector(區(qū)段)的集合。一個sector(區(qū)段)可以是一個房間、一個立方體、或者任意一個閉合的區(qū)間。 

      typedef struct tagSECTOR      // 創(chuàng)建Sector區(qū)段結(jié)構(gòu)
      {
       int numtriangles;      // Sector中的三角形個數(shù)
       TRIANGLE* triangle;      // 指向三角數(shù)組的指針
      } SECTOR;        // 命名為SECTOR

      一個sector(區(qū)段)包含了一系列的多邊形,所以下一個目標(biāo)就是triangle(我們將只用三角形,這樣寫代碼更容易些)。 
        
      typedef struct tagTRIANGLE      // 創(chuàng)建Triangle三角形結(jié)構(gòu)
      {
       VERTEX vertex[3];      // VERTEX矢量數(shù)組,大小為3
      } TRIANGLE;        // 命名為 TRIANGLE

      三角形本質(zhì)上是由一些(兩個以上)頂點(diǎn)組成的多邊形,頂點(diǎn)同時也是我們的最基本的分類單位。頂點(diǎn)包含了OpenGL真正感興趣的數(shù)據(jù)。我們用3D空間中的坐標(biāo)值(x,y,z)以及它們的紋理坐標(biāo)(u,v)來定義三角形的每

      個頂點(diǎn)。 
        
      typedef struct tagVERTEX      // 創(chuàng)建Vertex頂點(diǎn)結(jié)構(gòu)
      {
       float x, y, z;       // 3D 坐標(biāo)
       float u, v;       // 紋理坐標(biāo)
      } VERTEX;        // 命名為VERTEX

      載入文件
      在程序內(nèi)部直接存儲數(shù)據(jù)會讓程序顯得太過死板和無趣。從磁盤上載入世界資料,會給我們帶來更多的彈性,可以讓我們體驗不同的世界,而不用被迫重新編譯程序。另一個好處就是用戶可以切換世界資料并修改它們而

      無需知道程序如何讀入輸出這些資料的。數(shù)據(jù)文件的類型我們準(zhǔn)備使用文本格式。這樣編輯起來更容易,寫的代碼也更少。等將來我們也許會使用二進(jìn)制文件。

      問題是,怎樣才能從文件中取得數(shù)據(jù)資料呢?首先,創(chuàng)建一個叫做SetupWorld()的新函數(shù)。把這個文件定義為filein,并且使用只讀方式打開文件。我們必須在使用完畢之后關(guān)閉文件。大家一起來看看現(xiàn)在的代碼:

      // 先前的定義: char* worldfile = "data\\world.txt";
      void SetupWorld()       // 設(shè)置我們的世界
      {
       FILE *filein;       // 工作文件
       filein = fopen(worldfile, "rt");    // 打開文件

       ...
       (讀入數(shù)據(jù)資料))
       ...

       fclose(filein);       // 關(guān)閉文件
       return;        // 返回
      }

      下一個挑戰(zhàn)是將每個單獨(dú)的文本行讀入變量。這有很多辦法可以做到。一個問題是文件中并不是所有的行都包含有意義的信息??招泻妥⑨尣粦?yīng)該被讀入。我們創(chuàng)建了一個叫做readstr()的函數(shù)。這個函數(shù)會從數(shù)據(jù)文

      件中讀入一個有意義的行至一個已經(jīng)初始化過的字符串。下面就是代碼:
       
      void readstr(FILE *f,char *string)     //  讀入一個字符串

      {
       do        // 循環(huán)開始
       {
        fgets(string, 255, f);     // 讀入一行
       } while ((string[0] == '/') || (string[0] == '\n'));  // 考察是否有必要進(jìn)行處理
       return;        // 返回
      }

      下一步我們讀入?yún)^(qū)段數(shù)據(jù)。這一課將只處理一個區(qū)段,不過實(shí)現(xiàn)一個多區(qū)段引擎也很容易。讓我們將注意力轉(zhuǎn)回SetupWorld()。程序必須知道區(qū)段內(nèi)包含了多少個三角形。我們在數(shù)據(jù)文件中以下面這種形式定義三角形

      數(shù)量:
      接下來是讀取三角形數(shù)量的代碼: 
        
      int numtriangles;       // 區(qū)段中的三角形數(shù)量
      char oneline[255];       // 存儲數(shù)據(jù)的字符串
      ...
      readstr(filein,oneline);      // 讀入一行數(shù)據(jù)
      sscanf(oneline, "NUMPOLLIES %d\n", &numtriangles);   // 讀入三角形數(shù)量

      余下的世界載入過程采用了相似的方法。接著,我們對區(qū)段進(jìn)行初始化,并讀入部分?jǐn)?shù)據(jù): 
        
      // 先前的定義: SECTOR sector1;
      char oneline[255];       // 存儲數(shù)據(jù)的字符串
      int numtriangles;       // 區(qū)段的三角形數(shù)量
      float x, y, z, u, v;       // 3D 和 紋理坐標(biāo)
      ...
      sector1.triangle = new TRIANGLE[numtriangles];    // 為numtriangles個三角形分配內(nèi)存并設(shè)定指針
      sector1.numtriangles = numtriangles;     // 定義區(qū)段1中的三角形數(shù)量
      // 遍歷區(qū)段中的每個三角形
      for (int triloop = 0; triloop < numtriangles; triloop++)  // 遍歷所有的三角形
      {
       // 遍歷三角形的每個頂點(diǎn)
       for (int vertloop = 0; vertloop < 3; vertloop++)  // 遍歷所有的頂點(diǎn)
       {
        readstr(filein,oneline);    // 讀入一行數(shù)據(jù)
        // 讀入各自的頂點(diǎn)數(shù)據(jù)
        sscanf(oneline, "%f %f %f %f %f", &x, &y, &z, &u, &v);
        // 將頂點(diǎn)數(shù)據(jù)存入各自的頂點(diǎn)
        sector1.triangle[triloop].vertex[vertloop].x = x; // 區(qū)段 1,  第 triloop 個三角形, 第  vertloop 個頂點(diǎn), 值 x =x
        sector1.triangle[triloop].vertex[vertloop].y = y; // 區(qū)段 1,  第 triloop 個三角形, 第  vertloop 個頂點(diǎn), 值 y =y
        sector1.triangle[triloop].vertex[vertloop].z = z; // 區(qū)段 1,  第 triloop 個三角形, 第  vertloop 個頂點(diǎn), 值  z =z
        sector1.triangle[triloop].vertex[vertloop].u = u; // 區(qū)段 1,  第 triloop 個三角形, 第  vertloop 個頂點(diǎn), 值  u =u
        sector1.triangle[triloop].vertex[vertloop].v = v; // 區(qū)段 1,  第 triloop 個三角形, 第  vertloop 個頂點(diǎn), 值  e=v
       }
      }

      數(shù)據(jù)文件中每個三角形都以如下形式聲明:
      X1 Y1 Z1 U1 V1
      X2 Y2 Z2 U2 V2
      X3 Y3 Z3 U3 V3
      顯示世界
      現(xiàn)在區(qū)段已經(jīng)載入內(nèi)存,我們下一步要在屏幕上顯示它。到目前為止,我們所作過的都是些簡單的旋轉(zhuǎn)和平移。但我們的鏡頭始終位于原點(diǎn)(0,0,0)處。任何一個不錯的3D引擎都會允許用戶在這個世界中游走和遍歷,

      我們的這個也一樣。實(shí)現(xiàn)這個功能的一種途徑是直接移動鏡頭并繪制以鏡頭為中心的3D環(huán)境。這樣做會很慢并且不易用代碼實(shí)現(xiàn)。我們的解決方法如下:
      根據(jù)用戶的指令旋轉(zhuǎn)并變換鏡頭位置。
      圍繞原點(diǎn),以與鏡頭相反的旋轉(zhuǎn)方向來旋轉(zhuǎn)世界。(讓人產(chǎn)生鏡頭旋轉(zhuǎn)的錯覺)
      以與鏡頭平移方式相反的方式來平移世界(讓人產(chǎn)生鏡頭移動的錯覺)。
      這樣實(shí)現(xiàn)起來就很簡單.
      下面從第一步開始吧(平移并旋轉(zhuǎn)鏡頭)。

      if (keys[VK_RIGHT])       // 右方向鍵按下了么?
      {
       yrot -= 1.5f;       // 向左旋轉(zhuǎn)場景
      }

      if (keys[VK_LEFT])       // 左方向鍵按下了么?
      {
       yrot += 1.5f;       // 向右側(cè)旋轉(zhuǎn)場景
      }

      if (keys[VK_UP])       // 向上方向鍵按下了么?
      {
       xpos -= (float)sin(heading*piover180) * 0.05f;   // 沿游戲者所在的X平面移動
       zpos -= (float)cos(heading*piover180) * 0.05f;   // 沿游戲者所在的Z平面移動
       if (walkbiasangle >= 359.0f)     // 如果walkbiasangle大于359度
       {
        walkbiasangle = 0.0f;     // 將 walkbiasangle 設(shè)為0
       }
       else        // 否則
       {
         walkbiasangle+= 10;     // 如果 walkbiasangle < 359 ,則增加 10
       }
       walkbias = (float)sin(walkbiasangle * piover180)/20.0f;  // 使游戲者產(chǎn)生跳躍感
      }

      if (keys[VK_DOWN])       // 向下方向鍵按下了么?
      {
       xpos += (float)sin(heading*piover180) * 0.05f;   // 沿游戲者所在的X平面移動
       zpos += (float)cos(heading*piover180) * 0.05f;   // 沿游戲者所在的Z平面移動
       if (walkbiasangle <= 1.0f)     // 如果walkbiasangle小于1度
       {
        walkbiasangle = 359.0f;     // 使 walkbiasangle 等于 359
       }
       else        // 否則
       {
        walkbiasangle-= 10;     // 如果 walkbiasangle > 1 減去 10
       }
       walkbias = (float)sin(walkbiasangle * piover180)/20.0f;  // 使游戲者產(chǎn)生跳躍感
      }

      這個實(shí)現(xiàn)很簡單。當(dāng)左右方向鍵按下后,旋轉(zhuǎn)變量yrot
      相應(yīng)增加或減少。當(dāng)前后方向鍵按下后,我們使用sine和cosine函數(shù)重新生成鏡頭位置(您需要些許三角函數(shù)學(xué)的知識:-)。Piover180
      是一個很簡單的折算因子用來折算度和弧度。
      接著您可能會問:walkbias是什么意思?這是NeHe的發(fā)明的單詞:-)?;旧暇褪钱?dāng)人行走時頭部產(chǎn)生上下擺動的幅度。我們使用簡單的sine正弦波來調(diào)節(jié)鏡頭的Y軸位置。如果不添加這個而只是前后移動的話,程序

      看起來就沒這么棒了。
      現(xiàn)在,我們已經(jīng)有了下面這些變量??梢蚤_始進(jìn)行步驟2和3了。由于我們的程序還不太復(fù)雜,我們無需新建一個函數(shù),而是直接在顯示循環(huán)中完成這些步驟。 
        
      int DrawGLScene(GLvoid)       // 繪制 OpenGL 場景
      {
       glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);  // 清除 場景 和 深度緩沖
       glLoadIdentity();      // 重置當(dāng)前矩陣


       GLfloat x_m, y_m, z_m, u_m, v_m;    // 頂點(diǎn)的臨時 X, Y, Z, U 和 V 的數(shù)值
       GLfloat xtrans = -xpos;      // 用于游戲者沿X軸平移時的大小
       GLfloat ztrans = -zpos;      // 用于游戲者沿Z軸平移時的大小
       GLfloat ytrans = -walkbias-0.25f;    // 用于頭部的上下擺動
       GLfloat sceneroty = 360.0f - yrot;    // 位于游戲者方向的360度角

       int numtriangles;      // 保有三角形數(shù)量的整數(shù)

       glRotatef(lookupdown,1.0f,0,0);     // 上下旋轉(zhuǎn)
       glRotatef(sceneroty,0,1.0f,0);     // 根據(jù)游戲者正面所對方向所作的旋轉(zhuǎn)

       glTranslatef(xtrans, ytrans, ztrans);    // 以游戲者為中心的平移場景
       glBindTexture(GL_TEXTURE_2D, texture[filter]);   // 根據(jù) filter 選擇的紋理

       numtriangles = sector1.numtriangles;    // 取得Sector1的三角形數(shù)量

       // 逐個處理三角形
       for (int loop_m = 0; loop_m < numtriangles; loop_m++)  // 遍歷所有的三角形
       {
        glBegin(GL_TRIANGLES);     // 開始繪制三角形
         glNormal3f( 0.0f, 0.0f, 1.0f);   // 指向前面的法線
         x_m = sector1.triangle[loop_m].vertex[0].x; // 第一點(diǎn)的 X 分量
         y_m = sector1.triangle[loop_m].vertex[0].y; // 第一點(diǎn)的 Y 分量
         z_m = sector1.triangle[loop_m].vertex[0].z; // 第一點(diǎn)的 Z 分量
         u_m = sector1.triangle[loop_m].vertex[0].u; // 第一點(diǎn)的 U  紋理坐標(biāo)
         v_m = sector1.triangle[loop_m].vertex[0].v; // 第一點(diǎn)的 V  紋理坐標(biāo)
         glTexCoord2f(u_m,v_m); glVertex3f(x_m,y_m,z_m); // 設(shè)置紋理坐標(biāo)和頂點(diǎn)

         x_m = sector1.triangle[loop_m].vertex[1].x; // 第二點(diǎn)的 X 分量
         y_m = sector1.triangle[loop_m].vertex[1].y; // 第二點(diǎn)的 Y 分量
         z_m = sector1.triangle[loop_m].vertex[1].z; // 第二點(diǎn)的 Z 分量
         u_m = sector1.triangle[loop_m].vertex[1].u; // 第二點(diǎn)的 U  紋理坐標(biāo)
         v_m = sector1.triangle[loop_m].vertex[1].v; // 第二點(diǎn)的 V  紋理坐標(biāo)
         glTexCoord2f(u_m,v_m); glVertex3f(x_m,y_m,z_m); // 設(shè)置紋理坐標(biāo)和頂點(diǎn)

         x_m = sector1.triangle[loop_m].vertex[2].x; // 第三點(diǎn)的 X 分量
         y_m = sector1.triangle[loop_m].vertex[2].y; // 第三點(diǎn)的 Y 分量
         z_m = sector1.triangle[loop_m].vertex[2].z; // 第三點(diǎn)的 Z 分量
         u_m = sector1.triangle[loop_m].vertex[2].u; // 第二點(diǎn)的 U  紋理坐標(biāo)
         v_m = sector1.triangle[loop_m].vertex[2].v; // 第二點(diǎn)的 V  紋理坐標(biāo)
         glTexCoord2f(u_m,v_m); glVertex3f(x_m,y_m,z_m); // 設(shè)置紋理坐標(biāo)和頂點(diǎn)
        glEnd();      // 三角形繪制結(jié)束
       }
       return TRUE;       // 返回
      }
      原文及其個版本源代碼下載:

      http://nehe./data/lessons/lesson.asp?lesson=10

        本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點(diǎn)擊一鍵舉報。
        轉(zhuǎn)藏 分享 獻(xiàn)花(0

        0條評論

        發(fā)表

        請遵守用戶 評論公約

        類似文章 更多