OpenGL basics: calling glDrawElements once per object

Posted by Bethor on Stack Overflow See other posts from Stack Overflow or by Bethor
Published on 2011-01-12T18:51:32Z Indexed on 2011/01/12 18:53 UTC
Read the original article Hit count: 486

Filed under:
|
|

Hi all,

continuing on from my explorations of the basics of OpenGL (see this question), I'm trying to figure out the basic principles of drawing a scene with OpenGL.

I am trying to render a simple cube repeated n times in every direction.

My method appears to yield terrible performance : 1000 cubes brings performance below 50fps (on a QuadroFX 1800, roughly a GeForce 9600GT).

My method for drawing these cubes is as follows:

done once:

  • set up a vertex buffer and array buffer containing my cube vertices in model space
  • set up an array buffer indexing the cube for drawing as 12 triangles

done for each frame:

  • update uniform values used by the vertex shader to move all cubes at once

done for each cube, for each frame:

  • update uniform values used by the vertex shader to move each cube to its position
  • call glDrawElements to draw the positioned cube

Is this a sane method ? If not, how does one go about something like this ? I'm guessing I need to minimize calls to glUniform, glDrawElements, or both, but I'm not sure how to do that.


Full code for my little test : (depends on gletools and pyglet)

I'm aware that my init code (at least) is really ugly; I'm concerned with the rendering code for each frame right now, I'll move to something a little less insane for the creation of the vertex buffers and such later on.

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()

© Stack Overflow or respective owner

Related posts about python

Related posts about opengl