2013年4月10日 星期三

OpenGL基本瞭解(六) (glDraw 相關指令函數)





glDraw 相關指令函數

  • glAccum
  • glBegin
  • glBlitFramebufferEXT
  • glBitmap
  • glClear
  • glCopyPixels
  • glDrawPixels
  • glReadPixels
  • glCopyTexSubImage*D
  • glCopyTexImage*D
  • glDrawArrays
  • glDrawElements
  • glDrawRangeElements



glDrawArrays所傳入的座標陣列需要連續關係
glDrawElements傳入的座標陣列是一個index座標,可以定義虛擬的連續座標陣列

先用glVertexPointer來指定座標陣列

glDrawArrays(A, B, C);
A: 使用何種形式畫圖
B: 指定從陣列中哪個點開始畫
C: 指定要畫的數量

glDrawElements(A,B,C,D)
A:使用何種形式畫圖
B: 指定陣列數量
C: 陣列的型態
D: index陣列 (可以讓原本不具連續的座標陣列 變成虛擬連線的座標)


glDrawArrays 相當於

glBegin (mode)
for(i=0; i<count; i++)
{
     glArrayElement(first + i);
}
glEnd();
glBegin (mode)
for(i=0; i<count; i++)
{
     glArrayElement(indices[i]);
}
glEnd();
其实从这比较难看出区别,重点在于其参数上,后者有用到了索引
网络上的说法是:
 glDrawArrays传输或指定的数据是最终的真实数据,在绘制时效能更好
而glDrawElements指定的是真实数据的调用索引,在内存/显存占用上更节省
 glDrawArrays更快
glDrawElements所占的内存/显存容量更小
参考链接:



glDrawArrays簡單使用

static const GLfloat g_vertex_buffer_data[] = {
    -1.0f,-1.0f,-1.0f, // triangle 1 : begin
    -1.0f,-1.0f, 1.0f,
    -1.0f, 1.0f, 1.0f, // triangle 1 : end
    1.0f, 1.0f,-1.0f, // triangle 2 : begin
    -1.0f,-1.0f,-1.0f,
    -1.0f, 1.0f,-1.0f, // triangle 2 : end
    1.0f,-1.0f, 1.0f,
    -1.0f,-1.0f,-1.0f,
    1.0f,-1.0f,-1.0f,
    1.0f, 1.0f,-1.0f,
    1.0f,-1.0f,-1.0f,
    -1.0f,-1.0f,-1.0f,
    -1.0f,-1.0f,-1.0f,
    -1.0f, 1.0f, 1.0f,
    -1.0f, 1.0f,-1.0f,
    1.0f,-1.0f, 1.0f,
    -1.0f,-1.0f, 1.0f,
    -1.0f,-1.0f,-1.0f,
    -1.0f, 1.0f, 1.0f,
    -1.0f,-1.0f, 1.0f,
    1.0f,-1.0f, 1.0f,
    1.0f, 1.0f, 1.0f,
    1.0f,-1.0f,-1.0f,
    1.0f, 1.0f,-1.0f,
    1.0f,-1.0f,-1.0f,
    1.0f, 1.0f, 1.0f,
    1.0f,-1.0f, 1.0f,
    1.0f, 1.0f, 1.0f,
    1.0f, 1.0f,-1.0f,
    -1.0f, 1.0f,-1.0f,
    1.0f, 1.0f, 1.0f,
    -1.0f, 1.0f,-1.0f,
    -1.0f, 1.0f, 1.0f,
    1.0f, 1.0f, 1.0f,
    -1.0f, 1.0f, 1.0f,
    1.0f,-1.0f, 1.0f
};




// Draw the triangle !
glDrawArrays(GL_TRIANGLES, 0, 12*3); // 12*3 indices starting at 0 -> 12 triangles -> 6 squares



glDrawElements的參考用法
http://blog.csdn.net/ganggang1st/article/details/6846666



其中繪圖常用設定如下
GL_POINTS - 单独的将顶点画出来。
GL_LINES - 单独地将直线画出来。行为和 GL_TRIANGLES 类似。

