OpenGL绘制方式

什么是图元:图元是组成3D物体的基础单元,是顶点的集合以预定义的方式结合在一起。而预定义的方式可以是一维的点,二维的线,甚至是三维的多边形等。 基础的图元类型可以直接被图形硬件进行光栅化操作,常见的基础图元类型如图所示:
这里写图片描述
除了基础图元类型,还有几何着色器中的临接图元以及细分着色器中的Patch图元,但是这些图元不能直接被图形硬件光栅化。

绘制点:点的图元为GL_POINTS呈正方形状,不受透视除法影响(也就是不近大远小)。点的大小都会自动修正成允许的间距和范围内的值。常用接口如下:
1.void glPointSize(GLfloat size):设置默认点大小。当开启GL_PROGRAM_POINT_SIZE时,就会以着色器中的gl_PointSize属性值进行设置。

2.void glGetFloatv(GLenum pname, GLfloat* data):获取点的指定属性值。
.pname表示点的属性。当为GL_POINT_SIZE_RANGE表示点的最小和最大值。当为GL_POINT_SIZE_GRANULARITY时表示相邻点之间的最小间距。
.data表示点的指定属性的数值。

绘制线:两点组合成一条线。图元类型如下:
1.线:图元为GL_LINES。是任意两个点组成的一条线。
2.线段:图元为GL_LINE_STRIP。是任意一个点可以作为起点和终点,这样一个点就要绘制两次,性能相对绘制线而言会低一些,但是绘制的线是连续的。
3.线环:图元为GL_LINE_LOOP。是在线段的基础上记录一下起点,当线段绘制完毕后就从终点到起点之间再绘制一条线,这样就将所有点绘制一条闭合的环。

常用接口如下:
1.void glLineWidth(GLfloat width):设置线宽,宽度值为width大小。

绘制三角形:三点组合成一个三角形。图元类型如下:
1.三角形:图元为GL_TRIANGLE。三角形是OpenGL唯一支持的多边形,也是光栅化硬件最喜欢的多边形。
2.三角形带:图元为GL_TRIANGLE_STRIP。是一串相连的三角形。由于只需绘制第一个三角形,其他三角形只需要指定一个顶点,从而可以节省大量时间开销。
3.三角形环:图元为GL_TRIANGLE_FAN。是一组围绕同一个中心顶点相连的三角形。跟三角形带一样,由于绘制的顶点数变少,从而可以节省大量时间开销。

常用绘制多边形接口如下:
1.void glFrontFace(GLenum mode):控制多边形正面的判断方式。
.mode表示正面模式。当为GL_CW表示指定顺时针方向环绕的多边形是正面;当为GL_CCW表示指定逆时针方向环绕的多边形是正面,这也是默认设置方式。

2.void glCullFace(GLenum mode):裁剪多边形的指定面。需要开启GL_CULL_FACE才能使用。
.mode表示指定面类型。通常是正面(GL_FRONT),背面(GL_BACK)以及正反面(GL_FRONT_AND_BACK)中的一种。

3.void glPolygonMode(GLenum face, GLenum mode):按照指定面和指定模式来绘制多边形。
.face通常可以取正面(GL_FRONT),背面(GL_BACK)以及正反面(GL_FRONT_AND_BACK)的一种。
.mode通常可以取线(GL_LINE),点(GL_POINT)以及实心填充(GL_FILL)中的一种。

4.void glPolygonOffset(GLfloat factor, GLfloat uints):多边形偏移函数。通常用在同一个像素上存在相同深度值时,为了让深度测试针对相同值的像素进行正确的绘制,就需要对指定像素的深度值进行偏移了。

OpenGL着色器:由于篇幅较多,已经额外写了篇博客来阐述。详情参照https://blog.csdn.net/zjz520yy/article/details/83865809

OpenGL缓存数据:也叫做OpenGL缓存对象。是用来为着色器成员变量准备数据用的缓存区域对象。常用的接口如下:
1.void glCreateBuffers(GLsizei n, GLuint* buffers):创建指定个数的缓存对象。
.n表示需要创建的缓存对象个数。
.buffers表示返回创建的缓存对象索引标志数组。

