2011-01-12 9 views
3

OpenGLの基礎を掘り下げながら(this questionを参照)、OpenGLでシーンを描画する基本的な原則を理解しようとしています。OpenGLの基本:オブジェクトごとに1回glDrawElementsを呼び出す

私は単純なキューブをあらゆる方向にn回繰り返してレンダリングしようとしています。

私の方法では、1000キューブのパフォーマンスが50fps(QuadroFX 1800、おおよそのGeForce 9600GT)以下になります。次のように

これらのキューブを描画するための私の方法は次のとおりです。

が一度に行わ:モデル空間で私の立方体の頂点を含む頂点バッファと配列バッファを設定

  • は、配列バッファを設定しました

    :フレーム毎に行わ12個の三角形

として描画するキューブをインデックスそれぞれを移動させる頂点シェーダによって使用

  • 更新均一値:

がフレーム毎に、各キューブに対して行わ一度にすべてのキューブを移動するために、頂点シェーダによって使用の

  • 更新均一値配置キューブ

を描くその位置

  • コールglDrawElementsへのキューブが、これは正気の方法ですか?もしそうでなければ、このようなことについてどうやって行くのですか? glUniform、glDrawElements、またはその両方への呼び出しを最小限に抑える必要があると思いますが、その方法はわかりません。


    私の小さなテスト用

    完全なコードは:(gletoolsとpygletによって異なります)

    私は(少なくとも)本当に醜い私のinitコードであることを承知しています。私は今、各フレームのレンダリングコードに心配しています。私は、頂点バッファの作成などのために少し難解なものに移行します。

    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() 
    
  • +0

    あなたのコードを高速化できましたか?もしそうなら、どうですか? – memyself

    +0

    スピードの問題はおそらく、あなたの配列にnumpyの代わりにlist()を使うことです。 – ninMonkey

    答えて

    3

    基本的に、フレームごとにデータをループしているので、おそらくPythonのパフォーマンス制限を打ち始めるでしょう。 C言語でコードを書き直すと、パフォーマンスが大幅に向上する可能性があります。

    とにかく、OpenGLでのパフォーマンスを向上させるには、CPU使用量を制限し、CPUとGPU間の通信を改善するために、描画呼び出し(呼び出しはglDrawElements)の数を制限する必要があります。

    あなたのターゲットに依存し、あなたのテストをスピードアップするために、複数のオプションがあります。

    • すべてのキューブが静止したままになっている場合は、頂点をpretransformingことで、単一VBOに彼らのジオメトリを組み合わせることができ、すべてのキューブに対して1回の描画呼び出しを発行します。

    • すべてのキューブを独立してアニメーション化する場合は、ハードウェアのインスタンス化(ハードウェアが許可している場合)または疑似ハードウェアのインスタンス化を使用すると、ポインタhereを見つけることができます。これらのテクニックを使用すると、基本的に1回の描画呼び出しで複数の立方体を描くことができます。シェーダは原型のIDに従って立方体の位置を取得します。

    +0

    ポインタありがとう!私はPythonを(実際にはC++にもっと精通していますが)固執します。これはこの種のプロトタイプ作成には非常に適しているからです。 OpenGL 3.30をターゲットにしているので、ハードウェアのインスタンス化に関するあなたの提案を見ていきます。HWの要件は問題ではありません。 –

    +0

    私はあなたの要点を見て、プロトタイプ作成にはPythonを使用し、パフォーマンス関連の操作のためにカスタムC++モジュールと組み合わせて使用​​します。 – rotoglup

    関連する問題