一、说明

我们常用的着色器绘制函数是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。这些对象之间的层级关系如下图所示:

【OpenGL实践02】glDrawElements的使用案例-LMLPHP

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, \
                                                  glBindVertexArray

import pygame
import numpy as np

def 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()-1
    return vertex

def 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,EBO
    VBO = glGenBuffers(1)  # 创建缓存
    glBindBuffer(GL_ARRAY_BUFFER, VBO)
    glBufferData(GL_ARRAY_BUFFER, vertices.nbytes, vertices, GL_STATIC_DRAW)  # 输入数据


    # Create the index buffer object
    indices = 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 shaders
    VERTEX_SHADER = shaders.compileShader("""
      #version 330
    layout(location = 0) in vec4 position;    
    void main()
    {
        gl_Position = position;
    }
    """, GL_VERTEX_SHADER)

    FRAGMENT_SHADER = shaders.compileShader("""
    #version 330
    out 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 loop


    glClear(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 screen
    pygame.display.flip()

    try:
        while True:
            event = pygame.event.wait()
            if event.type == pygame.QUIT:
                break
            if event.type == pygame.KEYDOWN:
                if event.key == pygame.K_ESCAPE or event.unicode == "q":
                    break
            # pygame.display.flip()
    finally:
        pygame.quit()

main()

结果
【OpenGL实践02】glDrawElements的使用案例-LMLPHP

四、总结

EBO使用方法

1 创建并绑定索引

   # Create the index buffer object
    indices = 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); 
# 调用绘制点的索引

五、后记

还有一些其它的函数,我们将在后文中逐渐实现。

03-08 10:35