2.void glDeleteBuffers(GLsizei n, const GLuint* buffers):回收指定个数的缓存对象。参数同glCreateBuffers。

3.void glBindBuffer(GLenum target, GLuint buffer):绑定指定索引的缓存对象到指定的缓存目标点上。
.target表示缓存目标点。通常是GL_ARRAY_BUFFER(顶点缓存对象VBO),GL_ELEMENT_ARRAY_BUFFER(索引缓存对象EBO或者IBO),GL_UNIFORM_BUFFER ,GL_TEXTURE_BUFFER 等当中的一种。当为0时表示解绑指定缓存目标关联的缓存对象,当EBO存储在VAO(后面会讲解)中时,不能主动的解绑EBO,否则VAO中的EBO将会不存在,访问VAO中的EBO数据时会报错。此时只能在渲染结束后通过glDeleteBuffers来回收EBO。
.buffer表示缓存对象索引。

4.void glNamedBufferStorage(GLuint buffer, GLsizeiptr size, const void* data, GLbitfield flags):为缓存对象分配指定大小内存空间并初始化。
.buffer表示缓存对象标志。
.size表示缓存对象内存大小。
.data表示缓存对象初始化内容。
.flags表示缓存用途标识符。通常是GL_DYNAMIC_STORAGE_BIT,GL_MAP_READ_BIT,GL_MAP_WRITE_BIT等当中的一种。

5.void glNamedBufferSubData(GLuint buffer, GLintptr offset, GLsizeiptr size, const void* data):使用新的应用程序内存数据来替换用途为GL_DYNAMIC_STORAGE_BIT的缓存对象中的部分数据。
.buffer表示缓存对象标志。
.offset表示缓存对象的起始位置。
.size表示指定字节大小。
.data表示指定的新数据。

6.void glBufferData(GLenum target, GLsizeiptr size, const void* data, GLenum usage):将数据填充到缓存类型关联的缓存对象中。
.target表示缓存目标类型。
.size传输数据的大小。
.data表示传输的实际数据。
.usage表示显卡管理数据类型。通常是GL_STATIC_DRAW (数据不会或几乎不会改变),GL_DYNAMIC_DRAW(数据会被改变很多),GL_STREAM_DRAW (数据每次绘制时都会改变)中的一种。

内存分配和更新代码如下所示:

	// 顶点位置
    static const GLfloat vertex_positions[] =
    {
        -1.0f, -1.0f,  0.0f, 1.0f,
         1.0f, -1.0f,  0.0f, 1.0f,
        -1.0f,  1.0f,  0.0f, 1.0f,
        -1.0f, -1.0f,  0.0f, 1.0f,
    };

    // 顶点颜色
    static const GLfloat vertex_colors[] =
    {
        1.0f, 1.0f, 1.0f, 1.0f,
        1.0f, 1.0f, 0.0f, 1.0f,
        1.0f, 0.0f, 1.0f, 1.0f,
        0.0f, 1.0f, 1.0f, 1.0f
    };
    
	// 缓存对象
	GLuint buffer;
	// 创建新的缓存对象
	glCreateBuffers(1, &buffer);
	// 分配足够内存空间,并初始化为NULL
	glNamedBufferStorage(buffer, sizeof(vertex_positions) + sizeof(vertex_colors), nullptr, GL_DYNAMIC_STORAGE_BIT);
	// 更新位置信息到缓存对象
	glNamedBufferSubData(buffer, 0, sizeof(vertex_positions), vertex_positions);
	// 更新颜色信息到缓存对象
	glNamedBufferSubData(buffer, sizeof(vertex_positions), sizeof(vertex_colors), vertex_colors);

7.void glClearNamedBufferData(GLuint buffer, GLenum internalFormat, GLenum format, GLenum type, const void* data)
void glClearNamedBufferSubData(GLuint buffer, GLenum internalFormat, GLintptr offset GLsizeiptr size, GLenum format, GLenum type, const void* data):更新缓存对象中存储的数据。
.buffer表示缓存对象标志。
.internalFormat表示需要转换成的存储数据格式。
.offset表示缓存对象起始偏移位置。
.size表示需要更新的数据长度字节大小。
.format表示数据源格式。
.type表示数据源类型。
.data表示数据源内容。

