编程语言
首页 > 编程语言> > python-OpenGL基础知识:每个对象调用一次glDrawElements

python-OpenGL基础知识:每个对象调用一次glDrawElements

作者:互联网

继续我对OpenGL基础知识的探索(请参见this question),我试图弄清楚使用OpenGL绘制场景的基本原理.

我试图渲染一个简单的立方体,在每个方向上重复n次.

我的方法似乎产生了可怕的性能:1000个多维数据集使性能低于50fps(在QuadroFX 1800上,大约是GeForce 9600GT).

我绘制这些多维数据集的方法如下:

完成一次:

>在模型空间中设置包含我的多维数据集顶点的顶点缓冲区和数组缓冲区
>设置一个数组缓冲区,索引要绘制的立方体为12个三角形

为每一帧完成:

>更新顶点着色器使用的统一值以一次移动所有多维数据集

为每个立方体,每个框架完成:

>更新顶点着色器用来将每个立方体移到其位置的统一值
>调用glDrawElements绘制定位的立方体

这是理智的方法吗?如果不是,那该怎么办呢?我猜想我需要最小化对glUniform,glDrawElements或两者的调用,但是我不确定该怎么做.

我的小测试的完整代码:(取决于gletools和pyglet)

我知道我的初始化代码(至少)确实很丑陋.我现在关心的是每个帧的渲染代码,在稍后创建顶点缓冲区时,我会稍微简化一些工作.

import pyglet
from pyglet.gl import *
from pyglet.window import key
from numpy import deg2rad, tan
from gletools import ShaderProgram, FragmentShader, VertexShader, GeometryShader

vertexData = [-0.5, -0.5, -0.5, 1.0,
              -0.5, 0.5, -0.5, 1.0,
              0.5, -0.5, -0.5, 1.0,
              0.5, 0.5, -0.5, 1.0,
              -0.5, -0.5, 0.5, 1.0,
              -0.5, 0.5, 0.5, 1.0,
              0.5, -0.5, 0.5, 1.0,
              0.5, 0.5, 0.5, 1.0]

elementArray = [2, 1, 0, 1, 2, 3,## back face
                4, 7, 6, 4, 5, 7,## front face
                1, 3, 5, 3, 7, 5,## top face
                2, 0, 4, 2, 4, 6,## bottom face
                1, 5, 4, 0, 1, 4,## left face
                6, 7, 3, 6, 3, 2]## right face

def toGLArray(input):
    return (GLfloat*len(input))(*input)

def toGLushortArray(input):
    return (GLushort*len(input))(*input)

def initPerspectiveMatrix(aspectRatio = 1.0, fov = 45):
    frustumScale = 1.0 / tan(deg2rad(fov) / 2.0)
    fzNear = 0.5
    fzFar = 300.0
    perspectiveMatrix = [frustumScale*aspectRatio, 0.0         , 0.0                            , 0.0 ,
                         0.0                     , frustumScale, 0.0                            , 0.0 ,
                         0.0                     , 0.0         , (fzFar+fzNear)/(fzNear-fzFar)  , -1.0,
                         0.0                     , 0.0         , (2*fzFar*fzNear)/(fzNear-fzFar), 0.0 ]
    return perspectiveMatrix

class ModelObject(object):
    vbo = GLuint()
    vao = GLuint()
    eao = GLuint()
    initDone = False

    verticesPool = []
    indexPool = []

    def __init__(self, vertices, indexing):
        super(ModelObject, self).__init__()
        if not ModelObject.initDone:
            glGenVertexArrays(1, ModelObject.vao)
            glGenBuffers(1, ModelObject.vbo)
            glGenBuffers(1, ModelObject.eao)
            glBindVertexArray(ModelObject.vao)
            initDone = True
        self.numIndices = len(indexing)
        self.offsetIntoVerticesPool = len(ModelObject.verticesPool)
        ModelObject.verticesPool.extend(vertices)
        self.offsetIntoElementArray = len(ModelObject.indexPool)
        ModelObject.indexPool.extend(indexing)

        glBindBuffer(GL_ARRAY_BUFFER, ModelObject.vbo)
        glEnableVertexAttribArray(0) #position
        glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, 0)
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ModelObject.eao)
        glBufferData(GL_ARRAY_BUFFER, len(ModelObject.verticesPool)*4, toGLArray(ModelObject.verticesPool), GL_STREAM_DRAW)
        glBufferData(GL_ELEMENT_ARRAY_BUFFER, len(ModelObject.indexPool)*2, toGLushortArray(ModelObject.indexPool), GL_STREAM_DRAW)

    def draw(self):
        glDrawElements(GL_TRIANGLES, self.numIndices, GL_UNSIGNED_SHORT, self.offsetIntoElementArray)


class PositionedObject(object):
    def __init__(self, mesh, pos, objOffsetUf):
        super(PositionedObject, self).__init__()
        self.mesh = mesh
        self.pos = pos
        self.objOffsetUf = objOffsetUf

    def draw(self):
        glUniform3f(self.objOffsetUf, self.pos[0], self.pos[1], self.pos[2])
        self.mesh.draw()