GL_LINE_STRIP - 连贯地将直线画出来。行为和 GL_TRIANGLE_STRIP 类似。
GL_LINE_LOOP - 连贯地将直线画出来。行为和 GL_LINE_STRIP 类似,但是会自动将最后一个顶点和第一个顶点通过直线连接起来。
GL_TRIANGLES - 这个参数意味着OpenGL使用三个顶点来组成图形。所以,在开始的三个顶点,将用顶点1,顶点2,顶点3来组成一个三角形。完成后,在用下一组的三个顶点(顶点4,5,6)来组成三角形,直到数组结束。


GL_TRIANGLE_STRIP - OpenGL的使用将最开始的两个顶点出发,然后遍历每个顶点,这些顶点将使用前2个顶点一起组成一个三角形。
所以 squareVertices[6~8]{1.0, -1.0, 6.0} 将与 squareVerticies[0~2]{-1.0, 1.0, -6.0} 和 squareVerticies[3~5]{-1.0, -1.0, -6.0} 生成一个三角形。
squareVertices[9~11]{1.0, 1,0, -6.0} 将与  squareVertices[3~5]{-1.0, -1.0, -6.0} 和squareVertices[6~8]{1.0, -1.0, 6.0} 生成三角形。
也就是说,P0,P1,P2这三个点组成一个三角形,P1,P2,P3这三个点也组成一个三角形。
注意的是, squareVerticies[0~2]表示的意思是:
squareVerticies[0] x坐标
squareVerticies[1] y坐标
squareVerticies[2] z坐标
GL_TRIANGLE_FAN - 在跳过开始的2个顶点,然后遍历每个顶点,让OpenGL将这些顶点于它们前一个,以及数组的第一个顶点一起组成一个三角形。 squareVertices[6~8]{1.0, -1.0, 6.0} 将与 squareVerticies[3~5]{-1.0, -1.0, -6.0} (前一个)和 squareVerticies[0~2]{-1.0, 1.0, -6.0}(第一个).生成一个三角形。
也就是说,同样是P0,P1,P2,P3 这4个顶点。
1) 在 GL_TRIANGLE_STRIP 状态下是: P2、P1、P0 ; P3、P2、P1 这2个三角形。
2) 在 GL_TRIANGLE_FAN   状态下是: P2、P1、P0 ; P3、P2、P0 这2个三角形。

此處可參考網頁
http://blog.csdn.net/favormm/article/details/6920318





图中用两个三角形绘制了一个方形。(顺便说一下,OpenGL有一种规则GL_QUADS是用来直接绘制方形的,但是OpenGL ES不支持这种规则。)下面的代码分别用三种拓扑规则绘制同一个方形三次。

const int stride = 2 * sizeof(float);

float triangles[][2] = { {0, 0}, {0, 1}, {1, 1}, {1, 1}, {1, 0}, {0, 0} };
glVertexPointer(2, GL_FLOAT, stride, triangles);
glDrawArrays(GL_TRIANGLES, 0, sizeof(triangles) / stride);

float triangleStrip[][2] = { {0, 1}, {0, 0}, {1, 1}, {1, 0} };
glVertexPointer(2, GL_FLOAT, stride, triangleStrip);
glDrawArrays(GL_TRIANGLE_STRIP, 0, sizeof(triangleStrip) / stride);

float triangleFan[][2] = { {0, 0}, {0, 1}, {1, 1}, {1, 0} };
glVertexPointer(2, GL_FLOAT, stride, triangleFan);
glDrawArrays(GL_TRIANGLE_FAN, 0, sizeof(triangleFan) / stride);


OpenGL中关于线的图元拓扑规则有三个,分别是:separatelines, strips与loops。在strips与loops规则中,每一条件的结束点是下一条线的顶点,而loops更特别,第一条线的开始点是最后一条件的 结始点。如果你绘制图2.3中方形的边框,下面的代码分别用三种规则实现了。

const int stride = 2 * sizeof(float);