8.void glCopyNamedBufferSubData(GLuint readBuffer, GLuint writeBuffer, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size):将指定开始位置和指定大小的缓存对象拷贝到指定位置位置的目标缓存对象中。
.readBuffer表示源缓存对象标志。
.writeBuffer表示目标缓存对象标志。
.readOffset表示源缓存对象起始位置。
.writeOffset表示目标缓存对象起始位置。
.size表示源缓存对象拷贝数据大小。

9.void glGetNamedBufferData(GLenum target, void* data)
void glGetNamedBufferSubData(GLenum target, GLintptr offset, GLsizeiptr size, void* data):将指定目标关联的缓存对象中指定大小内存数据拷贝到指定应用程序内存中。
.target表示目标类型,对应一个关联的缓存对象。
.data表示存储拷贝数据的应用程序内存。
.offset表示目标关联缓存对象的起始位置。
.size表示目标关联的缓存对象的拷贝数据大小。

10.void* glMapBuffer(GLenum target, GLenum access):获取一个应用程序内存指针,这个指针指向图形处理器中指定缓存目标所关联的缓存对象地址,从而以操作应用程序指针的方式来操作缓存对象。
.target表示缓存目标,通常关联一个缓存对象。
.access表示对缓存对象的访问模式,通常是GL_READ_ONLY(只读),GL_WRITE_ONLY(只写),GL_READ_WRITE(可读可写)中的一种。

11.void* glMapNamedBufferRange(GLuint target, GLintptr offset, GLsizeiptr length, GLbitfield access):将缓存对象的指定位置和大小的内存映射到应用程序的地址空间中,从而以操作应用程序指针的方式来操作缓存对象。
.target表示缓存目标,通常关联一个缓存对象。
.offset表示缓存对象的指定起始位置。
.length表示缓存对象的指定大小。
.access表示对缓存对象的访问模式,通常是GL_MAP_INVALIDATE_RANGE_BIT,GL_MAP_INVALIDATE_BUFFER_BIT,GL_MAP_FLUSH_EXPLICIT_BIT,GL_MAP_UNSYNCHRONIZED_BIT中的一个或者多个组合,具体每个模式的定义参见OpenGL官方文档。

12.void glFlushMappedNamedBufferRange(GLuint target, GLintptr offset, GLsizeiptr length):通知OpenGL,缓存对象中指定位置和大小的内容发生了变化。变化的内存必须由glMapNamedBufferRange指定,并且访问模式必须包含GL_MAP_FLUSH_EXPLICIT_BIT。
.target表示缓存目标,通常关联一个缓存对象。
.offset表示缓存对象的指定起始位置。
.length表示缓存对象的指定大小。

13.GLboolean glUnmapBuffer(GLuint target)
GLboolean glUnmapNamedBuffer(GLuint target):卸载指定缓存类型关联的缓存对象映射出去的应用程序内存地址。其中glUnmapBuffer与glMapBuffer配对使用,glUnmapNamedBuffer与glMapNamedBufferRange配对使用。
.target表示缓存目标,通常关联一个缓存对象。

缓存对象映射和卸载代码如下所示:

GLuint buffer;
FILE* f;
size_t fileSize;

// 打开文件并确保它的大小
f = fopen("data.dat", "rb");
fseek(f, 0, SEEK_END);
fileSize = ftell(f);
fseek(f, 0, SEEK_SET);

// 创建缓存对象并绑定到缓存目标上
glGenBuffers(1, &buffer);
glBindBuffer(GL_COPY_WRITE_BUFFER, buffer);

// 将应用程序内存数据NULL更新到缓存对象内存中。等价于使用glNamedBufferStorage进行分配内存和以NULL初始化
glBufferData(GL_COPY_WRITE_BUFFER, (GLsizei)fileSize, NULL, GL_STATIC_DRAW);

