天天看點

OpenGL ES 2.0 (iOS)[03]:熟練圖元繪制,玩轉二維圖形本文核心目的就是熟練圖形的分析與繪制

學習這篇:

文章的大前提是,你得有《OpenGL ES 2.0 (iOS): 一步從一個小三角開始》的基礎知識。

本文核心目的就是熟練圖形的分析與繪制

零、目标+準備

  1. 目标
OpenGL ES 2.0 (iOS)[03]:熟練圖元繪制,玩轉二維圖形本文核心目的就是熟練圖形的分析與繪制

Geometries

  1. 準備
  • 觀察所有圖形,發現它們都是點與點之間的連線(直線或曲線),組成一個幾何形狀( _ 好像有點廢話);
  • 除了點線的問題外,還可以知道幾何形狀,有交疊、閉環、開環三種情況;
  • 除此之外,還有填充色有無的問題;
  • A、根據 OpenGL ES 的特點,歸納總結:
    • a. 要繪制這些圖形,需要控制頂點的數量
    • b. 控制頂點與頂點之間的連接配接情況,Strip 或 Loop(Fan) 或 沒關系
    • c. 控制圖形的填充色,即 Fragment Shader 與 Vertex Shader 之間的顔色傳遞問題;
  • B、OpenGL ES 下控制資料源與繪制方式的函數有那些?(VBO模式)
    • a. 綁定 VBO 資料 glBufferData
    • b. 繪制資料 glDrawArrays/glDrawElements
    • c. 繪制模式有:
      • GL_POINTS (點)
      • GL_LINES/GL_LINE_STRIP/GL_LINE_LOOP (線)
      • GL_TRIANGLES/GL_TRIANGLE_STRIP/GL_TRIANGLE_FAN (面)

是以本文就是根據圖形的形态,選擇适當的繪制方式,去繪制圖形;核心目的就是熟練圖形的分析與繪制;

因為是練習圖元,是以學習的重點在,資料綁定和圖形繪制這一塊;

一、圖元繪制之線

Lines,多條線的意思;

Line Strip , 指首尾相接的線段,第一條線和最後一條線沒有連接配接在一起;

Line Loops, 指首尾相接的線段,第一條線和最後一條線連接配接在一起,即閉合的曲線;

OpenGL ES 2.0 (iOS)[03]:熟練圖元繪制,玩轉二維圖形本文核心目的就是熟練圖形的分析與繪制
模式 線與點的數量關系
GL_LINES nPoints = 2 * mLines
GL_LINE_STRIP nPoints = mLines + 1
GL_LINE_LOOP nPoints = mLines

ep: 上圖中的圖形

模式 線與點的數量關系
GL_LINES v0~v5( 6 ) = 2 * 3
GL_LINE_STRIP v0~v3( 4 ) = 3 + 1
GL_LINE_LOOP v0~v4( 5 ) = 5

0.工程目錄

完整的線元工程在,這一章的結尾;

OpenGL ES 2.0 (iOS)[03]:熟練圖元繪制,玩轉二維圖形本文核心目的就是熟練圖形的分析與繪制

工程目錄

圖中紅色箭頭所指的就是要修改的類,其中 VFVertexDatasManager 類是核心,它是負責整個工程的資料綁定和圖形繪制的;

藍色框所指的都是工程中的靜态頂點資料(當然你也可以動态生成并進行綁定繪制);

1. 繪制單一、交叉的線

OpenGL ES 2.0 (iOS)[03]:熟練圖元繪制,玩轉二維圖形本文核心目的就是熟練圖形的分析與繪制

LINES

  • 圖形分析
  • 首先它們都是線,是以選擇的是 線模式;
  • 左側就是一條線 -> GL_LINES,有兩個頂點坐标,而且坐标是左底右高
  • 右側是兩條交叉線 -> GL_LINES,有四個頂點坐标
nPoints = 2 * mLines
  • 開始寫代碼
    • 資料源準備
// 位于 VFBaseGeometricVertexData.h
// 單線段
static const VFVertex singleLineVertices[] = {
      { 0.5f,  0.5f, 0.0f},
      {-0.5f, -0.5f, 0.0f},
};
// 交叉線
static const VFVertex crossLinesVertices[] = {
      // Line one
      { 0.5f,  0.5f, 0.0f},
      {-0.5f, -0.5f, 0.0f},
      // Line Two
      {-0.53f, 0.48f, 0.0f},
      { 0.55f, -0.4f, 0.0f},
};           

複制

  • 修改資料綁定方法
/**
   *  裝載資料
   */
    - (void)attachVertexDatas {
      self.currentVBOIdentifier = [self createVBO];
      self.drawInfo = [self drawInfoMaker];
      if (self.drawInfo.elementDataPtr) {
          self.currentElementVBOIdentifier = [self createVBO];
          [self bindVertexDatasWithVertexBufferID:self.currentElementVBOIdentifier
                                       bufferType:GL_ELEMENT_ARRAY_BUFFER
                                     verticesSize:self.drawInfo.elementDataSize
                                         datasPtr:self.drawInfo.elementDataPtr];
      }
      [self bindVertexDatasWithVertexBufferID:self.currentVBOIdentifier
                                   bufferType:GL_ARRAY_BUFFER
                                 verticesSize:self.drawInfo.dataSize
                                     datasPtr:self.drawInfo.dataPtr]; // CPU 記憶體首位址
      [self attachVertexArrays];
  }           

複制

關鍵的方法是

- (void)bindVertexDatasWithVertexBufferID: bufferType: verticesSize: datasPtr:

,如下:

/**
   *  使用頂點緩存對象
   *
   *  @param vertexBufferID 頂點緩存對象辨別
   */
    - (void)bindVertexDatasWithVertexBufferID:(GLuint)vertexBufferID
                                   bufferType:(GLenum)bufferType
                                 verticesSize:(GLsizeiptr)size
                                     datasPtr:(const GLvoid*)dataPtr {
    
      glBindBuffer(bufferType, vertexBufferID);
      // 建立 資源 ( context )
      glBufferData(bufferType,        // 緩存塊 類型
                   size,              // 建立的 緩存塊 尺寸
                   dataPtr,           // 要綁定的頂點資料
                   GL_STATIC_DRAW);   // 緩存塊 用途
}           

複制

還有

- (VFLineDrawInfo)drawLineInfoMaker

方法,生成相應圖形的資料源資訊,如下:

