OpenGL Grundlagen: Aufruf glDrawElements einmal pro Objekt
Frage
weiter auf meinen Erkundungen der Grundlagen der OpenGL (siehe diese Frage ), ich, um herauszufinden, die grundlegenden Prinzipien versucht, mit OpenGL eine Szene zu ziehen.
Ich versuche, einen einfachen Würfel zu machen in jede Richtung n-mal wiederholt.
Meine Methode scheint schreckliche Leistung zu erhalten. 1000 Würfel bringt Leistung unter 50fps (auf einem QuadroFX 1800 etwa eine GeForce 9600GT)
Meine Methode, um diese Würfel für das Zeichnen ist wie folgt:
getan einmal:
- einen Eckenpuffer und Array-Puffer, der meinen Würfelscheitel im Modellbereich einzurichten
- ein Array Puffer Indizieren des Würfels zum Zeichnen als Dreiecke 12 einrichten
für jeden Rahmen durchgeführt:
- update einheitliche Werte, die vom Vertex-Shader verwendet, um alle Würfel zu bewegen auf einmal
für jeden Würfel gemacht, für jeden Rahmen:
- update einheitliche Werte durch den Vertex-Shader verwendet, um jeden Würfel auf seine Position zu bewegen
- Anruf glDrawElements das positionierte Würfel zeichnen
Ist das eine vernünftige Methode? Wenn nicht, wie geht man über so etwas wie das? Ich vermute, ich brauche Anrufe glUniform, glDrawElements zu minimieren, oder beides, aber ich bin nicht sicher, wie das zu tun.
Voll Code für meinen kleinen Test: (abhängig von gletools und Pyglet)
Ich bin mir bewusst, dass mein init-Code (mindestens) ist wirklich hässlich; Ich bin in diesem Augenblick für jeden Rahmen mit dem Rendering-Code betreffen, ich etwas für die Erzeugung der Vertexpuffer etwas weniger verrückt bewegen würde und so später.
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()
Lösung
Im Grunde genommen, wie Sie durch Daten sind Looping einmal pro Frame, sind Sie wahrscheinlich beginnen Python Leistungsgrenzen zu treffen. Es ist wahrscheinlich, dass, wenn Sie Ihren Code in neu geschrieben, sagen wir, C würden Sie viel bessere Leistung haben.
Wie auch immer, zur Leistungssteigerung in OpenGL, unabhängig von der Sprache, haben Sie die Anzahl der Auslosung Anrufe (Anrufe glDrawElements
) zu begrenzen, die CPU-Auslastung zu begrenzen und die Kommunikation zwischen CPU und GPU zu verbessern.
Abhängig von Ihrem Ziel, haben Sie mehrere Möglichkeiten, um Ihre Prüfung zu beschleunigen:
-
, wenn alle Würfel sind statisch bleiben, können Sie deren Geometrien in einem einzigen VBO kombinieren könnte, durch die Eckpunkte pretransforming, und geben Sie einen einzelnen Zeichenaufruf für alle Ihre Würfel schneiden.
-
, wenn alle Würfel unabhängig voneinander animiert werden, Sie Hardware verwenden könnte Instancing (wenn Ihre Hardware es erlaubt) oder Pseudo-Hardware-Instancing, können Sie einige Hinweise hier . Mit Hilfe dieser Techniken können Sie im Grunde mehr Würfel mit einem einzigen Zeichenaufruf ziehen, dann liegt es an den Shader die Würfelposition nach seiner primitiven id zu holen.