目录
- 一、说明
- 二、顶点顺序渲染和选择渲染
- 2.1 基本方法函数
- 2.2.顶点数据管理
- 2.3 层级关系
- 三、测试EBO的代码
- 四、总结
- 五、后记
一、说明
我们常用的着色器绘制函数是glDrawArray和glDrawElements,glDrawArray我们已经使用的很熟练,不需要重提,那么glDrawElements就需要重点提示它的用法,本篇就是对这个函数用法详细记载。
二、顶点顺序渲染和选择渲染
在一般的顶点画出,有glDrawArray()函数,此函数是将VOB的顶点顺序发出就算OK,但是,用glDrawElements不是按照VOB顺序完成,而是将顶点按照index数组进行画出。这种渲染大大增加渲染的灵活性和多样性。
本篇我们将尝试使用这种渲染方法。
2.1 基本方法函数
OpenGL提供的画图函数可以分为两大类:non-indexed draw和indexed draw。下面列举出了几个最常用的画图函数。
//1.基本方法:non-indexed draw
void glDrawArrays( GLenum mode, GLint first, GLsizei count);
//mutidraw + non-indexed draw
void glMultiDrawArrays( GLenum mode, GLint *first, GLsizei *count, GLsizei primcount);
//instance + non-indexed draw
void glDrawArraysInstanced( GLenum mode, GLint first,GLsizei count, GLsizei instancecount);
//indirect + non-indexed draw
void glDrawArraysIndirect(GLenum mode, const void *indirect);
//2.基本方法:indexed draw
void glDrawElements(GLenum mode, GLsizei count, GLenum type, void * indices);
//mutidraw + indexed draw
void glMultiDrawElements( GLenum mode, GLsizei *count, GLenum type, void **indices, GLsizei primcount);
//instance + indexed draw
void glDrawElementsInstanced( GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount);
//indirect + indexed draw
void glDrawElementsIndirect(GLenum mode, GLenum type, const void *indirect);
glDrawArrays()和glDrawElements()是最基础的2个画图函数,每调用1次画图函数,CPU和GPU会进行一次数据通信,我们把这个过程叫做drawcall,drawcall是一个非常耗时的操作,为了减少drawcall,提高绘制效率,在上述2个函数的基础上,引入了intancing draw,multiDraw和indirect draw。但是,对于现如今PC端GPU,减少drawcall对于性能提升基本不管用了。
在调用上面的画图函数前,我们需要做一些准备工作,包括3点:
- 提供顶点数据来源;
- 提供顶点数据的解析方式;
- 告诉函数怎样把顶点组装成图元。
2.2.顶点数据管理
画图函数需要的顶点数据来自当前绑定的VAO,VAO提供了画图需要的所有顶点数据。
OpenGL中涉及到管理顶点数据的对象有VAO,VBO,EBO,IBO。这些对象之间的层级关系如下图所示:
2.3 层级关系
VAO处于最高层级,通过【绑定】操作,可以建立起VAO与VBO,EBO,IBO之间的关系;
VBO,EBO,IBO处于第二层级,用于管理顶点数据,VBO负责管理顶点属性数据,EBO负责管理顶点索引数据,IBO负责管理画图指令数据。
第三层级,VBO,EBO,IBO这些对象内部不存放数据,我们需要额外分配内存,把数据存放到这段内存中,并和上述3种对象关联起来。
其中VAO和VBO是必须的;EBO和IBO是可选的,根据我们的需求决定是否创建,调用indexed draw则必须创建EBO,调用indirect draw则必须创建IBO。
三、测试EBO的代码
from OpenGL.GL import shaders
from OpenGL.arrays import vbo
from OpenGL.GL import *
from OpenGL.raw.GL.ARB.vertex_array_object import glGenVertexArrays, \glBindVertexArrayimport pygame
import numpy as npdef get_index_vetices(N ):lst = list(range(100))indexs = np.array([lst], dtype=np.int32)return indexs
def get_primery_vetices(N,dim):vertex = np.zeros([N, dim], dtype=np.float32)for i in vertex:for Z in range(dim-1):i[Z] = 2*np.random.rand()-1return vertexdef main():pygame.init()screen = pygame.display.set_mode((800,600), pygame.OPENGL|pygame.DOUBLEBUF)# Create the VBO# vertices = np.array([[0, 1, 0], [-1, -1, 0], [1, -1, 0]], dtype='f')vertices = get_primery_vetices(100,3)global VBO,EBOVBO = glGenBuffers(1) # 创建缓存glBindBuffer(GL_ARRAY_BUFFER, VBO)glBufferData(GL_ARRAY_BUFFER, vertices.nbytes, vertices, GL_STATIC_DRAW) # 输入数据# Create the index buffer objectindices = np.array([[0, 1, 2],[3,2,5],[13,9,15],[33,14,16],[99,3,2]], dtype=np.int32)EBO = glGenBuffers(1 )glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO)glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.nbytes, indices, GL_STATIC_DRAW) # Now create the shadersVERTEX_SHADER = shaders.compileShader("""#version 330layout(location = 0) in vec4 position; void main(){gl_Position = position;}""", GL_VERTEX_SHADER)FRAGMENT_SHADER = shaders.compileShader("""#version 330out vec4 outputColor;void main(){outputColor = vec4(0.0f, 1.0f, 0.0f, 1.0f);}""", GL_FRAGMENT_SHADER)shader = shaders.compileProgram(VERTEX_SHADER, FRAGMENT_SHADER)#The draw loopglClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT)glUseProgram(shader)glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, VBO)glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO)glDisable(GL_PROGRAM_POINT_SIZE)glPointSize(33.0)glEnableVertexAttribArray(0);glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, None) # 这里的None不能写为0# glDrawArrays(GL_TRIANGLES, 0, 6) #This line still works# glDrawElements(GL_TRIANGLES, 11, GL_UNSIGNED_INT, indices) # This line does work too!glDrawElements(GL_TRIANGLES, 12, GL_UNSIGNED_INT, None)# Show the screenpygame.display.flip()try:while True:event = pygame.event.wait()if event.type == pygame.QUIT:breakif event.type == pygame.KEYDOWN:if event.key == pygame.K_ESCAPE or event.unicode == "q":break# pygame.display.flip()finally:pygame.quit()main()
结果
四、总结
EBO使用方法:
1 创建并绑定索引
# Create the index buffer objectindices = np.array([[0, 1, 2],[3,2,5],[13,9,15],[33,14,16],[99,3,2]], dtype=np.int32)EBO = glGenBuffers(1 )glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO)glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.nbytes, indices, GL_STATIC_DRAW)
2 在glutDisplayFunc回调函数中
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,EBO);
glDrawElements(GL_TRIANGLES, 12, GL_UNSIGNED_INT, 0);
# 调用绘制点的索引
五、后记
还有一些其它的函数,我们将在后文中逐渐实现。