// 位于 VFVertexDatasManager 類的
// - (VFLineDrawInfo)drawLineInfoMaker; 方法中
        case VFDrawGeometryType_SingleLine: {
            
            dataSize                = sizeof(singleLineVertices);
            dataPtr                 = singleLineVertices;
            verticesIndicesCount    = (GLsizei)(sizeof(singleLineVertices) /
                                                sizeof(singleLineVertices[0]));
            primitiveMode           = VFPrimitiveModeLines;
            
            break;
        }
        case VFDrawGeometryType_CrossLines: {
            
            dataSize                = sizeof(crossLinesVertices);
            dataPtr                 = crossLinesVertices;
            verticesIndicesCount    = (GLsizei)(sizeof(crossLinesVertices) /
                                                sizeof(crossLinesVertices[0]));
            primitiveMode           = VFPrimitiveModeLines;
            
            break;
        }           

複制

其中

@property (assign, nonatomic) VFDrawInfo drawInfo;

是定義的資料源資訊結構體,具體資訊如下:

// 位于 VFVertexDatasManager 類中
typedef struct {
      // 資料所占的記憶體大小
      GLsizeiptr dataSize;
      // 資料的記憶體首位址
      const GLvoid *dataPtr;
      // 需要繪制的點數量
      GLsizei verticesIndicesCount;
      // 圖元的繪制類型
      VFPrimitiveMode primitiveMode;
      // 下标資料所占的記憶體大小
      GLsizeiptr elementDataSize;
      // 下标記憶體首位址
      const GLvoid *elementDataPtr;
      // 下标個數
      GLsizei elementIndicesCount;
} VFDrawInfo;           

複制

  • 修改繪制方法,直接擷取資訊即可
// 位于 VFVertexDatasManager 類中
#define GPUVBOMemoryPtr    (0)
/**
  *  繪制圖形
  */
 - (void)draw {
   glLineWidth(DEFAULT_LINE_WITH);
   if (self.drawInfo.elementIndicesCount) {
      glDrawElements(self.drawInfo.primitiveMode,
                     self.drawInfo.elementIndicesCount,
                     GL_UNSIGNED_BYTE,
                     GPUVBOMemoryPtr);  // GPU 記憶體中的首位址
     return;
 }
   glDrawArrays(self.drawInfo.primitiveMode,
                StartIndex, // 就是 0
                self.drawInfo.verticesIndicesCount);
}           

複制

其中

glLineWidth

函數是修改線的寬度的;

glDrawElements

是繪制下标的方法;這裡不需要用到,是以先不解釋;

  • 修改圖形顯示
// 位于 VFVertexDatasManager 類中
   /**
   *  繪制的幾何圖形類型
   */
 @property (assign, nonatomic) VFDrawGeometryType drawGeometry;

  // 位于 VFRenderWindow 類
 // 位于 .m 檔案的 263 行
  /**
   *  裝載頂點資料
   */
   - (void)prepareVertexDatas {
     [self.vertexManager setDrawGeometry:VFDrawGeometryType_CrossLines];
     [self.vertexManager attachVertexDatas];
}           

複制

這裡新增了一個枚舉類型的變量,drawGeometry ,目的是友善外部類進行操控,而進行何種類型圖形的繪制渲染,VFDrawGeometryType 定義如下:

// VFVertexDatasManager .h 檔案中
typedef NS_ENUM(NSUInteger, VFDrawGeometryType) {

      VFDrawGeometryType_SingleLine = 0,  // 單條線
      VFDrawGeometryType_CrossLines,      // 交叉線
    
      VFDrawGeometryType_MountainLines,   // 拆線山
    
      VFDrawGeometryType_TriangleLines,   // 線三角
      VFDrawGeometryType_RectangleLines,  // 線正方形
      VFDrawGeometryType_PentagonsLines,  // 線五邊形
      VFDrawGeometryType_HexagonsLines,   // 線六邊形
      VFDrawGeometryType_TrapezoidLines,  // 線梯形
      VFDrawGeometryType_PentagramLines,  // 線五角星
      VFDrawGeometryType_RoundLines,      // 線圓
    
      VFDrawGeometryType_LowPolyRectLines,// LP 線正方形
      VFDrawGeometryType_LowPolyPentLines,// LP 線五邊形
      VFDrawGeometryType_LowPolyHexLines, // LP 線六邊形
      VFDrawGeometryType_LowPolyTrazLines,// LP 線梯形
      VFDrawGeometryType_LowPolyStarLines,// LP 線五角星
    
      VFDrawGeometryType_BezierMountain,  // Bezier 山
      VFDrawGeometryType_BezierRound,     // Bezier 圓
      VFDrawGeometryType_BezierOval,      // Beizer 橢圓
};           

複制

這一節隻是,單線與交叉線的繪制;

  • 程式運作結果
OpenGL ES 2.0 (iOS)[03]:熟練圖元繪制,玩轉二維圖形本文核心目的就是熟練圖形的分析與繪制

2. 繪制折線

OpenGL ES 2.0 (iOS)[03]:熟練圖元繪制,玩轉二維圖形本文核心目的就是熟練圖形的分析與繪制

LINE STRIP MOUN

  • 圖形分析
  • 首先這是一條線,是以選擇的是 線模式;
  • 但是它是一條折線,即多段線首尾相接組成的線,而且沒有閉合,GL_LINES_STRIP 模式;
  • 有 7 個頂點,6條線 (nPoints = mLines + 1)
  • 開始寫代碼
    • 資料源
// 位于 VFBaseGeometricVertexData.h
// 折線(山丘)
static const VFVertex mountainLinesVertices[] = {
    // Point one
    {-0.9f, -0.8f, 0.0f},
    
    // Point Two
    {-0.6f, -0.4f, 0.0f},
    
    // Point Three
    {-0.4f, -0.6f, 0.0f},
    
    // Point Four
    { 0.05f, -0.05f, 0.0f},
    
    // Point Five
    {0.45f, -0.65f, 0.0f},
    
    // Point Six
    { 0.55f,  -0.345f, 0.0f},
    
    // Point Seven
    { 0.95f, -0.95f, 0.0f},
};           

複制

  • 修改資料綁定方法

    在 drawLineInfoMaker 類中增加新的内容,其它不變;

// 位于 VFVertexDatasManager 類的
// - (VFLineDrawInfo)drawLineInfoMaker; 方法中
        case VFDrawGeometryType_MountainLines: {
            
            dataSize                = sizeof(mountainLinesVertices);
            dataPtr                 = mountainLinesVertices;
            verticesIndicesCount    = (GLsizei)(sizeof(mountainLinesVertices) /
                                                sizeof(mountainLinesVertices[0]));
            primitiveMode           = VFPrimitiveModeLineStrip;
            
            break;
        }           