// 映射缓存对象到应用程序内存中
void* data = glMapBuffer(GL_COPY_WRITE_BUFFER, GL_WRITE_ONLY);

// 操作应用程序内存间接操作缓存对象
fread(data, 1, fileSize, f);

// 卸载缓存对象映射
glUnmapBuffer(GL_COPY_WRITE_BUFFER);
fclose(f);

顶点规范:就是设置着色器中顶点属性数据来源。可以是缓存对象数据,也可以是默认的静态数据。常用接口如下:
1.void glEnableVertexAttribArray(GLuint index):允许缓存对象数据传递给指定顶点属性。默认情况下是禁用的。
.index表示顶点着色器中指定索引的顶点属性。

2.void glDisableVertexAttribArray(GLuint index):禁止缓存对象数据传递给指定顶点属性。
.index表示顶点着色器中指定索引的顶点属性。

3.void glVertexAttribPointer(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid* pointer):将缓存对象中的数据转换成浮点数值并设置到指定顶点属性中。
.index表示顶点着色器中指定索引的顶点属性。
.size表示顶点属性中需要更新的元素个数(如:数组就是多个,否则就是1个)。
.type表示每个元素的数据类型。可以用的类型如下表所示:

标识符OpenGL类型
GL_BYTEGLbyte(有符号8位整数)
GL_UNSIGNED_BYTEGLubyte(无符号8位整数)
GL_SHORTGLshort(有符号16位整数)
GL_UNSIGNED_SHORTGLushort(无符号16位整数)
GL_INTGLint(有符号32位整数)
GL_UNSIGNED_INTGLuint(无符号32位整数)
GL_FIXEDGLfixed(有符号16位定点型)
GL_FLOATGLfloat(32位IEEE单精度浮点型)
GL_HALF_FLOATGLhalf(16位S1E5M10半精度浮点型)
GL_DOUBLEGLdouble(64位IEEE双精度浮点型)
GL_INT_2_10_10_10_REVGLuint(压缩数据类型)
GL_UNSIGNED_INT_2_10_10_10_REVGLuint(压缩数据类型)

.normalized表示是否归一化。当为false时,就会将数据强制转换成float类型。当为true时就会采用公式将数据转换成float类型。转换公式如下:
有符号整形转换成float类型公式为:
f = c / (2^b - 1)
无符号整形转换成float类型公式为:
f = (2c + 1) / (2^b - 1)
其中f表示最终的float数值,c表示输入的整数分量,b表示整数数据类型位数。
.stride表示连续顶点属性的偏移字节数。
.pointer表示数据源。

4.void glVertexAttribIPointer(GLuint index, GLint size, GLenum type, GLsizei stride, const GLvoid* pointer):将缓存对象中的整形数据设置到指定顶点属性中。参数同上面的glVertexAttribPointer,只是type必须是整形数据类型。

5.void glVertexAttribLPointer(GLuint index, GLint size, GLenum type, GLsizei stride, const GLvoid* pointer):将缓存对象中的双精度浮点数据设置到指定顶点属性中。参数同上面的glVertexAttribPointer,只是type必须是双精度浮点数据类型。

6.void glVertexAttrib{1234}{fds}(GLuint index, TYPE values)
void glVertexAttrib{1234}{fds}v(GLuint index, const TYPE* values)
void glVertexAttrib4{bsifd ub us ui}v(GLuint index, const TYPE* values):将静态数据强制转换成浮点类型数据并设置到指定顶点属性中。
.index表示顶点着色器中指定索引的顶点属性。
.values表示静态数据。

7.void glVertexAttrib4Nub(GLuint index, GLubyte x, GLubyte y, GLubyte z, GLubyte w)
void glVertexAttrib4N{bsi ub us ui}(GLuint index, const TYPE* values):将静态数据归一化处理转换成浮点类型数据并设置到指定顶点属性中。其中无符号的归一化到[0,1],有符号的归一化到[-1, 1]。参数跟上面第6点一样。

8.void glVertexAttribI{1234}{i ui}(GLuint index, TYPE values)
void glVertexAttribI{123}{i ui}v(GLuint index, const TYPE* values)
void glVertexAttribI4{bsi ub us ui}v(GLuint index, const TYPE* values):将整形静态数据设置到指定顶点属性中。
.index表示顶点着色器中指定索引的顶点属性。
.values表示静态数据。

