在这篇文章里记录笔者在阅读《OpenGL(R) ES 2.0 Programming Guide》1-8章里面觉得重要的知识点,在开始分析代码之前,先分析两个api调用:
void glDrawArrays(GLenum mode , GLint first , GLsizei count )
mode specifies the primitive to render. Valid values are:
GL_POINTS
GL_LINES
GL_LINE_STRIP
GL_LINE_LOOP
GL_TRIANGLES
GL_TRIANGLE_STRIP
GL_TRIANGLE_FAN
first specifies the starting vertex index in the enabled vertex arrays
count specifies the number of indices to be drawn.
void glDrawElements(GLenum mode , GLsizei count ,
GLenum type , const GLvoid * indices )
mode specifies the primitive to render. Valid values are:
GL_POINTS
GL_LINES
GL_LINE_STRIP
GL_LINE_LOOP
GL_TRIANGLES
GL_TRIANGLE_STRIP
GL_TRIANGLE_FAN
count specifies the number of indices
type specifies the type of element indices stored in indices.Valid
values are:
GL_UNSIGNED_BYTE
GL_UNSIGNED_SHORT
GL_UNSIGNED_INT —optional (can be used only if the
OES_element_index_uint extension is implemented)
indices specifies a pointer to location where element indices are stored.
大致概括下这块书上的分析,glDrawArrays在大量接续的,不共享的图元绘制情况下有很大优势;
但是在现实的绘制场景中(连续而大量共享顶点),glDrawElements能表现出更优的性能,减少内存和子宽的占用;比如在下面的场景中:
使用glDrawArrays实现,
#define VERTEX_POS_INDX 0
#define NUM_FACES 6
GLfloat vertices[] = { … }; // (x, y, z) per vertex
glEnableVertexAttribArray(VERTEX_POS_INDX);
glVertexAttribPointer(VERTEX_POS_INDX, 3, GL_FLOAT, GL_FALSE,
0, vertices);
for (i=0; i<NUM_FACES; i++)
{
glDrawArrays(GL_TRIANGLE_FAN, first, 4);
first += 4;
}
或者
glDrawArrays(GL_TRIANGLES, 0, 36);
为了绘制这个立方体,需要为每个面调用一次glDrawArrays,并且要为每个复用顶点进行复制,意味着本来的8个顶点,我们需要分配24个顶点的内存。或者36个顶点内存。
使用glDrawElements实现,
#define VERTEX_POS_INDX 0
GLfloat vertices[] = { … };// (x, y, z) per vertex
GLubyte indices[36] = { 0, 1, 2, 0, 2, 3,
0, 3, 4, 0, 4, 5,
0, 5, 6, 0, 6, 1,
7, 6, 1, 7, 1, 2,
7, 4, 5, 7, 5, 6,
7, 2, 3, 7, 3, 4 };
glEnableVertexAttribArray(VERTEX_POS_INDX);
glVertexAttribPointer(VERTEX_POS_INDX, 3, GL_FLOAT, GL_FALSE,
0, vertices);
glDrawElements(GL_TRIANGLES, sizeof(indices)/sizeof(GLubyte),
GL_UNSIGNED_BYTE, indices);
接下来我们分析下书本上的几个例子,这里贴出相关Opengles的部分,因为EGL部分都是通用的esutils来实现的,源码里面。书本上有几个知识点没有在例子代码上写出来,文章后面我单独列一下。
Simple_VertexShader:
//
// Book: OpenGL(R) ES 2.0 Programming Guide
// Authors: Aaftab Munshi, Dan Ginsburg, Dave Shreiner
// ISBN-10: 0321502795
// ISBN-13: 9780321502797
// Publisher: Addison-Wesley Professional
// URLs: http://safari.informit.com/9780321563835
// http://www.opengles-book.com
//
// Simple_VertexShader.c
//
// This is a simple example that draws a rotating cube in perspective
// using a vertex shader to transform the object
//
#include <stdlib.h>
#include "esUtil.h"
typedef struct
{
// Handle to a program object
GLuint programObject;
// Attribute locations
GLint positionLoc;
// Uniform locations
GLint mvpLoc;
// Vertex daata
GLfloat *vertices;
GLuint *indices;
int numIndices;
// Rotation angle
GLfloat angle;
// MVP matrix
ESMatrix mvpMatrix;
} UserData;
///
// Initialize the shader and program object
//
int Init ( ESContext *esContext )
{
UserData *userData = esContext->userData;
GLbyte vShaderStr[] =
"uniform mat4 u_mvpMatrix; \n"
"attribute vec4 a_position; \n"
"void main() \n"
"{ \n"
" gl_Position = u_mvpMatrix * a_position; \n"
"} \n";
GLbyte fShaderStr[] =
"precision mediump float; \n"
"void main() \n"
"{ \n"
" gl_FragColor = vec4( 1.0, 0.0, 0.0, 1.0 ); \n"
"} \n";
// Load the shaders and get a linked program object
userData->programObject = esLoadProgram ( vShaderStr, fShaderStr );
// Get the attribute locations
userData->positionLoc = glGetAttribLocation ( userData->programObject, "a_position" );
// Get the uniform locations
userData->mvpLoc = glGetUniformLocation( userData->programObject, "u_mvpMatrix" );
// Generate the vertex data
userData->numIndices = esGenCube( 1.0, &userData->vertices,
NULL, NULL, &userData->indices );
// Starting rotation angle for the cube
userData->angle = 45.0f;
glClearColor ( 0.0f, 0.0f, 0.0f, 0.0f );
return TRUE;
}
///
// Update MVP matrix based on time
//
void Update ( ESContext *esContext, float deltaTime )
{
UserData *userData = (UserData*) esContext->userData;
ESMatrix perspective;
ESMatrix modelview;
float aspect;
// Compute a rotation angle based on time to rotate the cube
userData->angle += ( deltaTime * 40.0f );
if( userData->angle >= 360.0f )
userData->angle -= 360.0f;
// Compute the window aspect ratio
aspect = (GLfloat) esContext->width / (GLfloat) esContext->height;
// Generate a perspective matrix with a 60 degree FOV
esMatrixLoadIdentity( &perspective );
esPerspective( &perspective, 60.0f, aspect, 1.0f, 20.0f );
// Generate a model view matrix to rotate/translate the cube
esMatrixLoadIdentity( &modelview );
// Translate away from the viewer
esTranslate( &modelview, 0.0, 0.0, -2.0 );
// Rotate the cube
esRotate( &modelview, userData->angle, 1.0, 0.0, 1.0 );
// Compute the final MVP by multiplying the
// modevleiw and perspective matrices together
esMatrixMultiply( &userData->mvpMatrix, &modelview, &perspective );
}
///
// Draw a triangle using the shader pair created in Init()
//
void Draw ( ESContext *esContext )
{
UserData *userData = esContext->userData;
// Set the viewport
glViewport ( 0, 0, esContext->width, esContext->height );
// Clear the color buffer
glClear ( GL_COLOR_BUFFER_BIT );
// Use the program object
glUseProgram ( userData->programObject );
// Load the vertex position
glVertexAttribPointer ( userData->positionLoc, 3, GL_FLOAT,
GL_FALSE, 3 * sizeof(GLfloat), userData->vertices );
glEnableVertexAttribArray ( userData->positionLoc );
// Load the MVP matrix
glUniformMatrix4fv( userData->mvpLoc, 1, GL_FALSE, (GLfloat*) &userData->mvpMatrix.m[0][0] );
// Draw the cube
glDrawElements ( GL_TRIANGLES, userData->numIndices, GL_UNSIGNED_INT, userData->indices );
eglSwapBuffers ( esContext->eglDisplay, esContext->eglSurface );
}
///
// Cleanup
//
void ShutDown ( ESContext *esContext )
{
UserData *userData = esContext->userData;
if ( userData->vertices != NULL )
{
free ( userData->vertices );
}
if ( userData->indices != NULL )
{
free ( userData->indices );
}
// Delete program object
glDeleteProgram ( userData->programObject );
}
int main ( int argc, char *argv[] )
{
ESContext esContext;
UserData userData;
esInitContext ( &esContext );
esContext.userData = &userData;
esCreateWindow ( &esContext, "Simple Texture 2D", 320, 240, ES_WINDOW_RGB );
if ( !Init ( &esContext ) )
return 0;
esRegisterDrawFunc ( &esContext, Draw );
esRegisterUpdateFunc ( &esContext, Update );
esMainLoop ( &esContext );
ShutDown ( &esContext );
}