複制

  • 修改圖形的顯示
// 位于 VFRenderWindow 類
// 位于 .m 檔案的 263 行
/**
 *  裝載頂點資料
 */
 - (void)prepareVertexDatas {
    [self.vertexManager  setDrawGeometry:VFDrawGeometryType_MountainLines];
    [self.vertexManager attachVertexDatas];
}           

複制

  • 程式運作結果
OpenGL ES 2.0 (iOS)[03]:熟練圖元繪制,玩轉二維圖形本文核心目的就是熟練圖形的分析與繪制

3. 繪制幾何圖形

OpenGL ES 2.0 (iOS)[03]:熟練圖元繪制,玩轉二維圖形本文核心目的就是熟練圖形的分析與繪制

Triangle2Round.gif

OpenGL ES 2.0 (iOS)[03]:熟練圖元繪制,玩轉二維圖形本文核心目的就是熟練圖形的分析與繪制

LINE LOOP

  • 圖形分析

    多段線首尾相接組成的幾何形狀,GL_LINES_LOOP 模式;

nPoints = mLines
  • 開始寫代碼
    • 資料源(從左至右),其中五角星這個資料,可以利用内五邊形與外五邊形相結合的方法(當然内五邊形的點要做一個角度旋轉),生成相應的點;

      所有的點,都通過程式動态生成,如下:

OpenGL ES 2.0 (iOS)[03]:熟練圖元繪制,玩轉二維圖形本文核心目的就是熟練圖形的分析與繪制

這個類的計算原理是,建立極坐标系,确定起始點,再循環增加旋轉角度,就可以得到所有的點,包括圓的點(圓即正多邊形,不過它的邊數已經多到細到人眼無法識别,而出現曲線的效果,就像這一小節開始的動态圖一樣的原理,當然橢圓的點集也可以通過這種方式得到);

這兩個類在另外的工程裡面, Github: 動态計算點

它的小應用,你可以按照自己的想法盡情改寫......

OpenGL ES 2.0 (iOS)[03]:熟練圖元繪制,玩轉二維圖形本文核心目的就是熟練圖形的分析與繪制

紅框處的,就是點的生成方法;箭頭所指的函數是把生成的點資料按照一定的格式寫入檔案的方法(檔案會自動建立);

下面是具體的資料:

// 三角形
static const VFVertex triangleLinesVertices[] = {
    // Point one
      {0.000000, 0.500000, 0.000000},

    // Point Two
      {-0.433013, -0.250000, 0.000000},

    // Point Three
      {0.433013, -0.250000, 0.000000},
};           

複制

// 四邊形
static const VFVertex rectangleLinesVertices[] = {
    // Point one
      {-0.353553, 0.353553, 0.000000},
    
    // Point Two
      {-0.353553, -0.353553, 0.000000},
    
    // Point Three
      {0.353553, -0.353553, 0.000000},
    
    // Point Four
      {0.353553, 0.353553, 0.000000},
};           

複制

// 五邊形
static const VFVertex pentagonsLinesVertices[] = {
    // Line one
      {0.000000, 0.500000, 0.000000},
      
    // Line Two
      {-0.475528, 0.154509, 0.000000},
    
    // Line Three
      {-0.293893, -0.404509, 0.000000},
    
    // Line Four
      {0.293893, -0.404509, 0.000000},
    
    // Line Five
      {0.475528, 0.154509, 0.000000},
};           

複制

// 六邊形
static const VFVertex hexagonsLinesVertices[] = {
    // Point one
      {0.000000, 0.500000, 0.000000},
    
    // Point Two
      {-0.433013, 0.250000, 0.000000},
    
    // Point Three
      {-0.433013, -0.250000, 0.000000},
    
    // Point Four
      {-0.000000, -0.500000, 0.000000},
    
    // Point Five
      {0.433013, -0.250000, 0.000000},
    
    // Point Six
      {0.433013, 0.250000, 0.000000},
};           

複制

// 梯形
static const VFVertex trapezoidLinesVertices[] = {
    // Point one
      {0.430057, 0.350000, 0.000000},
    
    // Point Two
      {-0.430057, 0.350000, 0.000000},
    
    // Point Three
      {-0.180057, -0.350000, 0.000000},
    
    // Point Four
      {0.180057, -0.350000, 0.000000},
};           

複制

// 五角星形
static const VFVertex pentagramLinesVertices[] = {
    // Point one
      {0.000000, 0.500000, 0.000000},
    
    // Point Two
      {-0.176336, 0.242705, 0.000000},
    
    // Point Three
      {-0.475528, 0.154509, 0.000000},
    
    // Point Four
      {-0.285317, -0.092705, 0.000000},
    
    // Point Five
      {-0.293893, -0.404509, 0.000000},
    
    // Point Six
      {-0.000000, -0.300000, 0.000000},
    
    // Point Seven
      {0.293893, -0.404509, 0.000000},
    
    // Point Eight
      {0.285317, -0.092705, 0.000000},
    
    // Point Nine
      {0.475528, 0.154509, 0.000000},
    
    // Point Ten
      {0.176336, 0.242705, 0.000000},
};           

複制

圓的頂點資料在單獨的檔案中,

VFRound.h

,也是通過動态點生成的【因為點太多,是以單獨放在一個檔案中進行管理】;

  • 修改資料綁定方法,在 drawLineInfoMaker 方法中增加新的内容
