天天看點

加載3D世界并在3D世界中漫遊

在這一課中,你将學會如何加載3D世界,并在3D世界中漫遊。

現在這些日子您所需要的是一個大一點的、更複雜些的、動态3D世界,它帶有空間的六自由度和花哨的效果如鏡像、入口、扭曲等等,當然還要有更快的幀顯示速度。這一課就要解釋一個基本的3D世界"結構",以及如何在這個世界裡遊走。

資料結構

當您想要使用一系列的數字來完美的表達3D環境時,随着環境複雜度的上升,這個工作的難度也會随之上升。出于這個原因,我們必須将資料歸類,使其具有更多的可操作性風格。在程式清單頭部出現了sector(區段)的定義。每個3D世界基本上可以看作是sector(區段)的集合。一個sector(區段)可以是一個房間、一個立方體、或者任意一個閉合的區間。

這裡我們在渲染類Render裡面定義了三個内部類,内部類的代碼如下所示:

Java代碼

加載3D世界并在3D世界中漫遊
  1. private static class Vertex {   
  2.     public float x, y, z;   
  3.     public float u, v;   
  4. }   
  5. private static class Triangle {   
  6.     public Vertex[] vertex = new Vertex[3];   
  7.     public Triangle() {   
  8.         for (int i = 0; i < 3; i++)   
  9.             vertex[i] = new Vertex();   
  10.     }   
  11. }   
  12. private static class Sector {   
  13.     public int numtriangles;   
  14.     public Triangle[] triangles;   
  15.     public Sector(int inTri) {   
  16.         //确定了三邊形的數量   
  17.         numtriangles = inTri;   
  18.         triangles = new Triangle[inTri];   
  19.         //對三邊形指派   
  20.         for (int i = 0; i < inTri; i++)   
  21.             triangles[i] = new Triangle();   
  22.     }   
  23. }  
/**
     * 定義頂點
     * 頂點包含了OpenGL真正感興趣的資料。
     * 我們用3D空間中的坐标值(x,y,z)以及它們的紋理坐标(u,v)來定義三角形的每個頂點。
     */
    private static class Vertex {
        public float x, y, z;
        public float u, v;
    }

    /**
     * 定義三邊形
     * 三角形本質上是由一些(兩個以上)頂點組成的多邊形,
     * 頂點同時也是我們的最基本的分類機關。
     */
    private static class Triangle {
        public Vertex[] vertex = new Vertex[3];

        public Triangle() {
            for (int i = 0; i < 3; i++)
                vertex[i] = new Vertex();

        }
    }

    /**
     * 區段定義
     * 一個sector(區段)包含了一系列的多邊形,
     * 是以下一個目标就是triangle(我們将隻用三角形,這樣寫代碼更容易些)。
     */
    private static class Sector {
        public int numtriangles;
        public Triangle[] triangles;

        public Sector(int inTri) {
            //确定了三邊形的數量
            numtriangles = inTri;
            triangles = new Triangle[inTri];
            //對三邊形指派
            for (int i = 0; i < inTri; i++)
                triangles[i] = new Triangle();
        }
    }
           

 下面是渲染類的全部的代碼:

Java代碼