w = 800
h = 600
AR = float(h)/float(w)
window = pyglet.window.Window(width=w, height=h, vsync=False)
window.set_exclusive_mouse(True)
pyglet.clock.set_fps_limit(None)

## input
forward = [False]
left = [False]
back = [False]
right = [False]
up = [False]
down = [False]
inputs = {key.Z: forward, key.Q: left, key.S: back, key.D: right,
          key.UP: forward, key.LEFT: left, key.DOWN: back, key.RIGHT: right,
          key.PAGEUP: up, key.PAGEDOWN: down}

## camera
camX = 0.0
camY = 0.0
camZ = -1.0

def simulate(delta):
    global camZ, camX, camY
    scale = 10.0
    move = scale*delta
    if forward[0]:
        camZ += move
    if back[0]:
        camZ += -move
    if left[0]:
        camX += move
    if right[0]:
        camX += -move
    if up[0]:
        camY += move
    if down[0]:
        camY += -move
pyglet.clock.schedule(simulate)

@window.event
def on_key_press(symbol, modifiers):
    global forward, back, left, right, up, down
    if symbol in inputs.keys():
        inputs[symbol][0] = True

@window.event
def on_key_release(symbol, modifiers):
    global forward, back, left, right, up, down
    if symbol in inputs.keys():
        inputs[symbol][0] = False


## uniforms for shaders
camOffsetUf = GLuint()
objOffsetUf = GLuint()
perspectiveMatrixUf = GLuint()
camRotationUf = GLuint()

program = ShaderProgram(
    VertexShader('''
    #version 330
    layout(location = 0) in vec4 objCoord;
    uniform vec3 objOffset;
    uniform vec3 cameraOffset;
    uniform mat4 perspMx;
    void main()
    {
        mat4 translateCamera = mat4(1.0f, 0.0f, 0.0f, 0.0f,
                                    0.0f, 1.0f, 0.0f, 0.0f,
                                    0.0f, 0.0f, 1.0f, 0.0f,
                                    cameraOffset.x, cameraOffset.y, cameraOffset.z, 1.0f);
        mat4 translateObject = mat4(1.0f, 0.0f, 0.0f, 0.0f,
                                    0.0f, 1.0f, 0.0f, 0.0f,
                                    0.0f, 0.0f, 1.0f, 0.0f,
                                    objOffset.x, objOffset.y, objOffset.z, 1.0f);

        vec4 modelCoord = objCoord;
        vec4 positionedModel = translateObject*modelCoord;
        vec4 cameraPos = translateCamera*positionedModel;

        gl_Position = perspMx * cameraPos;
    }'''),
    FragmentShader('''
    #version 330
    out vec4 outputColor;
    const vec4 fillColor = vec4(1.0f, 1.0f, 1.0f, 1.0f);

    void main()
    {
        outputColor = fillColor;
    }''')
)


shapes = []
def init():
    global camOffsetUf, objOffsetUf
    with program:
        camOffsetUf = glGetUniformLocation(program.id, "cameraOffset")
        objOffsetUf = glGetUniformLocation(program.id, "objOffset")
        perspectiveMatrixUf = glGetUniformLocation(program.id, "perspMx")
        glUniformMatrix4fv(perspectiveMatrixUf, 1, GL_FALSE, toGLArray(initPerspectiveMatrix(AR)))

        obj = ModelObject(vertexData, elementArray)
        nb = 20
        for i in range(nb):
            for j in range(nb):
                for k in range(nb):
                    shapes.append(PositionedObject(obj, (float(i*2), float(j*2), float(k*2)), objOffsetUf))

        glEnable(GL_CULL_FACE)
        glCullFace(GL_BACK)
        glFrontFace(GL_CW)
        glEnable(GL_DEPTH_TEST)
        glDepthMask(GL_TRUE)
        glDepthFunc(GL_LEQUAL)
        glDepthRange(0.0, 1.0)
        glClearDepth(1.0)

def update(dt):
    print pyglet.clock.get_fps()
pyglet.clock.schedule_interval(update, 1.0)

@window.event
def on_draw():
    with program:
        pyglet.clock.tick()
        glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT)
        glUniform3f(camOffsetUf, camX, camY, camZ)

        for shape in shapes:
            shape.draw()


init()
pyglet.app.run()

解决方法:

基本上,当您每帧循环浏览一次数据时,您可能开始达到Python的性能极限.如果将代码重写为C,则​​性能可能会好得多.

无论如何,要提高OpenGL的性能(与语言无关),必须限制绘图调用(对glDrawElements的调用)的数量,以限制CPU使用率并改善CPU与GPU之间的通信.

根据您的目标,您有多种选择来加快测试速度:

>如果要使所有多维数据集保持静态,则可以通过预变换顶点将其几何形状合并到单个VBO中,然后对所有多维数据集发出一次绘制调用.
>如果要对所有多维数据集进行独立动画处理,则可以使用硬件实例化(如果硬件允许),或使用伪硬件实例化,则可以找到一些指针here.使用这些技术,基本上可以用一个绘制一个多维数据集绘制调用,然后由着色器根据其原始ID获取立方体位置.

标签:pyglet,opengl,python
来源: https://codeday.me/bug/20191208/2094353.html