//  位于 VFVertexDatasManager 類的
// - (VFLineDrawInfo)drawLineInfoMaker; 方法中
        case VFDrawGeometryType_TriangleLines: {
            
            dataSize                = sizeof(triangleLinesVertices);
            dataPtr                 = triangleLinesVertices;
            verticesIndicesCount    = (GLsizei)(sizeof(triangleLinesVertices) /
                                                sizeof(triangleLinesVertices[0]));
            primitiveMode           = VFPrimitiveModeLineLoop;
            
            break;
        }
        case VFDrawGeometryType_RectangleLines: {
            
            dataSize                = sizeof(rectangleLinesVertices);
            dataPtr                 = rectangleLinesVertices;
            verticesIndicesCount    = (GLsizei)(sizeof(rectangleLinesVertices) /
                                                sizeof(rectangleLinesVertices[0]));
            primitiveMode           = VFPrimitiveModeLineLoop;
            
            break;
        }
        case VFDrawGeometryType_PentagonsLines: {
            
            dataSize                = sizeof(pentagonsLinesVertices);
            dataPtr                 = pentagonsLinesVertices;
            verticesIndicesCount    = (GLsizei)(sizeof(pentagonsLinesVertices) /
                                                sizeof(pentagonsLinesVertices[0]));
            primitiveMode           = VFPrimitiveModeLineLoop;
            
            break;
        }
        case VFDrawGeometryType_HexagonsLines: {
            
            dataSize                = sizeof(hexagonsLinesVertices);
            dataPtr                 = hexagonsLinesVertices;
            verticesIndicesCount    = (GLsizei)(sizeof(hexagonsLinesVertices) /
                                                sizeof(hexagonsLinesVertices[0]));
            primitiveMode           = VFPrimitiveModeLineLoop;
            
            break;
        }
        case VFDrawGeometryType_TrapezoidLines: {
            
            dataSize                = sizeof(trapezoidLinesVertices);
            dataPtr                 = trapezoidLinesVertices;
            verticesIndicesCount    = (GLsizei)(sizeof(trapezoidLinesVertices) /
                                                sizeof(trapezoidLinesVertices[0]));
            primitiveMode           = VFPrimitiveModeLineLoop;
            
            break;
        }
        case VFDrawGeometryType_PentagramLines: {
            
            dataSize                = sizeof(pentagramLinesVertices);
            dataPtr                 = pentagramLinesVertices;
            verticesIndicesCount    = (GLsizei)(sizeof(pentagramLinesVertices) /
                                                sizeof(pentagramLinesVertices[0]));
            primitiveMode           = VFPrimitiveModeLineLoop;
            
            break;
        }
        case VFDrawGeometryType_RoundLines: {
            
            dataSize                = sizeof(roundGeometry);
            dataPtr                 = roundGeometry;
            verticesIndicesCount    = (GLsizei)(sizeof(roundGeometry) /
                                                sizeof(roundGeometry[0]));
            primitiveMode           = VFPrimitiveModeLineLoop;
            
            break;
        }           

複制

  • 圖形顯示類(VFRenderWindow )也做相應的修改即可,位于 .m 檔案的 263 行;
  • 程式運作結果
OpenGL ES 2.0 (iOS)[03]:熟練圖元繪制,玩轉二維圖形本文核心目的就是熟練圖形的分析與繪制

TRI-ROUND

4. 繪制三角化的幾何圖形(Low Poly)

OpenGL ES 2.0 (iOS)[03]:熟練圖元繪制,玩轉二維圖形本文核心目的就是熟練圖形的分析與繪制

TRIANGLE STRIP FAN PLO

  • 圖形分析
    • 首先它們都是由線組成,線模式
    • 其次,它們的線是閉合的,首尾相接?GL_LINES_LOOP ?
    • 所謂首尾相接,形成閉合圖形,是起點直接到達終點,就是說起點隻會被經過一次,就是最後閉合的那一次;觀察圖形,起點如果隻被經過一次,能不能用線繪制出來,很難吧,特别是最後一個,是以這裡直接用 GL_LINES_STRIP 模式,之後任意編排線經過點的順序,即可。(當然,如果你有興趣的話,也可以寫一個算法去計算點被經過最少的次數下,圖形可以完整繪制出來)**
    • 點可能會多次被經過,那麼就是說,這個點要被程式排程多次,但是 glDrawArrays 隻能一個頂點被排程一次啊。是以這裡要用它的兄弟函數 glDrawElements 這個函數的意思就是繪制成員,頂點資料的下标就是它的成員,即通過頂點資料的成員來通路資料而進行靈活繪制。
glDrawElements 根據頂點資料在記憶體的下标進行繪制的方法
glDrawElements
void glDrawElements(GLenum mode, GLsizei count,GLenum type, const GLvoid* indices)
mode 隻能是以下幾種:GL_POINTS、GL_LINES、GL_LINE_STRIP、GL_LINE_LOOP、GL_TRIANGLES、GL_TRIANGLE_STRIP、GL_TRIANGLE_FAN
count indices 的數量
type 下标的資料類型:GL_UNSIGNED_BYTE、GL_UNSIGNED_SHORT、GL_UNSIGNED_INT(它隻能在使用了OES_element_index_uint 才能使用)
indices 下标在記憶體中的首位址(如果使用了 VBO,就是 GPU 記憶體中的首位址,若不是,則為 CPU 記憶體中的首位址)
  • 開始寫代碼
    • VFLineDrawInfo 增加了對下标繪制的支援
typedef struct {
      // 資料所占的記憶體大小
      GLsizeiptr dataSize;
      // 資料的記憶體首位址
      const GLvoid *dataPtr;
      // 需要繪制的點數量
      GLsizei verticesIndicesCount;
      // 圖元的繪制類型
      VFPrimitiveMode primitiveMode;
      // 下标資料所占的記憶體大小
      GLsizeiptr elementDataSize; // 在這.....
      // 下标記憶體首位址
      const GLvoid *elementDataPtr; // 在這.....
      // 下标個數
      GLsizei elementIndicesCount; // 在這.....
} VFLineDrawInfo;           

複制

  • 在原來的線資料基礎下,增加對應圖形的下标資料

    這裡選取下标的原則是,讓每一個點都盡可能少地被經過,進而完成圖形的繪制,目的就是為了節省資源。

// 四邊形的下标資料
static const GLubyte rectangleElementIndeices[] = {
      0, 1, 2,
      3, 0, 2,
};
// 五邊形的下标資料
static const GLubyte pentagonsElementIndeices[] = {
      4, 1, 0, 4,
      3, 1, 2, 3,
};
// 六邊形的下标資料
static const GLubyte hexagonsElementIndeices[] = {
      5, 1, 0, 5,
      4, 1, 2, 4,
      3, 2,
};
// 梯形的下标資料
static const GLubyte trapezoidElementIndeices[] = {
    1, 2, 3, 0,
    1, 3,
};
//五角星形的下标資料
static const GLubyte pentagramElementIndeices[] = {
      1, 2, 3, 4,
      5, 6, 7, 8,
      9, 0, 1,
      9, 7, 5, 3, 1,
      5, 7, 1 
};           

複制

  • 修改資料綁定方法

綁定新增加的下标資料支援,使用 VBO 的方式(雖然前面已經寫過,這裡重溫一下,因為這裡都是真正的應用)

