OpenGLの基本:オブジェクトごとに1回Gldrawelementsを呼び出します
質問
OpenGLの基本の調査から続けてください(参照 この質問)、私はOpenGLでシーンを描くことの基本原則を把握しようとしています。
シンプルなキューブをあらゆる方向にn回繰り返したままにしようとしています。
私の方法はひどいパフォーマンスをもたらすように見えます:1000キューブは50fps未満のパフォーマンスをもたらします(Quadrofx 1800、ほぼgeforce 9600gt)。
これらのキューブを描くための私の方法は次のとおりです。
一度完了:
- モデル空間に私のキューブの頂点を含む頂点バッファとアレイバッファーをセットアップします
- 12の三角形として描画するためにキューブをインデックスする配列バッファーを設定する
各フレームに対して完了:
- 頂点シェーダーが使用する均一な値を更新して、すべてのキューブを一度に移動する
各キューブ、各フレームに対して行われます:
- 頂点シェーダーが使用する均一な値を更新して、各キューブをその位置に移動します
- Gldrawelementsに電話して、配置されたキューブを描画します
これは正気な方法ですか?そうでない場合、どうやってこのようなことをしますか? Gluniform、Gldrawelements、またはその両方への呼び出しを最小限に抑える必要があると思いますが、それを行う方法はわかりません。
私の小さなテストのための完全なコード:( グリートール と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のパフォーマンス制限に達し始めているでしょう。たとえば、コードを書き直すと、パフォーマンスがはるかに優れている可能性があります。
とにかく、言語とは独立してOpenGLでのパフォーマンスを向上させるには、抽選の数を制限する必要があります( glDrawElements
)、CPUの使用を制限し、CPUとGPU間の通信を改善します。
ターゲットによっては、テストをスピードアップするための複数のオプションがあります。
すべてのキューブが静的なままである場合、頂点を移植することにより、それらのジオメトリを単一のVBOに結合し、すべてのキューブに単一のドローコールを発行できます。
すべてのキューブを個別にアニメーション化する場合は、ハードウェアインスタンス(ハードウェアで許可されている場合)、または擬似ハードウェアインスタンスを使用することができます。 ここ. 。これらの手法を使用すると、基本的には1回のドローコールで複数のキューブを描画できます。その後、シェーダーまで上昇して、その原始IDに従ってキューブの位置を取得します。