float lines[][2] = { {0, 0}, {0, 1},
    {0, 1}, {1, 1},
    {1, 1}, {1, 0},
    {1, 0}, {0, 0} };
glVertexPointer(2, GL_FLOAT, stride, lines);
glDrawArrays(GL_LINES, 0, sizeof(lines) / stride);

float lineStrip[][2] = { {0, 0}, {0, 1}, {1, 1}, {1, 0}, {0, 0} };
glVertexPointer(2, GL_FLOAT, stride, lineStrip);
glDrawArrays(GL_LINE_STRIP, 0, sizeof(lineStrip) / stride);

float lineLoop[][2] = { {0, 0}, {0, 1}, {1, 1}, {1, 0} };
glVertexPointer(2, GL_FLOAT, stride, lineLoop);
glDrawArrays(GL_LINE_LOOP, 0, sizeof(lineLoop) / stride);




另外 glDrawElements 是一个OPENGL的图元绘制函数,从数组中获得数据并渲染图元。
函数原型为:
void glDrawElements( 
        GLenum mode, 
        GLsizei count,
        GLenum type, 
        const GLvoid *indices
        );
其中:
    mode 指定绘制图元的类型,但是如果GL_VERTEX_ARRAY 没有被激活的话,不能生成任何图元。它应该是下列值之一:
        GL_POINTS, GL_LINE_STRIP, 
        GL_LINE_LOOP, GL_LINES, 
        GL_TRIANGLE_STRIP, 
        GL_TRIANGLE_FAN, 
        GL_TRIANGLES, 
        GL_QUAD_STRIP, 
        GL_QUADS, 
        GL_POLYGON

         
    count 为绘制图元的数量。
    type 为索引数组(indices)中元素的类型,只能是下列值之一: 
        GL_UNSIGNED_BYTE, 
        GL_UNSIGNED_SHORT, 
        GL_UNSIGNED_INT

        
    indices:指向索引数组的指针。

glDrawElements函数能够通过较少的函数调用绘制多个几何图元,而不是通过OPENGL函数调用来传递每一个顶点,法线,颜色信息。
你可以事先准备一系列分离的顶点、法线、颜色数组,并且调用一次glDrawElements把这些数组定义成一个图元序列。
当调用glDrawElements函数的时候,它将通过索引使用count个成序列的元素来创建一系列的几何图元。
glDrawElements修改的顶点属性在glDrawElements调用返回后的值具有不确定性。

例如,在 GL_COLOR_ARRAY 被激活后,当glDrawElements执行完成时,当前的颜色值是没有指定的。没有被修改的属性值保持不变。
可以在显示列表中包含 glDrawElements,当glDrawElements被包含进显示列表时,相应的顶点、法线、颜色数组数据也得进入显示列表的,因为那些数组指 针是ClientSideState的变量,在显示列表创建的时候而不是在显示列表执行的时候,这些数组指针的值影响显示列表。


使用glDrawElements可以稍稍減少資料點,參考下面例子。

vec2 vertices[6] = { vec2(0, 0), vec2(0, 1), vec2(1, 1), vec2(1, 1), vec2(1, 0), vec2(0, 0) };
glVertexPointer(2, GL_FLOAT, sizeof(vec2), (void*) vertices); glDrawArrays(GL_TRIANGLES, 0, 6);
 

Note that two vertices—(0, 0) and (1, 1)—appear twice in the vertex list. Vertex in- dexing can eliminate this redundancy. Here’s how: 點資料重複了
 

vec2 vertices[4] = { vec2(0, 0), vec2(0, 1), vec2(1, 1), vec2(1, 0) }; 
GLubyte indices[6] = { 0, 1, 2, 2, 3, 0};
glVertexPointer(2, GL_FLOAT, sizeof(vec2), vertices); 

glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_BYTE, (void*) indices); 


So, instead of sending 6 vertices to OpenGL (8 bytes per vertex), we’re now sending 4 vertices plus 6 indices (one byte per index). That’s a total of 48 bytes with glDrawArrays and 38 bytes with glDrawIndices. 減少了一些資料儲存。