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

时间:2011-01-12 18:51:32

标签: python opengl pyglet

继续探索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()

1 个答案:

答案 0 :(得分:3)

基本上,当你每帧循环一次数据时,你可能已经开始达到Python的性能极限。如果你用C语言重新编写代码,很可能你会有更好的表现。

无论如何,为了提高OpenGL的性能,独立于语言,你必须限制绘制调用的次数(调用glDrawElements),以限制CPU的使用并改善CPU和GPU之间的通信。 / p>

根据您的目标,您有多种方法可以加快测试速度:

  • 如果你的所有立方体都要保持静态,你可以将它们的几何结构组合成一个VBO,通过预变形顶点,并为你的所有立方体发出一个绘图调用。

  • 如果您要独立制作所有立方体,您可以使用硬件实例化(如果您的硬件允许),或者伪硬件实例化,您可以找到一些指针here。使用这些技术,您基本上可以使用单个绘制调用绘制多个多维数据集,然后由着色器根据其原始ID获取多维数据集位置。

相关问题