// 核心方法
/**
 *  裝載資料
*/
  - (void)attachVertexDatas {
    self.currentVBOIdentifier = [self createVBO];
    
    self.lineInfo = [self drawLineInfoMaker];
    
    if (self.lineInfo.elementDataPtr) {
        self.currentElementVBOIdentifier = [self createVBO];
        [self bindVertexDatasWithVertexBufferID:self.currentElementVBOIdentifier
                                     bufferType:GL_ELEMENT_ARRAY_BUFFER
                                   verticesSize:self.lineInfo.elementDataSize
                                       datasPtr:self.lineInfo.elementDataPtr];
    }
    
    [self bindVertexDatasWithVertexBufferID:self.currentVBOIdentifier
                                 bufferType:GL_ARRAY_BUFFER
                               verticesSize:self.lineInfo.dataSize
                                   datasPtr:self.lineInfo.dataPtr]; // CPU 記憶體首位址
    
    [self attachVertexArrays];
}           

複制

在 drawLineInfoMaker 方法中新增内容:

// drawLineInfoMaker 裡面的新增内容
        case VFDrawGeometryType_LowPolyRectLines: {
            
            dataSize                = sizeof(rectangleLinesVertices);
            dataPtr                 = rectangleLinesVertices;
            elementDataSize         = sizeof(rectangleElementIndeices);
            elementDataPtr          = rectangleElementIndeices;
            elementIndicesCount     = (GLsizei)(sizeof(rectangleElementIndeices) /
                                                sizeof(rectangleElementIndeices[0]));
            primitiveMode           = VFPrimitiveModeLineStrip;
            
            break;
        }
        case VFDrawGeometryType_LowPolyPentLines: {
            
            dataSize                = sizeof(pentagonsLinesVertices);
            dataPtr                 = pentagonsLinesVertices;
            elementDataSize         = sizeof(pentagonsElementIndeices);
            elementDataPtr          = pentagonsElementIndeices;
            elementIndicesCount     = (GLsizei)(sizeof(pentagonsElementIndeices) /
                                                sizeof(pentagonsElementIndeices[0]));
            primitiveMode           = VFPrimitiveModeLineStrip;
            
            break;
        }
        case VFDrawGeometryType_LowPolyHexLines: {
            
            dataSize                = sizeof(hexagonsLinesVertices);
            dataPtr                 = hexagonsLinesVertices;
            elementDataSize         = sizeof(hexagonsElementIndeices);
            elementDataPtr          = hexagonsElementIndeices;
            elementIndicesCount     = (GLsizei)(sizeof(hexagonsElementIndeices) /
                                                sizeof(hexagonsElementIndeices[0]));
            primitiveMode           = VFPrimitiveModeLineStrip;
            
            break;
        }
        case VFDrawGeometryType_LowPolyTrazLines: {
            
            dataSize                = sizeof(trapezoidLinesVertices);
            dataPtr                 = trapezoidLinesVertices;
            elementDataSize         = sizeof(trapezoidElementIndeices);
            elementDataPtr          = trapezoidElementIndeices;
            elementIndicesCount     = (GLsizei)(sizeof(trapezoidElementIndeices) /
                                                sizeof(trapezoidElementIndeices[0]));
            primitiveMode           = VFPrimitiveModeLineStrip;
            
            break;
        }
        case VFDrawGeometryType_LowPolyStarLines: {
        
            dataSize                = sizeof(pentagramLinesVertices);
            dataPtr                 = pentagramLinesVertices;
            elementDataSize         = sizeof(pentagramElementIndeices);
            elementDataPtr          = pentagramElementIndeices;
            elementIndicesCount     = (GLsizei)(sizeof(pentagramElementIndeices) /
                                                sizeof(pentagramElementIndeices[0]));
            primitiveMode           = VFPrimitiveModeLineStrip;
            
            break;
        }           

複制

// 修改的資料綁定方法
/**
 *  使用頂點緩存對象
 *
 *  @param vertexBufferID 頂點緩存對象辨別
 */
  - (void)bindVertexDatasWithVertexBufferID:(GLuint)vertexBufferID
                               bufferType:(GLenum)bufferType
                             verticesSize:(GLsizeiptr)size
                                 datasPtr:(const GLvoid*)dataPtr {
    
    glBindBuffer(bufferType, vertexBufferID);
    
    // 建立 資源 ( context )
    glBufferData(bufferType,        // 緩存塊 類型
                 size,              // 建立的 緩存塊 尺寸
                 dataPtr,           // 要綁定的頂點資料
                 GL_STATIC_DRAW);   // 緩存塊 用途
}           

複制

  • 資料繪制方法中的下标繪制支援
// 修改的繪制方法
#define GPUVBOMemoryPtr    (0)
/**
 *  繪制圖形
 */
  - (void)draw {
    
    glLineWidth(DEFAULT_LINE_WITH);
    
    if (self.lineInfo.elementIndicesCount) {
        glDrawElements(self.lineInfo.primitiveMode,
                       self.lineInfo.elementIndicesCount,
                       GL_UNSIGNED_BYTE,
                       GPUVBOMemoryPtr);  // GPU 記憶體中的首位址
        return;
    }
    
    glDrawArrays(self.lineInfo.primitiveMode,
                 0,
                 self.lineInfo.verticesIndicesCount);
}           

複制

  • 程式運作結果
OpenGL ES 2.0 (iOS)[03]:熟練圖元繪制,玩轉二維圖形本文核心目的就是熟練圖形的分析與繪制

Rect-Star

5. 繪制曲線、圓形

OpenGL ES 2.0 (iOS)[03]:熟練圖元繪制,玩轉二維圖形本文核心目的就是熟練圖形的分析與繪制

BAISER

  • 圖形分析
    • 首先,它們都是曲線,它們都可以通過 GL_LINE_STRIP 條帶來進行繪制,而且後者也可能通過 GL_LINE_LOOP 進行繪制;
    • 根據上一節的圓可以知道,隻要線足夠短,以緻人眼無法分辨,那麼折線就可以形成曲線,但是有個問題?左邊的,折線怎麼控制它的方向呢,第一個點與第二個點之間的折線彎曲程度,要怎麼才能生成它的點集呢?
    • OpenGL 是以點為基礎進行圖元的繪制的,那麼隻要有一個方法動态地根據固定點去控制之間曲線點的生成,問題就解決了。坐标與點,那麼肯定是函數,要生成曲線,貝塞爾曲線函數就可以了(如果想不到,回憶你所見過的任一個圖形繪制軟體,就秒懂了,如:PS 的鋼筆工具, skecth 的鋼筆工具......)。
  • 知識補充( 貝塞爾曲線 )

    請看下面的 word/pdf 文檔《貝塞爾曲線推導》

    書寫貝塞爾曲線函數如下,具體實作也在Github: 動态計算點 這裡