9.void glVertexAttribL{1234}(GLuint index, TYPE values)
void glVertexAttribL{1234}v(GLuint index, const TYPE* values):将双精度浮点类型静态数据设置到指定顶点属性中。
.index表示顶点着色器中指定索引的顶点属性。
.values表示静态数据。

顶点数组对象(VAO):用来存储顶点属性(关联到顶点缓存对象)和元素缓存对象(关联到索引缓存对象)。只需要绑定VAO就可以使用里面存放的数据。
一个VAO存储数据如图所示:
图片来源与网络
常用接口如下:
1.void glGenVertexArrays(GLsizei n, GLuint* arrays):创建顶点数组对象。
.n表示对象标志。
.arrays表示数组对象。

2.void glBindVertexArray(GLuint array):绑定顶点数组对象。从绑定之处起,后面的配置顶点属性以及设置缓存对象等操作都会存储到顶点数组对象中。渲染接口中用到的顶点数据也必须在绑定了顶点数组对象后才能正常获取。
.array表示顶点数组对象。当为0时表示解绑上次绑定的顶点数组对象。

3.void glDeleteVertexArrays(GLsizei n, GLuint* arrays):回收顶点数组对象。
.n表示对象标志。

OpenGL绘制命令:绘制命令大致分为索引形式和非索引形式。
直接绘制:绘制命令以参数的形式传递到接口中。且常用接口如下所示:
1.void glDrawArrays(GLenum mode, GLint first, GLsizei count):从数据源数组中取指定位置和大小的数据,每个数据按照指定图元类型进行绘制。
.mode表示绘制的模式,也就是图元类型。通常是GL_POINTS(点),GL_LINES(线),GL_TRIANGLES(三角形)等图元中的一个。
.first表示数据源数组的起始位置。
.count表示数据源数组中读取的个数。

2.void glMultiDrawArrays(GLenum mode, const GLint* first, const GLsizei* count, GLsizei primCount):多次glDrawArrays调用。
.primCount表示元素总数。

等价于以下代码:

void glMultiDrawArrays(GLenum mode, const GLint* first, const GLsizei* count, GLsizei primCount)
{
	GLsizei i;
	for (i = 0; i < primCount; ++i)
	{
		glDrawArrays(mode, first[i], count[i]);
	}
}

3.void glDrawElements(GLenum mode, GLsizei count, GLenum type, const char* indices):从绑定到GL_ELEMENT_ARRAY_BUFFER目标的EBO中获取索引,然后用获取到的索引从绑定到GL_ARRAY_BUFFER目标的VBO中获取顶点数据,然后用获取到的顶点数据按照指定的图元类型进行渲染。
.mode表示绘制的模式,也就是图元类型。通常是GL_POINTS(点),GL_LINES(线),GL_TRIANGLES(三角形)等图元中的一个。
.count表示绘制顶点的个数。
.type表示索引的数据类型。
.indices表示索引数据源数组。

4.void glMultiDrawElements(GLenum mode, const GLint* count, GLenum type, const GLvoid* const* indices, GLsizei primCount):多次glDrawElements调用。
.primCount表示元素总数。

等价于以下代码:

void glMultiDrawElements(GLenum mode, const GLint* count, GLenum type, const GLvoid* const* indices, GLsizei primCount)
{
	GLsizei i;
	for (i = 0; i < primCount; ++i)
	{
		glDrawElements(mode, count[i], type, indices[i]);
	}
}

5.void glDrawElementsBaseVertex(GLenum mode, GLsizei count, GLenum type, const char* indices, GLint baseVertex):接口作用跟glDrawElements一致。只是最终索引值等于索引缓存对象中的索引值加上索引偏移值。
.baseVertex索引偏移值。

6.void glMultiDrawElementsBaseVertex(GLenum mode, const GLint* count, GLenum type, const GLvoid* const* indices, GLsizei primCount, const GLint* baseVertex):多次glDrawElementsBaseVertex调用。
.primCount表示元素总数。

