iOS --- OpenGLES之简单的图形绘制
2016-01-03 19:22
519 查看
在上一篇博客 iOS — OpenGLES之着色器(shader)的编译、链接及使用 中,简要介绍着色器(shader)的编译、链接及使用。本文将在之前一系列OpenGLES相关博客的基础上,使用OpenGLES绘制基本的图形。
以下两个例子中,对于Shader的编译使用等过程基本一致。
Fragment Shader如下:
在这里,我们要绘制一个红色的三角形,因此只需要传递Position参数即可。
颜色我们在Fragment Shader中指定即可 gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
iOS — OpenGLES之着色器[shader]的编译、链接及使用
以上代码使用已编译好的_glProgram着色器程序, 将Shader脚本的Position参数, 与_positionSlot插槽绑定起来.
则后续赋值给_positionSlot的顶点数据, 会直接传递至Shader中的Position参数.
这样, 就完成了Shader脚本的链接及使用步骤.
而glViewPort方法用于设置OpenGLES显示的区域.
绘制三角形的步骤也很简单:
1. 准备好顶点数据.
2. 将顶点数据传递给_positionSlot插槽, 即传递至Shader脚本中.
3. 使用glDrawArrays绘制图形, 使用presentRenderbuffer:将其呈现到屏幕上.
如果不使用VBO, 则每次直接从CPU内存中传递顶点数据到GPU内存中进行运算和渲染.
使用VBO就可将顶点数据存于GPU内存中, 而不需要每次都在CPU和GPU之间进行传递, 效率大大提升.
VBO适用于频繁读取顶点数据的场景, 在这里绘制三角形仅需三个顶点, 且不会重复使用. 因此不是VBO的典型使用场景. 这里的目的仅为了简单介绍VBO.
由上边代码可看出, VBO的使用方式比较固定.
一旦将VBO对象与GL_ARRAY_BUFFER绑定起来, 并将顶点数据传递过去, 则后续的顶点数据默认都从GL_ARRAY_BUFFER中取得.
glVertexAttribPointer方法的最后一个参数要注意:
当不使用VBO时, 要传递顶点数组的指针, 如
而当使用VBO时, 传递顶点数据在GL_ARRAY_BUFFER中的偏离量, 这里是0.
结果如图:
![](https://img-blog.csdn.net/20160103185827001)
Vertex Shader接收如下两个参数:
Position: 直接赋值给gl_Position变量, 用于指定顶点坐标.
SourceColor: 传递给Fragment Shader中的DestinationColor参数, 指定顶点颜色.
Fragment Shader如下:
将DestinationColor传递给gl_FragColor变量, 用于指定顶点的颜色.
我们即将绘制的矩形不再是纯色,而是通过shader传递颜色给每一个像素。所以SourceColor参数至关重要。
使用顶点数组绘制矩形时, Colors数组用于指定每个顶点的颜色, 与顶点数组Vertices中的顶点一一对应.
注意, 此时可留意Shader中的DestinationColor之前的varying关键字, 作用是使两个顶点之间的颜色平滑渐变.
关于三角形绘制方式, 还可以使用如下两种方式:
三种方式的绘制效果是一样的.
// 定义一个Vertex结构, 其中包含了坐标和颜色
typedef struct {
float Position[3];
float Color[4];
} Vertex;
// 顶点数组
const Vertex Vertices[] = {
{{-1,-1,0}, {0,0,0,1}},// 左下,黑色
{{1,-1,0}, {1,0,0,1}}, // 右下,红色
{{-1,1,0}, {0,0,1,1}}, // 左上,蓝色
{{1,1,0}, {0,1,0,1}}, // 右上,绿色
};
// 索引数组
const GLubyte Indices[] = {
0,1,2, // 三角形0
1,2,3 // 三角形1
};
// setup VBOs
// GL_ARRAY_BUFFER用于顶点数组
GLuint vertexBuffer;
glGenBuffers(1, &vertexBuffer);
// 绑定vertexBuffer到GL_ARRAY_BUFFER,
glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
// 给VBO传递数据
glBufferData(GL_ARRAY_BUFFER, sizeof(Vertices), Vertices, GL_STATIC_DRAW);
// GL_ELEMENT_ARRAY_BUFFER用于顶点数组对应的Indices,即索引数组
GLuint indexBuffer;
glGenBuffers(1, &indexBuffer);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(Indices), Indices, GL_STATIC_DRAW);
// 注意,未使用VBO时,glVertexAttribPointer的最后一个参数是指向对应数组的指针。
// 但是,当使用VBO时,glVertexAttribPointer的最后一个参数是要获取的参数在GL_ARRAY_BUFFER(每一个Vertex)的偏移量
// 取出Vertex结构体的Position,赋给_positionSlot
glVertexAttribPointer(_positionSlot, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), 0);
glEnableVertexAttribArray(_positionSlot);
// 注意,未使用VBO时,glVertexAttribPointer的最后一个参数是指向对应数组的指针。
// 但是,当使用VBO时,glVertexAttribPointer的最后一个参数是要获取的参数在GL_ARRAY_BUFFER(每一个Vertex)的偏移量
// Vertex结构体,偏移3个float的位置,即是Color值
glVertexAttribPointer(_colorSlot, 4, GL_FLOAT, GL_FALSE, sizeof(Vertex), (GLvoid )(sizeof(float) 3));
glEnableVertexAttribArray(_colorSlot);
// 使用glDrawArrays也可绘制,此时仅从GL_ARRAY_BUFFER中取出顶点数据,
// 而索引数组就可以不要了,即GL_ELEMENT_ARRAY_BUFFER实际上没有用到。
// glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
// 而使用glDrawElements的方式:本身就用到了索引,即GL_ELEMENT_ARRAY_BUFFER。
// 所以,GL_ARRAY_BUFFER和GL_ELEMENT_ARRAY_BUFFER两个都需要。
/**
* 参数1:三角形组合方式
* 参数2:索引数组中的元素个数,即6个元素,才能绘制矩形
* 参数3:索引数组中的元素类型
* 参数4:索引数组在GL_ELEMENT_ARRAY_BUFFER(索引数组)中的偏移量
*/
// 注意,未使用VBO时,glDrawElements的最后一个参数是指向对应索引数组的指针。
// 但是,当使用VBO时,参数4表示索引数据在VBO(GL_ELEMENT_ARRAY_BUFFER)中的偏移量
glDrawElements(GL_TRIANGLE_STRIP, sizeof(Indices)/sizeof(Indices[0]), GL_UNSIGNED_BYTE, 0);
“`
其中,Vertices数组中包含了每个顶点的位置信息及颜色。Indices数组是绘制矩形过程中使用的顶点索引数组。
使用顶点索引数组结合glDrawElements(),可减少存储和绘制重复顶点的资源消耗。
使用VBO可大大提高顶点数据在内存中的传递效率.
结果如图:
![](https://img-blog.csdn.net/20160103190444183)
iOS — OpenGLES之着色器(shader)的编译、链接及使用
OpenGL ES渲染管线与着色器
另外,绘制该矩形的时候,用到了Vertex Buffer Object(VBO),是用来提升OpenGLES绘制效率的,不是本文的重点所在,将在下一篇博客中再做介绍。
以下两个例子中,对于Shader的编译使用等过程基本一致。
绘制三角形
Shader脚本
Vertex Shader如下:attribute vec4 Position; // variable passed into void main(void) { gl_Position = Position; // gl_Position is built-in pass-out variable. Must config for in vertex shader }
Fragment Shader如下:
precision mediump float; void main(void) { gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); // must set gl_FragColor for fragment shader }
在这里,我们要绘制一个红色的三角形,因此只需要传递Position参数即可。
颜色我们在Fragment Shader中指定即可 gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
关联Shader参数
关于Shader的编译,链接及使用的更多内容, 请参考之前的博客:iOS — OpenGLES之着色器[shader]的编译、链接及使用
_glProgram = [ShaderOperations compileShaders:@"DemoShaderVertex" shaderFragment:@"DemoShaderFragment"]; glUseProgram(_glProgram); _positionSlot = glGetAttribLocation(_glProgram, "Position"); // 设置viewport glViewport(0, 0, self.view.frame.size.width, self.view.frame.size.height);
以上代码使用已编译好的_glProgram着色器程序, 将Shader脚本的Position参数, 与_positionSlot插槽绑定起来.
则后续赋值给_positionSlot的顶点数据, 会直接传递至Shader中的Position参数.
这样, 就完成了Shader脚本的链接及使用步骤.
而glViewPort方法用于设置OpenGLES显示的区域.
仅使用顶点数据绘制三角形
// 设置顶点数组 const GLfloat vertices[] = { 0.0f, 0.5f, 0.0f, -0.5f, -0.5f, 0.0f, 0.5f, -0.5f, 0.0f }; // 给_positionSlot传递vertices数据 glVertexAttribPointer(_positionSlot, 3, GL_FLOAT, GL_FALSE, 0, vertices); glEnableVertexAttribArray(_positionSlot); // Draw triangle glDrawArrays(GL_TRIANGLES, 0, 3); [_eaglContext presentRenderbuffer:GL_RENDERBUFFER];
绘制三角形的步骤也很简单:
1. 准备好顶点数据.
2. 将顶点数据传递给_positionSlot插槽, 即传递至Shader脚本中.
3. 使用glDrawArrays绘制图形, 使用presentRenderbuffer:将其呈现到屏幕上.
使用VBO绘制三角形
Vertex Buffer Object(VBO)是GPU存储空间里的一块缓冲区, 可用于存储顶点的所有信息. OpenGL在GPU中记录着VBO与对应的GPU内存地址.如果不使用VBO, 则每次直接从CPU内存中传递顶点数据到GPU内存中进行运算和渲染.
使用VBO就可将顶点数据存于GPU内存中, 而不需要每次都在CPU和GPU之间进行传递, 效率大大提升.
VBO适用于频繁读取顶点数据的场景, 在这里绘制三角形仅需三个顶点, 且不会重复使用. 因此不是VBO的典型使用场景. 这里的目的仅为了简单介绍VBO.
const GLfloat vertices[] = { 0.0f, 0.5f, 0.0f, -0.5f, -0.5f, 0.0f, 0.5f, -0.5f, 0.0f }; GLuint vertexBuffer; glGenBuffers(1, &vertexBuffer); // 绑定vertexBuffer到GL_ARRAY_BUFFER目标 glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer); // 为VBO申请空间,初始化并传递数据 glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); // 使用VBO时,最后一个参数0为要获取参数在GL_ARRAY_BUFFER中的偏移量 glVertexAttribPointer(_positionSlot, 3, GL_FLOAT, GL_FALSE, 0, 0); glEnableVertexAttribArray(_positionSlot); glDrawArrays(GL_TRIANGLES, 0, 3);
由上边代码可看出, VBO的使用方式比较固定.
一旦将VBO对象与GL_ARRAY_BUFFER绑定起来, 并将顶点数据传递过去, 则后续的顶点数据默认都从GL_ARRAY_BUFFER中取得.
glVertexAttribPointer方法的最后一个参数要注意:
当不使用VBO时, 要传递顶点数组的指针, 如
glVertexAttribPointer(_positionSlot, 3, GL_FLOAT, GL_FALSE, 0, vertices);
而当使用VBO时, 传递顶点数据在GL_ARRAY_BUFFER中的偏离量, 这里是0.
glVertexAttribPointer(_positionSlot, 3, GL_FLOAT, GL_FALSE, 0, 0);
结果如图:
绘制矩形
Shader脚本
Vertex Shader如下:// variable pass into attribute vec4 Position; // position of vertex attribute vec4 SourceColor; // color of vertex // variable pass out into fragment shader // varying means that calculate the color of every pixel between two vertex linearly(smoothly) according to the 2 vertex's color varying vec4 DestinationColor; void main(void) { DestinationColor = SourceColor; // gl_Position is built-in pass-out variable. Must config for in vertex shader gl_Position = Position; }
Vertex Shader接收如下两个参数:
Position: 直接赋值给gl_Position变量, 用于指定顶点坐标.
SourceColor: 传递给Fragment Shader中的DestinationColor参数, 指定顶点颜色.
Fragment Shader如下:
varying lowp vec4 DestinationColor; void main(void) { // must set gl_FragColor for fragment shader gl_FragColor = DestinationColor; }
将DestinationColor传递给gl_FragColor变量, 用于指定顶点的颜色.
我们即将绘制的矩形不再是纯色,而是通过shader传递颜色给每一个像素。所以SourceColor参数至关重要。
关联Shader参数
glUseProgram(_glProgram); _positionSlot = glGetAttribLocation(_glProgram, "Position"); _colorSlot = glGetAttribLocation(_glProgram, "SourceColor"); glViewport(0, 0, self.view.frame.size.width, self.view.frame.size.height);
仅使用顶点数据绘制矩形
// 顶点数组 const GLfloat Vertices[] = { -1,-1,0,// 左下,黑色 1,-1,0, // 右下,红色 -1,1,0, // 左上,蓝色 1,-1,0, // 右下,红色 -1,1,0, // 左上,蓝色 1,1,0, // 右上,绿色 }; // 颜色数组 const GLfloat Colors[] = { 0,0,0,1, // 左下,黑色 1,0,0,1, // 右下,红色 0,0,1,1, // 左上,蓝色 1,0,0,1, // 右下,红色 0,0,1,1, // 左上,蓝色 0,1,0,1, // 右上,绿色 }; // 纯粹使用顶点的方式,颜色与顶点要一一对应。 // 在shader中DestinationColor为最终要传递给OpenGLES的颜色,要使用varying,即两个顶点之间颜色平滑渐变 // 若不使用varying,则完全花掉。 // 取出Vertices数组中的坐标点值,赋给_positionSlot glVertexAttribPointer(_positionSlot, 3, GL_FLOAT, GL_FALSE, 0, Vertices); glEnableVertexAttribArray(_positionSlot); // 取出Colors数组中的每个坐标点的颜色值,赋给_colorSlot glVertexAttribPointer(_colorSlot, 4, GL_FLOAT, GL_FALSE, 0, Colors); glEnableVertexAttribArray(_colorSlot); // 以上两个slot分别于着色器脚本中的Positon,SourceColor两个参数 // 绘制两个三角形,不复用顶点,因此需要6个顶点坐标。 // V0-V1-V2, V3-V4-V5 /** * 参数1:三角形组合方式 * 参数2:从顶点数组的哪个offset开始 * 参数3:顶点个数6个 */ glDrawArrays(GL_TRIANGLES, 0, 6);
使用顶点数组绘制矩形时, Colors数组用于指定每个顶点的颜色, 与顶点数组Vertices中的顶点一一对应.
注意, 此时可留意Shader中的DestinationColor之前的varying关键字, 作用是使两个顶点之间的颜色平滑渐变.
关于三角形绘制方式, 还可以使用如下两种方式:
// 绘制两个三角形,复用两个顶点,因此只需要四个顶点坐标 // V0-V1-V2, V1-V2-V3 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); // 绘制两个三角形,复用两个顶点,因此只需要四个顶点坐标 // V0-V1-V2, V0-V2-V3 glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
三种方式的绘制效果是一样的.
使用顶点索引数组
// 顶点数组 const GLfloat Vertices[] = { -1,-1,0,// 左下,黑色 1,-1,0, // 右下,红色 -1,1,0, // 左上,蓝色 1,1,0, // 右上,绿色 }; // 颜色数组 const GLfloat Colors[] = { 0,0,0,1, // 左下,黑色 1,0,0,1, // 右下,红色 0,0,1,1, // 左上,蓝色 0,1,0,1, // 右上,绿色 }; // 索引数组,指定好了绘制三角形的方式 // 与glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);一样。 const GLubyte Indices[] = { 0,1,2, // 三角形0 1,2,3 // 三角形1 }; // 取出Vertices数组中的坐标点值,赋给_positionSlot glVertexAttribPointer(_positionSlot, 3, GL_FLOAT, GL_FALSE, 0, Vertices); glEnableVertexAttribArray(_positionSlot); // 注意,未使用VBO时,glVertexAttribPointer的最后一个参数是指向对应数组的指针。 // 取出Colors数组中的每个坐标点的颜色值,赋给_colorSlot glVertexAttribPointer(_colorSlot, 4, GL_FLOAT, GL_FALSE, 0, Colors); glEnableVertexAttribArray(_colorSlot); /** * 参数1:三角形组合方式 * 参数2:索引数组中的元素个数,即6个元素,才能绘制矩形 * 参数3:索引数组中的元素类型 * 参数4:索引数组 */ // 注意,未使用VBO时,glDrawElements的最后一个参数是指向对应索引数组的指针。 glDrawElements(GL_TRIANGLES, sizeof(Indices)/sizeof(Indices[0]), GL_UNSIGNED_BYTE, Indices); /** * 结论: * 不管使用哪种方式,顶点和颜色两个数组一定要一一对应。 * glDrawArrays: * glDrawElements: 引入了索引,则很方便地实现顶点的复用。 * * 在每个vertex上调用我们的vertex shader,以及每个像素调用fragment shader * 相比glDrawArray, 使用顶点索引数组可减少存储和绘制重复顶点的资源消耗 */ ``` indices是顶点索引数组, 指定绘制图形时的顶点顺序. 使用glDrawElements即可按照顶点索引数组指定的顺序进行绘制. ### 使用VBO
// 定义一个Vertex结构, 其中包含了坐标和颜色
typedef struct {
float Position[3];
float Color[4];
} Vertex;
// 顶点数组
const Vertex Vertices[] = {
{{-1,-1,0}, {0,0,0,1}},// 左下,黑色
{{1,-1,0}, {1,0,0,1}}, // 右下,红色
{{-1,1,0}, {0,0,1,1}}, // 左上,蓝色
{{1,1,0}, {0,1,0,1}}, // 右上,绿色
};
// 索引数组
const GLubyte Indices[] = {
0,1,2, // 三角形0
1,2,3 // 三角形1
};
// setup VBOs
// GL_ARRAY_BUFFER用于顶点数组
GLuint vertexBuffer;
glGenBuffers(1, &vertexBuffer);
// 绑定vertexBuffer到GL_ARRAY_BUFFER,
glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
// 给VBO传递数据
glBufferData(GL_ARRAY_BUFFER, sizeof(Vertices), Vertices, GL_STATIC_DRAW);
// GL_ELEMENT_ARRAY_BUFFER用于顶点数组对应的Indices,即索引数组
GLuint indexBuffer;
glGenBuffers(1, &indexBuffer);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(Indices), Indices, GL_STATIC_DRAW);
// 注意,未使用VBO时,glVertexAttribPointer的最后一个参数是指向对应数组的指针。
// 但是,当使用VBO时,glVertexAttribPointer的最后一个参数是要获取的参数在GL_ARRAY_BUFFER(每一个Vertex)的偏移量
// 取出Vertex结构体的Position,赋给_positionSlot
glVertexAttribPointer(_positionSlot, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), 0);
glEnableVertexAttribArray(_positionSlot);
// 注意,未使用VBO时,glVertexAttribPointer的最后一个参数是指向对应数组的指针。
// 但是,当使用VBO时,glVertexAttribPointer的最后一个参数是要获取的参数在GL_ARRAY_BUFFER(每一个Vertex)的偏移量
// Vertex结构体,偏移3个float的位置,即是Color值
glVertexAttribPointer(_colorSlot, 4, GL_FLOAT, GL_FALSE, sizeof(Vertex), (GLvoid )(sizeof(float) 3));
glEnableVertexAttribArray(_colorSlot);
// 使用glDrawArrays也可绘制,此时仅从GL_ARRAY_BUFFER中取出顶点数据,
// 而索引数组就可以不要了,即GL_ELEMENT_ARRAY_BUFFER实际上没有用到。
// glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
// 而使用glDrawElements的方式:本身就用到了索引,即GL_ELEMENT_ARRAY_BUFFER。
// 所以,GL_ARRAY_BUFFER和GL_ELEMENT_ARRAY_BUFFER两个都需要。
/**
* 参数1:三角形组合方式
* 参数2:索引数组中的元素个数,即6个元素,才能绘制矩形
* 参数3:索引数组中的元素类型
* 参数4:索引数组在GL_ELEMENT_ARRAY_BUFFER(索引数组)中的偏移量
*/
// 注意,未使用VBO时,glDrawElements的最后一个参数是指向对应索引数组的指针。
// 但是,当使用VBO时,参数4表示索引数据在VBO(GL_ELEMENT_ARRAY_BUFFER)中的偏移量
glDrawElements(GL_TRIANGLE_STRIP, sizeof(Indices)/sizeof(Indices[0]), GL_UNSIGNED_BYTE, 0);
“`
其中,Vertices数组中包含了每个顶点的位置信息及颜色。Indices数组是绘制矩形过程中使用的顶点索引数组。
使用顶点索引数组结合glDrawElements(),可减少存储和绘制重复顶点的资源消耗。
使用VBO可大大提高顶点数据在内存中的传递效率.
结果如图:
Demo
请参考: Demo参考资料
OpenGL Tutorial for iOS: OpenGL ES 2.0iOS — OpenGLES之着色器(shader)的编译、链接及使用
OpenGL ES渲染管线与着色器
另外,绘制该矩形的时候,用到了Vertex Buffer Object(VBO),是用来提升OpenGLES绘制效率的,不是本文的重点所在,将在下一篇博客中再做介绍。
相关文章推荐
- 峰回路转,Firefox 浏览器即将重返 iOS 平台
- 峰回路转,Firefox 浏览器即将重返 iOS 平台
- 不可修补的 iOS 漏洞可能导致 iPhone 4s 到 iPhone X 永久越狱
- iOS 12.4 系统遭黑客破解,漏洞危及数百万用户
- 每日安全资讯:NSO,一家专业入侵 iPhone 的神秘公司
- [转][源代码]Comex公布JailbreakMe 3.0源代码
- c#用for语句输出一个三角形的方法
- js判断一点是否在一个三角形内
- javascript 绘制矩形框
- 讲解iOS开发中基本的定位功能实现
- js判断客户端是iOS还是Android等移动终端的方法
- IOS开发环境windows化攻略
- 浅析iOS应用开发中线程间的通信与线程安全问题
- 检测iOS设备是否越狱的方法
- C#使用GDI绘制矩形的方法
- Python判断直线和矩形是否相交的方法
- .net平台推送ios消息的实现方法
- php使用for语句输出三角形的方法
- 使用css实现全兼容浏览器的三角形