鍍金池/ 教程/ Android/ 3D 繪圖基本概念
                                          3D 繪圖基本概念
                                          概述
                                          真正的 3D 圖形
                                          添加顏色
                                          3D 坐標變換
                                          構造
                                          材質渲染

                                          3D 繪圖基本概念

                                          前面介紹了使用 Android 編寫 OpenGL ES 應用的程序框架,本篇介紹3D繪圖的一些基本構成要素,最終將實現一個多邊形的繪制。

                                          一個 3D 圖形通常是由一些小的基本元素(頂點,邊,面,多邊形)構成,每個基本元素都可以單獨來操作。

                                          Vertex (頂點)

                                          頂點是 3D 建模時用到的最小構成元素,頂點定義為兩條或是多條邊交會的地方。在 3D 模型中一個頂點可以為多條邊,面或是多邊形所共享。一個頂點也可以代表一個點光源或是 Camera 的位置。下圖中標識為黃色的點為一個頂點(Vertex)。

                                          http://wiki.jikexueyuan.com/project/opengl-es-basics/images/1.png" alt="" />

                                          在 Android 系統中可以使用一個浮點數數組來定義一個頂點,浮點數數組通常放在一個 Buffer(java.nio)中來提高性能。

                                          比如:下圖中定義了四個頂點和對應的 Android 頂點定義:

                                          http://wiki.jikexueyuan.com/project/opengl-es-basics/images/2.png" alt="" />

                                          private float vertices[] = {
                                           -1.0f,  1.0f, 0.0f,  // 0, Top Left
                                           -1.0f, -1.0f, 0.0f,  // 1, Bottom Left
                                           1.0f, -1.0f, 0.0f,  // 2, Bottom Right
                                           1.0f,  1.0f, 0.0f,  // 3, Top Right
                                          };  

                                          為了提高性能,通常將這些數組存放到 java.io 中定義的 Buffer 類中:

                                          // a float is 4 bytes, therefore we multiply the
                                          //number if vertices with 4.
                                          ByteBuffer vbb = ByteBuffer.allocateDirect(vertices.length * 4);
                                          vbb.order(ByteOrder.nativeOrder());
                                          FloatBuffer vertexBuffer = vbb.asFloatBuffer();
                                          vertexBuffer.put(vertices);
                                          vertexBuffer.position(0);  

                                          有了頂點的定義,下面一步就是如何將它們傳給 OpenGL ES 庫,OpenGL ES 提供一個成為”管道 Pipeline ”的機制,這個管道定義了一些“開關”來控制 OpenGL ES 支持的某些功能,缺省情況這些功能是關閉的,如果需要使用 OpenGL ES 的這些功能,需要明確告知 OpenGL “管道”打開所需功能。因此對于我們的這個示例,需要告訴 OpenGL 庫打開 Vertex buffer 以便傳入頂點坐標 Buffer。要注意的使用完某個功能之后,要關閉這個功能以免影響后續操作:

                                          // Enabled the vertex buffer for writing and to be used during rendering.
                                          gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);// OpenGL docs.
                                          // Specifies the location and data format of an array of vertex
                                          // coordinates to use when rendering.
                                          gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vertexBuffer); // OpenGL docs.
                                          When you are done with the buffer don't forget to disable it.
                                          // Disable the vertices buffer.
                                          gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);// OpenGL docs.  

                                          Edge(邊)

                                          邊定義為兩個頂點之間的線段。邊是面和多邊形的邊界線。在 3D 模型中,邊可以被相鄰的兩個面或是多邊形形共享。對一個邊做變換將影響邊相接的所有頂點,面或多邊形。在 OpenGL 中,通常無需直接來定義一個邊,而是通過頂點定義一個面,從而由面定義了其所對應的三條邊??梢酝ㄟ^修改邊的兩個頂點來更改一條邊,下圖黃色的線段代表一條邊:

                                          http://wiki.jikexueyuan.com/project/opengl-es-basics/images/3.png" alt="" />

                                          Face (面)

                                          在 OpenGL ES 中,面特指一個三角形,由三個頂點和三條邊構成,對一個面所做的變化影響到連接面的所有頂點和邊,面多邊形。下圖黃色區域代表一個面。

                                          http://wiki.jikexueyuan.com/project/opengl-es-basics/images/4.png" alt="" />

                                          定義面的頂點的順序很重要 在拼接曲面的時候,用來定義面的頂點的順序非常重要,因為頂點的順序定義了面的朝向(前向或是后向),為了獲取繪制的高性能,一般情況不會繪制面的前面和后面,只繪制面的“前面”。雖然“前面”“后面”的定義可以應人而易,但一般為所有的“前面”定義統一的頂點順序(順時針或是逆時針方向)。

                                          下面代碼設置逆時針方法為面的“前面”:

                                          gl.glFrontFace(GL10.GL_CCW);  

                                          打開 忽略“后面”設置:

                                          gl.glEnable(GL10.GL_CULL_FACE);  

                                          明確指明“忽略“哪個面的代碼如下:

                                          gl.glCullFace(GL10.GL_BACK);  

                                          Polygon (多邊形)

                                          多邊形由多個面(三角形)拼接而成,在三維空間上,多邊形并一定表示這個 Polygon 在同一平面上。這里我們使用缺省的逆時針方向代表面的“前面 Front),下圖黃色區域為一個多邊形。

                                          http://wiki.jikexueyuan.com/project/opengl-es-basics/images/5.png" alt="" />

                                          來看一個多邊形的示例在 Android 系統如何使用頂點和 buffer 來定義,如下圖定義了一個正方形:

                                          http://wiki.jikexueyuan.com/project/opengl-es-basics/images/6.png" alt="" />

                                          對應的頂點和buffer 定義代碼:

                                          private short[] indices = { 0, 1, 2, 0, 2, 3 };
                                          To gain some performance we also put this ones in a byte buffer.
                                          // short is 2 bytes, therefore we multiply the number if vertices with 2.
                                          ByteBuffer ibb = ByteBuffer.allocateDirect(indices.length * 2);
                                          ibb.order(ByteOrder.nativeOrder());
                                          ShortBuffer indexBuffer = ibb.asShortBuffer();
                                          indexBuffer.put(indices);
                                          indexBuffer.position(0);  

                                          Render (渲染)

                                          我們已定義好了多邊形,下面就要了解如和使用 OpenGL ES 的 API 來繪制(渲染)這個多邊形了。OpenGL ES 提供了兩類方法來繪制一個空間幾何圖形:

                                          • public abstract void glDrawArrays(int mode, int first, int count) 使用VetexBuffer 來繪制,頂點的順序由vertexBuffer中的順序指定。
                                          • public abstract void glDrawElements(int mode, int count, int type, Buffer indices) ,可以重新定義頂點的順序,頂點的順序由indices Buffer 指定。

                                          前面我們已定義里頂點數組,因此我們將采用 glDrawElements 來繪制多邊形。

                                          同樣的頂點,可以定義的幾何圖形可以有所不同,比如三個頂點,可以代表三個獨立的點,也可以表示一個三角形,這就需要使用 mode 來指明所需繪制的幾何圖形的基本類型。

                                          GL_POINTS

                                          繪制獨立的點。

                                          http://wiki.jikexueyuan.com/project/opengl-es-basics/images/7.png" alt="" />

                                          GL_LINE_STRIP

                                          繪制一系列線段。

                                          http://wiki.jikexueyuan.com/project/opengl-es-basics/images/8.png" alt="" />

                                          GL_LINE_LOOP

                                          類同上,但是首尾相連,構成一個封閉曲線。

                                          http://wiki.jikexueyuan.com/project/opengl-es-basics/images/9.png" alt="" />

                                          GL_LINES

                                          頂點兩兩連接,為多條線段構成。

                                          http://wiki.jikexueyuan.com/project/opengl-es-basics/images/10.png" alt="" />

                                          GL_TRIANGLES

                                          每隔三個頂點構成一個三角形,為多個三角形組成。

                                          http://wiki.jikexueyuan.com/project/opengl-es-basics/images/11.png" alt="" />

                                          GL_TRIANGLE_STRIP

                                          每相鄰三個頂點組成一個三角形,為一系列相接三角形構成。

                                          http://wiki.jikexueyuan.com/project/opengl-es-basics/images/12.png" alt="" />

                                          GL_TRIANGLE_FAN

                                          以一個點為三角形公共頂點,組成一系列相鄰的三角形。

                                          http://wiki.jikexueyuan.com/project/opengl-es-basics/images/13.png" alt="" />

                                          下面可以來繪制正方形了,在項目中添加一個 Square.java 定義如下:

                                          package se.jayway.opengl.tutorial;
                                          import java.nio.ByteBuffer;
                                          import java.nio.ByteOrder;
                                          import java.nio.FloatBuffer;
                                          import java.nio.ShortBuffer;
                                          import javax.microedition.khronos.opengles.GL10;
                                          public class Square {
                                           // Our vertices.
                                           private float vertices[] = {
                                           -1.0f,  1.0f, 0.0f,  // 0, Top Left
                                           -1.0f, -1.0f, 0.0f,  // 1, Bottom Left
                                           1.0f, -1.0f, 0.0f,  // 2, Bottom Right
                                           1.0f,  1.0f, 0.0f,  // 3, Top Right
                                           };
                                           // The order we like to connect them.
                                           private short[] indices = { 0, 1, 2, 0, 2, 3 };
                                           // Our vertex buffer.
                                           private FloatBuffer vertexBuffer;
                                           // Our index buffer.
                                           private ShortBuffer indexBuffer;
                                           public Square() {
                                           // a float is 4 bytes, therefore we
                                           // multiply the number if
                                           // vertices with 4.
                                           ByteBuffer vbb
                                           = ByteBuffer.allocateDirect(vertices.length * 4);
                                           vbb.order(ByteOrder.nativeOrder());
                                           vertexBuffer = vbb.asFloatBuffer();
                                           vertexBuffer.put(vertices);
                                           vertexBuffer.position(0);
                                           // short is 2 bytes, therefore we multiply
                                           //the number if
                                           // vertices with 2.
                                           ByteBuffer ibb
                                           = ByteBuffer.allocateDirect(indices.length * 2);
                                           ibb.order(ByteOrder.nativeOrder());
                                           indexBuffer = ibb.asShortBuffer();
                                           indexBuffer.put(indices);
                                           indexBuffer.position(0);
                                           }
                                           /**
                                           * This function draws our square on screen.
                                           * @param gl
                                           */
                                           public void draw(GL10 gl) {
                                           // Counter-clockwise winding.
                                           gl.glFrontFace(GL10.GL_CCW);
                                           // Enable face culling.
                                           gl.glEnable(GL10.GL_CULL_FACE);
                                           // What faces to remove with the face culling.
                                           gl.glCullFace(GL10.GL_BACK);
                                           // Enabled the vertices buffer for writing
                                           //and to be used during
                                           // rendering.
                                           gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
                                           // Specifies the location and data format of
                                           //an array of vertex
                                           // coordinates to use when rendering.
                                           gl.glVertexPointer(3, GL10.GL_FLOAT, 0,
                                           vertexBuffer);
                                           gl.glDrawElements(GL10.GL_TRIANGLES, indices.length
                                           GL10.GL_UNSIGNED_SHORT, indexBuffer);
                                           // Disable the vertices buffer.
                                           gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
                                           // Disable face culling.
                                           gl.glDisable(GL10.GL_CULL_FACE);
                                           }
                                          }  

                                          在 OpenGLRenderer 中添加 Square 成員變量并初始化:

                                          // Initialize our square.
                                          Square square = new Square();  

                                          并在 public void onDrawFrame(GL10 gl) 添加

                                          // Draw our square.
                                          square.draw(gl);  

                                          來繪制這個正方形,編譯運行,什么也沒顯示,這是為什么呢?這是因為 OpenGL ES 從當前位置開始渲染,缺省坐標為(0,0,0),和 View port 的坐標一樣,相當于把畫面放在眼前,對應這種情況 OpenGL 不會渲染離 view Port 很近的畫面,因此我們需要將畫面向后退一點距離:

                                          // Translates 4 units into the screen.
                                          gl.glTranslatef(0, 0, -4);  

                                          在編譯運行,這次倒是有顯示了,當正方形迅速后移直至看不見,這是因為每次調用onDrawFrame 時,每次都再向后移動4個單位,需要加上重置 Matrix 的代碼。

                                          // Replace the current matrix with the identity matrix
                                          gl.glLoadIdentity();  

                                          最終 onDrawFrame 的代碼如下:

                                          public void onDrawFrame(GL10 gl) {
                                           // Clears the screen and depth buffer.
                                           gl.glClear(GL10.GL_COLOR_BUFFER_BIT |
                                           GL10.GL_DEPTH_BUFFER_BIT);
                                           gl.glLoadIdentity();
                                           gl.glTranslatef(0, 0, -4);
                                           // Draw our square.
                                           square.draw(gl); // ( NEW )
                                          }  

                                          http://wiki.jikexueyuan.com/project/opengl-es-basics/images/14.png" alt="" />

                                          上一篇:材質渲染下一篇:構造
                                          人妻精品动漫h无码中字