大家好!
本期丹丹将给大家解析OpenGL基本图元以及绘制~~
本期视频的链接地址是:
https://www.bilibili.com/video/av22494256/
大家也可以直接在bi站首页搜索:New程序媛 ,即可看到相应视频
本期的资源和代码下载链接是:
链接:https://pan.baidu.com/s/1EMTZ6sgLmxq5qk1DWHKXdQ 密码:hbjp
OpenGL中可以支持很多不同的图元类型,但总结起来可以归为点、线、三角形、四边形、多边形这几种。
比如我们能游戏中看到的模型它是这样的:
但实际我们的美术设计人员制作模型的时候是先有了这样的一个网格模型,然后再做的蒙皮处理的:
而我们的代码使用模型,实际就是读取到了这些模型的顶点信息,然后再逐步绘制出来的。
好啦~千里之行始于足下,我们先来看看OpenGL中的基本图元类型:
OpenGL枚举量 | 图元类型 |
GL_POINTS | 点 |
GL_LINES | 线 |
GL_POLYGON | 多边形 |
GL_TRAINGLES | 独立三角形 |
GL_QUADS | 独立四边形 |
GL_LINE_STRIP | 条带线 |
GL_LINE_LOOP | 循环线 |
GL_TRAINGLE_STRIP | 线性连续填充三角形串 |
GL_TRAINGLE_FAN | 扇形连续填充三角形串 |
GL_QUAD_STRIP | 连续填充四边形串 |
基本图元都有了,我们就先来看看绘制效果,咱们今天代码绘制出的东东
嘿嘿嘿,是不是充(nan)满(kan)创(wu)意(bi)?丹丹已经尽力啦~ 为了让推文看起来美那么一丢丢~
还有人会说,丹丹你在搞什么? 这跟游戏中我们看到的男神女神模型到底有什么关系?丹丹要告诉你们,有关系的,以后我们会慢慢的把东西变得好看,因为模型制作的工作不是由程序完成,所以原谅一下丹丹的绘图能力吧。时间紧张的情况下已经很尽力啦~
点and线:
void glPointSize(GLfloat size);
参数size设置点的宽度(以像素为单位),必须大于0.0,缺省时为1.0。
void glLineWidth(GLfloat width);
设置线宽(以像素为单位)。参数width必须大于0.0,缺省时为1.0。
线绘制时有三种方式:
GL_LINES:是指每两个点构成一条独立的线
GL_LINE_STRIP:每一个点都跟前一个顶点构成一条直线,是连续的线
GL_LINE_LOOP:跟GL_LINE_STRIP相同,并且线首尾相接,形成封闭连续线
三角形:
GL_TRIANGLES: 每三个点一组画一个三角形,三角形之间是独立的
GL_TRIANGLE_STRIP:从第三个点开始,每个点与前面的两个顶点组合画一个三角形,绘制的是线性连续的一组三角形
GL_TRIANGLE_FAN:第三个点开始,每个点与前一个点和第一个点组合画一个三角形,即扇形连续三角形
四边形原理与三角形类似,可以在之后的编码阶段大家进行实战的测试,观看结果。
有了基本图元,我们就要进行相应的绘制工作了,先看看绘制前的一些设置工作相对应的函数:
void glPolygonMode(GLenum face ,GLenum mode);
函数控制多边形的正面与背面绘制模式。
face:GL_ FRONT_ AND BACK
mode:可以是GL_ POINT、GL_LINE或者GL_FILL,它们分别是点、线和填充,默认是填充模式
void glFrontFace(GLenum mode)
设置多边形正面的判断方式。默认为GL_CCW即顶点按照逆时针排序的面作为正面。另一个模式是GL_CW即为顺时针方向的面是物体的正面。
void glCullFace(GLenum mode):
背面一般是玩家看不到的面,所以我们可以通过该函数设置需要抛弃(裁减掉)哪一面的多边形。
mode:可以是GL FRONT、 GL BACK或者 GL FRONT AND_BACK,分别表示正面、背面或者所有多边形。
要使命令生效,我们还需要使用 glEnable()和 GL CULL FACE参数来开启裁减;之后也可以使用 glDisable()和同样的参数来关闭它。
接下来要做的就是绘制数据的准备啦~
a.创建和分配缓存
OpenGL中的缓存对象是使用 GLuint来进行命名的。而GLuint是使用 glGenBuffers()函数来创建。
void glGenBuffers(Glsizei n, Gluint* buffers)
返回n个当前未使用的缓存对象名称,并保存到 buffers数组中。
调用 glGenBuffers()完成之后,我们们将在 buffers中得到一个缓存对象名称的数组,但是此时这些名称只是徒有其表。并不是真正的缓存对象。只有绑定到系统环境中的一个目标结点后,它所对应的缓存对象才会真正创建出来。
b.绑定目标
void glBindeBuffer(GLenum target,GLuint buffer);
OpenGL会采取一种最优内存管理策略,根据缓存对象完成绑定的情况来分配它对应的内存。可用的缓存目标结点我们称作target,我们使用到了如下的类型:
GL ARRAY BUFFER:顶点数组,保存glVertexAttribPointer()设置的顶点数据
GL ELEMENT ARRAY BUFFER:顶点索引数据,以便用于glDrawElements()等索引形式的绘制命令
c.向缓存输入数据
void glBufferData(GLenum target,GLsizeiptr size,const GLvoid * data,GLenum usage );
target参数意义同b步骤中的,size参数表示需要分配的空间大小,以字节为单位,data参数用于指定数据源,如果data不为空将会拷贝其数据来初始化这个缓冲区,否则只是分配预定大小的空间。预分配空间后,后续可以通过glBufferSubData来更新缓冲区内容。 usage参数指定数据使用模式,OpenGL对于缓存对象存储数据中的最优分配方案的管理,并不仅仅依赖于初始化绑定时的 target参数。另一个重要的参数就是usage,它必须是内置标准标识符中的一个,例如 GL STATIC DRAW或者 GL DYNAMIC COPY。注意这里的标识符名称要分解为两个部分去理解:第第一部分可以是 STATIC、 DYNAMIC或者 STREAM中的一个,而第二部分可以是DRAW、READ或者COPY中的一个。
STATIC:数据存储内容只写入一次,然后多次使用
DYNAMIC:数据存储内容会被反复写入和反复使用
STREAM:数据存储内容只写入一次,然后也不会被频繁使用
DRAW:数据存储内容由应用程序负责写入,并且作为 OpenGL绘制和图像命令的数据源
READ:数据存储内容通过 OpenGL反馈的数据写入,然后在应用程序进行查询时返回这些数据
COPY:数据存储内容通过 OpenGL反馈的数据写入,并且作为 OpenGL绘制和图像命令的数据源
最后一步骤,绘制!
大部分OpenGL绘制函数都是以Draw开始的。绘制可分为索引(GL ELEMENT ARRAY)和非索引两种形式。
非索引绘制函数:
void glDrawArrays(Glenum mode, Glint first, Glsizei count);
mode表示基本图元类型,first是指数据数组的起始位置,count表示数量,绘制的结束位置为first+count-1。
索引绘制函数:
void glDrawElements(GLenum mode, GLsizei count, GLenum type, const GL void* indices);
接下来咱们就看看今天的代码,以下部分都是代码中有编辑和修改的部分:
shader中我们添加了控制颜色的uniform量,如下:
接着是我们绘制的几个图形的顶点:
这些顶点可以用纸笔直接绘制计算出相对的位置,也可以使用类似TileMap工具来生成,如果想知道工具,请大家看看视频里头的说明哦
下图是初始化顶点数据,因为我们这次的图形都没有说明索引规律可循,所以都是直接使用的顶点数组绘制方式绘制出来的。
绘制代码如下图:
好啦~ 到此为止咱们本期的基本图元和绘制就介绍完毕了。
丹丹期待大家的意见和建议,欢迎小伙伴们积极留言