OpenGL ES 2.0 (iOS)[03]:熟練圖元繪制,玩轉二維圖形本文核心目的就是熟練圖形的分析與繪制

檔案

OpenGL ES 2.0 (iOS)[03]:熟練圖元繪制,玩轉二維圖形本文核心目的就是熟練圖形的分析與繪制

應用

  • 開始寫代碼
    • 資料源都在 檔案中,紅框處
OpenGL ES 2.0 (iOS)[03]:熟練圖元繪制,玩轉二維圖形本文核心目的就是熟練圖形的分析與繪制
  • 增加 VFDrawGeometryType 内容
VFDrawGeometryType_BezierMountain,
    VFDrawGeometryType_BezierRound,
    VFDrawGeometryType_BezierOval,           

複制

  • drawLineInfoMaker 裡面的新增内容
case VFDrawGeometryType_BezierMountain: {
            
            dataSize                = sizeof(_BEZMountain);
            dataPtr                 = _BEZMountain;
            verticesIndicesCount    = (GLsizei)(sizeof(_BEZMountain) /
                                                sizeof(_BEZMountain[0]));
            primitiveMode           = VFPrimitiveModeLineStrip;
            
            break;
        }
        case VFDrawGeometryType_BezierRound: {
        
            dataSize                = sizeof(_BEZRound);
            dataPtr                 = _BEZRound;
            verticesIndicesCount    = (GLsizei)(sizeof(_BEZRound) /
                                                sizeof(_BEZRound[0]));
            primitiveMode           = VFPrimitiveModeLineStrip;
            
            break;
        }
        case VFDrawGeometryType_BezierOval: {
            
            dataSize                = sizeof(_BEZOval);
            dataPtr                 = _BEZOval;
            verticesIndicesCount    = (GLsizei)(sizeof(_BEZOval) /
                                                sizeof(_BEZOval[0]));
            primitiveMode           = VFPrimitiveModeLineStrip;
            
            break;
        }           

複制

  • 當然圖形顯示類,也要改咯!
  • 程式運作結果
OpenGL ES 2.0 (iOS)[03]:熟練圖元繪制,玩轉二維圖形本文核心目的就是熟練圖形的分析與繪制

Bezier

  • 完整的線元工程, Github:DrawGeometries_Lines

二、圖元繪制之三角形

Triangles,就是多個三角形;

Triangle Strip, 指條帶,互相連接配接的三角形;

Triangle Fan, 指扇面,互相連接配接的三角形;

OpenGL ES 2.0 (iOS)[03]:熟練圖元繪制,玩轉二維圖形本文核心目的就是熟練圖形的分析與繪制

圖1:三角形模式

OpenGL ES 2.0 (iOS)[03]:熟練圖元繪制,玩轉二維圖形本文核心目的就是熟練圖形的分析與繪制

圖2:STRIP

OpenGL ES 2.0 (iOS)[03]:熟練圖元繪制,玩轉二維圖形本文核心目的就是熟練圖形的分析與繪制

圖3:FAN

模式 三角形與點的數量關系
GL_TRIANGLES nPoints = 3 * mTriangles
GL_TRIANGLE_STRIP nPoints = mTriangles + 2
GL_TRIANGLE_FAN nPoints = mTriangles + 2

ep: 圖1 中的圖形

模式 三角形與點的數量關系
GL_TRIANGLES v0~v5( 6 ) = 3 * 2
GL_TRIANGLE_STRIP v0~v4( 5 ) = 3+ 2
GL_TRIANGLE_FAN v0~v4( 5 ) = 3+ 2

0. 工程目錄

OpenGL ES 2.0 (iOS)[03]:熟練圖元繪制,玩轉二維圖形本文核心目的就是熟練圖形的分析與繪制

工程目錄

這裡沒有什麼太大的變化,隻是資料的集合發生了一些變化而已;

1. 繪制基本幾何圖形

OpenGL ES 2.0 (iOS)[03]:熟練圖元繪制,玩轉二維圖形本文核心目的就是熟練圖形的分析與繪制

TRIANGLE STRIP FAN

  • 圖形分析
    • 首先,第一張圖檔每一個圖形都是一個面,但是 OpenGL 隻能直接繪制三角面,是以必須把圖形分解成三角面才能進行繪制;
    • 以下就是分解成三角面之後的圖形:
OpenGL ES 2.0 (iOS)[03]:熟練圖元繪制,玩轉二維圖形本文核心目的就是熟練圖形的分析與繪制

TRIANGLE LINESON

當然你也可以按照自己的方式進行分解,一定要遵守這裡的點、三角形關系

OpenGL ES 2.0 (iOS)[03]:熟練圖元繪制,玩轉二維圖形本文核心目的就是熟練圖形的分析與繪制

不然圖形是不能正确地繪制出來的;

  • 這裡容易出問題的是最後一個圖形(五角星形),三角形與點的關系:10(點的數量) = 10(分割出來的三角形數量) + 2,很明顯是不相等的,是以 10 個點是不可能繪制出來這個圖形的,隻能再增加兩個點;除了點的數量問題外,它還不是一個條帶(或者說用條帶來描述并不合适),它更适合用扇面來描述,即 GL_TRIANGLE_FAN;
  • 開始寫代碼
    • 資料源,它們都可以通過 FAN 或 STRIP 進行繪制,當然那個點用得少而且圖形繪制完整,以及友善,就用那個;像五角星那個圖形這麼麻煩,當然不做兩種試驗了;STRIP 模式下的點的分布要特别注意,偶數下标在上面,奇數下标在下面【把圖形壓扁,你就能看出來了】
// 三角形
static const VFVertex triangleTrianglesVertices[] = {
      // Point V0
      {0.000000, 0.500000, 0.000000},
    
      // Point V1
    {-0.433013, -0.250000, 0.000000},
    
      // Point V2
      {0.433013, -0.250000, 0.000000},
};           

複制