加載3D世界并在3D世界中漫遊
  1. package demos.nehe.lesson10;   
  2. import demos.common.ResourceRetriever;   
  3. import demos.common.TextureReader;   
  4. import javax.media.opengl.GL;   
  5. import javax.media.opengl.GLAutoDrawable;   
  6. import javax.media.opengl.GLEventListener;   
  7. import javax.media.opengl.glu.GLU;   
  8. import java.io.BufferedReader;   
  9. import java.io.IOException;   
  10. import java.io.InputStreamReader;   
  11. import java.util.StringTokenizer;   
  12. import javax.media.opengl.GL2;   
  13. import javax.media.opengl.glu.gl2.GLUgl2;   
  14. class Renderer implements GLEventListener {   
  15.     //常量   
  16.     private final float PI_180 = (float) (Math.PI / 180.0);   
  17.     //啟用混合的開關   
  18.     private boolean blendingEnabled;                // Blending ON/OFF   
  19.     //   
  20.     private float heading;   
  21.     private float xpos;   
  22.     private float zpos;   
  23.     //設定是否可以在上下左右移動或旋轉視角   
  24.     private boolean stepForward;   
  25.     private boolean stepBackward;   
  26.     private boolean turnRight;   
  27.     private boolean turnLeft;   
  28.     //Y軸方向上的旋轉量   
  29.     private float yrot;             // Y Rotation   
  30.     private float walkbias = 0;   
  31.     private float walkbiasangle = 0;   
  32.     private float lookupdown = 0.0f;   
  33.     private boolean lookUp;   
  34.     private boolean lookDown;   
  35.     //選擇過濾的種類   
  36.     private int filter;                
  37.     //使用的紋理貼圖   
  38.     private int[] textures = new int[3];               
  39.      //我們的模型存儲   
  40.     private Sector sector1;                
  41.     private GLU glu = new GLUgl2();   
  42.     public void toggleBlending() {   
  43.         blendingEnabled = !blendingEnabled;   
  44.     }   
  45.     public void switchFilter() {   
  46.         filter = (filter + 1) % 3;   
  47.     }   
  48.     public void stepForward(boolean step) {   
  49.         stepForward = step;   
  50.     }   
  51.     public void stepBackward(boolean step) {   
  52.         stepBackward = step;   
  53.     }   
  54.     public void turnRight(boolean turn) {   
  55.         turnRight = turn;   
  56.     }   
  57.     public void turnLeft(boolean turn) {   
  58.         turnLeft = turn;   
  59.     }   
  60.     public void lookUp(boolean look) {   
  61.         lookUp = look;   
  62.     }   
  63.     public void lookDown(boolean look) {   
  64.         lookDown = look;   
  65.     }   
  66.     private void setupWorld() throws IOException {   
  67.         BufferedReader in = null;   
  68.         try {   
  69.             in = new BufferedReader(new InputStreamReader(ResourceRetriever.getResourceAsStream("demos/data/models/world.txt")));   
  70.             String line = null;   
  71.             while ((line = in.readLine()) != null) {   
  72.                 //如果一行為空   
  73.     if (line.trim().length() == 0 || line.trim().startsWith("//"))   
  74.                     continue;   
  75.                 //讀取第一行 第一行裡以字元串“NUMPOLLIES”開頭,記錄了這個區段裡的三邊形的數量   
  76.      if (line.startsWith("NUMPOLLIES")) {   
  77.              int numTriangles;   
  78.                     //将讀取的數量的字元串轉化為整形   
  79.     numTriangles = Integer.parseInt(line.substring(line.indexOf("NUMPOLLIES") + "NUMPOLLIES".length() + 1));   
  80.                     //建構區段将頂點和面的資訊存儲在區段裡并儲存在記憶體中   
  81.             sector1 = new Sector(numTriangles);   
  82.                     break;   
  83.                 }   
  84.             }   
  85.             //将頂點資訊和面的資訊儲存在區段中   
  86.             for (int i = 0; i < sector1.numtriangles; i++) {   
  87.                 for (int vert = 0; vert < 3; vert++) {   
  88.                     //去除空格行   
  89.                     while ((line = in.readLine()) != null) {   
  90.                         if (line.trim().length() == 0 || line.trim().startsWith("//"))   
  91.                             continue;   
  92.                         break;   
  93.                     }   
  94.                     //如果不為空格行   
  95.                     if (line != null) {   
  96.                         StringTokenizer st = new StringTokenizer(line, " ");   
  97.                         //由檔案txt中存儲的格式,将每行中的資料轉換為浮點型,儲存到三邊形的頂點數組中   
  98.    sector1.triangles[i].vertex[vert].x = Float.valueOf(st.nextToken()).floatValue();   
  99.     sector1.triangles[i].vertex[vert].y = Float.valueOf(st.nextToken()).floatValue();   
  100.      sector1.triangles[i].vertex[vert].z = Float.valueOf(st.nextToken()).floatValue();   
  101.      sector1.triangles[i].vertex[vert].u = Float.valueOf(st.nextToken()).floatValue();   
  102.      sector1.triangles[i].vertex[vert].v = Float.valueOf(st.nextToken()).floatValue();   
  103.                     }   
  104.                 }   
  105.             }   
  106.         } finally {   
  107.             if (in != null)   
  108.                 in.close();   
  109.         }   
  110.     }   
  111.     private void loadGLTextures(GL2 gl, GLU glu) throws IOException {   
  112.         TextureReader.Texture texture = TextureReader.readTexture("demos/data/images/mud.png");   
  113.         //Create Nearest Filtered Texture   
  114.         //啟用Nearest 過濾   
  115.         gl.glGenTextures(3, textures, 0);   
  116.         gl.glBindTexture(GL2.GL_TEXTURE_2D, textures[0]);   
  117.         gl.glTexParameteri(GL2.GL_TEXTURE_2D, GL2.GL_TEXTURE_MAG_FILTER, GL.GL_NEAREST);   
  118.         gl.glTexParameteri(GL2.GL_TEXTURE_2D, GL2.GL_TEXTURE_MIN_FILTER, GL.GL_NEAREST);   
  119.         gl.glTexImage2D(GL2.GL_TEXTURE_2D,   
  120.                 0,   
  121.                 3,   
  122.                 texture.getWidth(),   
  123.                 texture.getHeight(),   
  124.                 0,   
  125.                 GL.GL_RGB,   
  126.                 GL.GL_UNSIGNED_BYTE,   
  127.                 texture.getPixels());   
  128.         //Create Linear Filtered Texture   
  129.         //啟用線性過濾的紋理   
  130.         gl.glBindTexture(GL2.GL_TEXTURE_2D, textures[1]);   
  131.         gl.glTexParameteri(GL2.GL_TEXTURE_2D, GL2.GL_TEXTURE_MAG_FILTER, GL.GL_LINEAR);   
  132.         gl.glTexParameteri(GL2.GL_TEXTURE_2D, GL2.GL_TEXTURE_MIN_FILTER, GL.GL_LINEAR);   
  133.         gl.glTexImage2D(GL2.GL_TEXTURE_2D,   
  134.                 0,   
  135.                 3,   
  136.                 texture.getWidth(),   
  137.                 texture.getHeight(),   
  138.                 0,   
  139.                 GL.GL_RGB,   
  140.                 GL.GL_UNSIGNED_BYTE,   
  141.                 texture.getPixels());   
  142.         //啟用 mipmapped濾波方式   
  143.         gl.glBindTexture(GL2.GL_TEXTURE_2D, textures[2]);   
  144.         gl.glTexParameteri(GL2.GL_TEXTURE_2D, GL2.GL_TEXTURE_MAG_FILTER, GL.GL_LINEAR);   
  145.         gl.glTexParameteri(GL2.GL_TEXTURE_2D, GL2.GL_TEXTURE_MIN_FILTER, GL.GL_LINEAR_MIPMAP_NEAREST);   
  146.         glu.gluBuild2DMipmaps(GL2.GL_TEXTURE_2D,   
  147.                 3,   
  148.                 texture.getWidth(),   
  149.                 texture.getHeight(),   
  150.                 GL.GL_RGB,   
  151.                 GL.GL_UNSIGNED_BYTE,   
  152.                 texture.getPixels());   
  153.     }   
  154.     public void init(GLAutoDrawable drawable) {   
  155.         GL2 gl = drawable.getGL().getGL2();   
  156.         try {   
  157.             //導入紋理圖檔并banding   
  158.             loadGLTextures(gl, glu);   
  159.             //導入模型頂點和三邊形   
  160.             setupWorld();   
  161.         } catch (IOException e) {   
  162.             throw new RuntimeException(e);   
  163.         }   
  164.         //啟用紋理映射   
  165.         gl.glEnable(GL2.GL_TEXTURE_2D);   
  166.         //設定混合   
  167.         gl.glBlendFunc(GL2.GL_SRC_ALPHA, GL.GL_ONE);   
  168.         //啟用高斯模糊   
  169.         gl.glShadeModel(GL2.GL_SMOOTH);   
  170.         //設定背景顔色   
  171.         gl.glClearColor(0.0f, 0.0f, 0.0f, 0.0f);   
  172.         //啟用深度緩存   
  173.         gl.glClearDepth(1.0);   
  174.         //啟用深度測試   
  175.         gl.glEnable(GL.GL_DEPTH_TEST);   
  176.         gl.glDepthFunc(GL.GL_LEQUAL);                             //The Type Of Depth Test To Do   
  177.         //啟用   
  178.         gl.glHint(GL2.GL_PERSPECTIVE_CORRECTION_HINT, GL.GL_NICEST);  // Really Nice Perspective Calculations   
  179.     }   
  180.     private void update() {   
  181.         if (stepForward) {   
  182.             xpos -= (float) Math.sin(heading * PI_180) * 0.05f;   
  183.             zpos -= (float) Math.cos(heading * PI_180) * 0.05f;   
  184.             if (walkbiasangle >= 359.0f) {   
  185.                 walkbiasangle = 0.0f;   
  186.             } else {   
  187.                 walkbiasangle += 10;   
  188.             }   
  189.             walkbias = (float) Math.sin(walkbiasangle * PI_180) / 20.0f;   
  190.         }   
  191.         if (stepBackward) {   
  192.             xpos += (float) Math.sin(heading * PI_180) * 0.05f;   
  193.             zpos += (float) Math.cos(heading * PI_180) * 0.05f;   
  194.             if (walkbiasangle <= 1.0f) {   
  195.                 walkbiasangle = 359.0f;   
  196.             } else {   
  197.                 walkbiasangle -= 10;   
  198.             }   
  199.             walkbias = (float) Math.sin(walkbiasangle * PI_180) / 20.0f;   
  200.         }   
  201.         if (turnRight) {   
  202.             heading -= 1.0f;   
  203.             yrot = heading;   
  204.         }   
  205.         if (turnLeft) {   
  206.             heading += 1.0f;   
  207.             yrot = heading;   
  208.         }   
  209.         if (lookUp) {   
  210.             lookupdown -= 1.0f;   
  211.         }   
  212.         if (lookDown) {   
  213.             lookupdown += 1.0f;   
  214.         }   
  215.     }   
  216.     public void display(GLAutoDrawable drawable) {   
  217.         //更新   
  218.         update();   
  219.         GL2 gl = drawable.getGL().getGL2();   
  220.         //啟用顔色緩存和深度緩存   
  221.         gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);       //Clear The Screen And The Depth Buffer   
  222.         //重置觀察模型   
  223.         gl.glLoadIdentity();                                         //Reset The View   
  224.         //如果混合啟用就開啟混合   
  225.         if (!blendingEnabled) {   
  226.             gl.glDisable(GL.GL_BLEND);   
  227.             gl.glEnable(GL.GL_DEPTH_TEST);   
  228.         } else {   
  229.             gl.glEnable(GL.GL_BLEND);   
  230.             gl.glDisable(GL.GL_DEPTH_TEST);   
  231.         }   
  232.         // 頂點的臨時 X, Y, Z, U 和 V 的數值   
  233.         float x = 0,y = 0,z = 0,u = 0,v = 0;   
  234.         //平移的三維分量   
  235.          用于遊戲者沿X軸平移時的大小   
  236.         float xtrans = -xpos;   
  237.         // 用于遊戲者沿Z軸平移時的大小   
  238.         float ztrans = -zpos;   
  239.         // 用于頭部的上下擺動   
  240.         float ytrans = -walkbias - 0.25f;   
  241.         // 位于遊戲者方向的360度角   
  242.         float sceneroty = 360.0f - yrot;   
  243.         // 上下旋轉   
  244.         gl.glRotatef(lookupdown, 1.0f, 0, 0);   
  245.         // 根據遊戲者正面所對方向所作的旋轉   
  246.         gl.glRotatef(sceneroty, 0, 1.0f, 0);   
  247.         // 以遊戲者為中心的平移場景   
  248.         gl.glTranslatef(xtrans, ytrans, ztrans);   
  249.         // 根據 filter 選擇的紋理   
  250.         gl.glBindTexture(GL2.GL_TEXTURE_2D, textures[filter]);   
  251.         // 處理各個三邊形   
  252.         for (int i = 0; i < sector1.numtriangles; i++) {   
  253.             //繪制三邊形   
  254.             gl.glBegin(GL2.GL_TRIANGLES);   
  255.             // 指向前面的法線   
  256.             gl.glNormal3f(0.0f, 0.0f, 1.0f);   
  257.             //第一個頂點的各分量   
  258.             x = sector1.triangles[i].vertex[0].x;   
  259.             y = sector1.triangles[i].vertex[0].y;   
  260.             z = sector1.triangles[i].vertex[0].z;   
  261.             u = sector1.triangles[i].vertex[0].u;   
  262.             v = sector1.triangles[i].vertex[0].v;   
  263.             gl.glTexCoord2f(u, v);   
  264.             gl.glVertex3f(x, y, z);   
  265.             //第二個頂點的各分量   
  266.             x = sector1.triangles[i].vertex[1].x;   
  267.             y = sector1.triangles[i].vertex[1].y;   
  268.             z = sector1.triangles[i].vertex[1].z;   
  269.             u = sector1.triangles[i].vertex[1].u;   
  270.             v = sector1.triangles[i].vertex[1].v;   
  271.             gl.glTexCoord2f(u, v);   
  272.             gl.glVertex3f(x, y, z);   
  273.             //第三個頂點的各分量   
  274.             x = sector1.triangles[i].vertex[2].x;   

繼續閱讀