等价于以下代码:

void glMultiDrawElementsBaseVertex(GLenum mode, const GLint* count, GLenum type, const GLvoid* const* indices, GLsizei primCount, const GLint* baseVertex)
{
	GLsizei i;
	for (i = 0; i < primCount; ++i)
	{
		glDrawElementsBaseVertex(mode, count[i], type, indices[i], baseVertex[i]);
	}
}

7.void glDrawRangeElements(GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const char* indices):接口作用跟glDrawElements一致。只是从索引缓存对象中获取到的索引值必须在指定区间内。
.start表示索引值的起始值。
.end表示索引值的结束值。

8.void glDrawRangeElementsBaseVertex(GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const char* indices, GLint baseVertex):接口作用跟glDrawElements一致。只是从索引缓存对象中获取的索引必须在指定区间范围内,而且最终索引等于索引缓存对象中的索引值加上索引偏移值。

间接绘制:绘制命令从绑定到GL_DRAW_INDIRECT_BUFFER的缓存对象中进行获取。且常用接口如下所示:
1.void glDrawArrayIndirect(GLenum mode, const GLvoid* indirect):特性跟glDrawArray一样。
.indirect表示绘制命令结构体。其定义如下所示:

typedef struct DrawArraysIndirectCommand_t
{
	GLuint count;	// 表示数据源数组中读取的个数。
	GLuint primCount;	// 表示实例个数。
	GLuint first;	// 表示数据源数组的起始位置。
	GLuint baseInstance;	// 多实例顶点属性偏移值
} DrawArraysIndirectCommand;

2.void glMultiDrawArrayIndirect(GLenum mode, const void* indirect, GLsizei drawCount, GLsizei stride):多次glDrawArrayIndirect的调用。
.drawCount表示绘制命令个数。
.stride表示参数命令结构体之间的间距。

3.void glDrawElementsIndirect(GLenum mode, GLenum type, const GLvoid* indirect):特性跟glDrawElements一致。
.indirect表示绘制命令结构体。其定义如下所示:

typedef struct DrawElementsIndirectCommand_t
{
	GLuint count;	// 表示绘制顶点的个数。
	GLuint primCount;	// 表示实例个数。
	GLuint firstIndex;	// 表示索引数组的起始位置。
	GLuint baseVertex;	// 表示索引偏移值
	GLuint baseInstance;	// 多实例顶点属性偏移值
} DrawElementsIndirectCommand;

4.void glMultiDrawElementsIndirect(GLenum mode, GLenum type, const void* indirect, GLsizei drawCount, GLsizei stride):多次glDrawElementsIndirect调用。
.drawCount表示绘制命令个数。
.stride表示参数命令结构体之间的间距。

多实例绘制:是一种连续执行相同的渲染命令来渲染大量几何体的方法,其中实例标志会传递到着色器中用来做唯一区分。常用接口如下:
1.void glDrawArraysInstanced(GLenum mode, GLint first, GLsizei count, GLsizei instanceCount):每个实例调用一次glDrawArrays。
.instanceCount表示实例个数。

2.void glDrawElementsInstanced(GLenum mode, GLsizei count, GLenum type, const void* indices, GLsizei instanceCount):每个实例调用一次glDrawElements。
.instanceCount表示实例个数。

3.void glDrawElementsInstancedBaseVertex(GLenum mode, GLsizei count, GLenum type, const void* indices, GLsizei instanceCount, GLuint baseVertex):每个实例调用一次glDrawElementsBaseVertex。
.instanceCount表示实例个数。

图元重启动:减少使用索引缓存对象进行绘制图元的调用次数。使用该功能时必须调用glEnable(GL_PRIMITIVE_RESTART)来启动。接口定义如下:
void glPrimitiveRestartIndex(GLuint index):设置从指定索引位置处开始停止绘制,而后面的索引按照之前的图元进行绘制。
.index指定图元重启的索引位置。为了保证和其他绘制索引不相同,通常是取2^n - 1,其中n表示索引类型的位数。

  • 9
    点赞
  • 37
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值