// 四邊形( 0,1,2,3,0,2 )
static const VFVertex rectangleTrianglesVertices[] = {

      // GL_TRIANGLE_FAN
      // Point V0
    {-0.353553, 0.353553, 0.000000},    // V0
    
      // Point V1
    {-0.353553, -0.353553, 0.000000},   // V1
    
      // Point V2
    {0.353553, -0.353553, 0.000000},    // V2
    
      // Point V3
       {0.353553, 0.353553, 0.000000},     // V3
    
    // GL_TRIANGLE_STRIP
//    // Point V0
//    {-0.353553, 0.353553, 0.000000},    // V0
//    
//    // Point V1
//    {-0.353553, -0.353553, 0.000000},   // V1
//    
//    // Point V3
//    {0.353553, 0.353553, 0.000000},     // V3
//    
//    // Point V2
//    {0.353553, -0.353553, 0.000000},    // V2
};           

複制

// 五邊形
static const VFVertex pentagonsTrianglesVertices[] = {
    
    // GL_TRIANGLE_FAN
//    // Point V0
//  {0.000000, 0.500000, 0.000000},
//    
//    // Point V1
//  {-0.475528, 0.154509, 0.000000},
//    
//    // Point V2
//  {-0.293893, -0.404509, 0.000000},
//    
//    // Point V3
//  {0.293893, -0.404509, 0.000000},
//    
//    // Point V4
//  {0.475528, 0.154509, 0.000000},
    
      // GL_TRIANGLE_STRIP
      // Point V1
      {-0.475528, 0.154509, 0.000000},
      
      // Point V2
      {-0.293893, -0.404509, 0.000000},
    
      // Point V0
      {0.000000, 0.500000, 0.000000},
    
      // Point V3
      {0.293893, -0.404509, 0.000000},
    
      // Point V4
      {0.475528, 0.154509, 0.000000},
};           

複制

// 六邊形
static const VFVertex hexagonsTrianglesVertices[] = {
    
      // GL_TRIANGLE_FAN
      // Point V0
    {0.000000, 0.500000, 0.000000},
    
      // Point V1
    {-0.433013, 0.250000, 0.000000},
    
      // Point V2
    {-0.433013, -0.250000, 0.000000},
    
      // Point V3
    {-0.000000, -0.500000, 0.000000},
    
      // Point V4
      {0.433013, -0.250000, 0.000000},
    
      // Point V5
      {0.433013, 0.250000, 0.000000},
    
    // GL_TRIANGLE_STRIP
//    // Point V1
//    {-0.433013, 0.250000, 0.000000},
//    
//    // Point V2
//    {-0.433013, -0.250000, 0.000000},
//    
//    // Point V0
//    {0.000000, 0.500000, 0.000000},
//    
//    // Point V3
//    {-0.000000, -0.500000, 0.000000},
//    
//    // Point V4
//    {0.433013, -0.250000, 0.000000},
//    
//    // Point V5
//    {0.433013, 0.250000, 0.000000},
//    
//    // Point V0
//    {0.000000, 0.500000, 0.000000},
};           

複制

// 梯形
static const VFVertex trapezoidTrianglesVertices[] = {
    
      // GL_TRIANGLE_FAN
  //    // Point V0
  //    {0.430057, 0.350000, 0.000000},
  //    
  //    // Point V1
  //    {-0.430057, 0.350000, 0.000000},
  //    
  //    // Point V2
  //    {-0.180057, -0.350000, 0.000000},
   //    
  //    // Point V3
  //    {0.180057, -0.350000, 0.000000},
    
      // GL_TRIANGLE_STRIP
      // Point V0
      {0.430057, 0.350000, 0.000000},
    
      // Point V1
      {-0.430057, 0.350000, 0.000000},
    
      // Point V3
      {0.180057, -0.350000, 0.000000},
      
      // Point V2
      {-0.180057, -0.350000, 0.000000},
};           

複制

// 五角星形 10 = (n - 2) -> n = 12
static const VFVertex pentagramTrianglesVertices[] = {
    
      // GL_TRIANGLE_FAN
      // Point V0
      {0.000000, 0.000000, 0.000000}, // 在原來的基礎上,增加的起點
    
      // Point V1
    {0.000000, 0.500000, 0.000000},
    
      // Point V2
    {-0.176336, 0.242705, 0.000000},
    
      // Point V3
    {-0.475528, 0.154509, 0.000000},
    
      // Point V4
    {-0.285317, -0.092705, 0.000000},
    
      // Point V5
      {-0.293893, -0.404509, 0.000000},
    
      // Point V6
    {-0.000000, -0.300000, 0.000000},
    
      // Point V7
      {0.293893, -0.404509, 0.000000},
    
      // Point V8
      {0.285317, -0.092705, 0.000000},
    
      // Point V9
      {0.475528, 0.154509, 0.000000},
    
      // Point V10
      {0.176336, 0.242705, 0.000000},
    
      // Point V11
    {0.000000, 0.500000, 0.000000},// 在原來的基礎上,增加的終點
};           

複制

  • 資料的綁定(與線元一緻),隻是修改了 VFDrawGeometryType 枚舉和 drawLineInfoMaker 方法而已;
    • attachVertexDatas
/**
     *  裝載資料
     */
      - (void)attachVertexDatas {
        self.currentVBOIdentifier = [self createVBO];
        self.lineInfo = [self drawLineInfoMaker];
        if (self.lineInfo.elementDataPtr) {
            self.currentElementVBOIdentifier = [self createVBO];
            [self bindVertexDatasWithVertexBufferID:self.currentElementVBOIdentifier
                                         bufferType:GL_ELEMENT_ARRAY_BUFFER
                                       verticesSize:self.lineInfo.elementDataSize
                                           datasPtr:self.lineInfo.elementDataPtr];
      }
        [self bindVertexDatasWithVertexBufferID:self.currentVBOIdentifier
                                     bufferType:GL_ARRAY_BUFFER
                                   verticesSize:self.lineInfo.dataSize
                                       datasPtr:self.lineInfo.dataPtr]; // CPU 記憶體首位址
        [self attachVertexArrays];
}           

複制

- VFDrawGeometryType           

複制

// 在這呢......
typedef NS_ENUM(NSUInteger, VFDrawGeometryType) {
      VFDrawGeometryType_TriangleTriangles = 0,
      VFDrawGeometryType_RectangleTriangles,
      VFDrawGeometryType_PentagonsTriangles,
      VFDrawGeometryType_HexagonsTriangles,
      VFDrawGeometryType_TrapezoidTriangles,
      VFDrawGeometryType_PentagramTriangles,
      VFDrawGeometryType_RoundTriangles,
};           

複制

- drawInfoMaker 方法           

複制

// - (VFDrawInfo)drawInfoMaker 方法
// 在這呢......
    switch (self.drawGeometry) {
        case VFDrawGeometryType_TriangleTriangles: {
            
            dataSize                = sizeof(triangleTrianglesVertices);
            dataPtr                 = triangleTrianglesVertices;
            verticesIndicesCount    = (GLsizei)(sizeof(triangleTrianglesVertices) /
                                                sizeof(triangleTrianglesVertices[0]));
            primitiveMode           = VFPrimitiveModeTriangles;
            
            break;
        }
        case VFDrawGeometryType_RectangleTriangles: {
            
            dataSize                = sizeof(rectangleTrianglesVertices);
            dataPtr                 = rectangleTrianglesVertices;
            verticesIndicesCount    = (GLsizei)(sizeof(rectangleTrianglesVertices) /
                                                sizeof(rectangleTrianglesVertices[0]));
            primitiveMode           = VFPrimitiveModeTriangleFan;
            
            break;
        }
        case VFDrawGeometryType_PentagonsTriangles: {
            
            dataSize                = sizeof(pentagonsTrianglesVertices);
            dataPtr                 = pentagonsTrianglesVertices;
            verticesIndicesCount    = (GLsizei)(sizeof(pentagonsTrianglesVertices) /
                                                sizeof(pentagonsTrianglesVertices[0]));
            primitiveMode           = VFPrimitiveModeTriangleStrip;
            
            break;
        }
        case VFDrawGeometryType_HexagonsTriangles: {
            
            dataSize                = sizeof(hexagonsTrianglesVertices);
            dataPtr                 = hexagonsTrianglesVertices;
            verticesIndicesCount    = (GLsizei)(sizeof(hexagonsTrianglesVertices) /
                                                sizeof(hexagonsTrianglesVertices[0]));
            primitiveMode           = VFPrimitiveModeTriangleFan;
            
            break;
        }
        case VFDrawGeometryType_TrapezoidTriangles: {
            
            dataSize                = sizeof(trapezoidTrianglesVertices);
            dataPtr                 = trapezoidTrianglesVertices;
            verticesIndicesCount    = (GLsizei)(sizeof(trapezoidTrianglesVertices) /
                                                sizeof(trapezoidTrianglesVertices[0]));
            primitiveMode           = VFPrimitiveModeTriangleStrip;
            
            break;
        }
        case VFDrawGeometryType_PentagramTriangles: {
            
            dataSize                = sizeof(pentagramTrianglesVertices);
            dataPtr                 = pentagramTrianglesVertices;
            verticesIndicesCount    = (GLsizei)(sizeof(pentagramTrianglesVertices) /
                                                sizeof(pentagramTrianglesVertices[0]));
            primitiveMode           = VFPrimitiveModeTriangleFan;
            
            break;
        }
        case VFDrawGeometryType_RoundTriangles: {
            
            dataSize                = sizeof(roundGeometry);
            dataPtr                 = roundGeometry;
            verticesIndicesCount    = (GLsizei)(sizeof(roundGeometry) /
                                                sizeof(roundGeometry[0]));
            primitiveMode           = VFPrimitiveModeTriangleFan;
            
            break;
        }
    }           

複制

- draw 方法           

複制

#define GPUVBOMemoryPtr    (0)
  /**
     *  繪制圖形
   */
      - (void)draw {
    
        if (self.lineInfo.elementIndicesCount) {
            glDrawElements(self.lineInfo.primitiveMode,
                           self.lineInfo.elementIndicesCount,
                           GL_UNSIGNED_BYTE,
                           GPUVBOMemoryPtr);  // GPU 記憶體中的首位址
          return;
    }
    
        glDrawArrays(self.lineInfo.primitiveMode,
                     StartIndex, // 0
                     self.lineInfo.verticesIndicesCount);
}           

複制

  • 同樣要修改圖形顯示類(VFRenderWindow).m 檔案的 263 行;
  • 程式運作結果
OpenGL ES 2.0 (iOS)[03]:熟練圖元繪制,玩轉二維圖形本文核心目的就是熟練圖形的分析與繪制

TRI-ROUND Triangle

完整的程式代碼: Github DrawGeometries_Triangles

三、圖元繪制之點精靈

這裡不進行詳細講解,個人感覺在這裡講沒什麼意思,還是放在 Texture 紋理部分進行詳細講解會比較有用,而且好玩;

如果隻是學習 gl_PointSize 的話沒意思,結合 gl_PointCoord 去學習反而更有趣,不過這裡要有紋理的知識,是以先行不講了;

四、練練手

OpenGL ES 2.0 (iOS)[03]:熟練圖元繪制,玩轉二維圖形本文核心目的就是熟練圖形的分析與繪制

Challenges

這裡的目的不是為了繪制它們而進行繪制,而是針對圖元繪制做一個深入的學習,要學習分析圖形和尋找合适有效的繪制方式,而且還要做到判斷資料的大緻生成方法方式是什麼,不然你永遠都隻是一個隻會搞代碼的搬運工而已;

程式設計可不僅僅是搞代碼;

0. 工程目錄

OpenGL ES 2.0 (iOS)[03]:熟練圖元繪制,玩轉二維圖形本文核心目的就是熟練圖形的分析與繪制

取消了采用結構體存取資料的方式,改用 Model 類,友善 OC 處理和傳輸;

1. 繪制一棵卡通樹

OpenGL ES 2.0 (iOS)[03]:熟練圖元繪制,玩轉二維圖形本文核心目的就是熟練圖形的分析與繪制

Tree

提示:進行兩次的 glDraw* 調用,分别繪制外邊的線和内部的填充圖

2. 繪制一張卡片

OpenGL ES 2.0 (iOS)[03]:熟練圖元繪制,玩轉二維圖形本文核心目的就是熟練圖形的分析與繪制

Card

提示:把資料分成左、右、右中線,三種,原因是左邊的資料是用貝塞爾曲線生成資料量非常大;主要是利用 glBufferSubData 與 glBufferData 的結合,以及 glVertexAttribPointer 的配合;

3. 繪制一棵草

OpenGL ES 2.0 (iOS)[03]:熟練圖元繪制,玩轉二維圖形本文核心目的就是熟練圖形的分析與繪制

Grass

注意:盡可以地用肉眼去判斷線的走向,用 動态計算點 的類做實驗,不斷成長起來吧。

完整的挑戰項目:Github DrawGeometries_Challenge