私はopenglで最も効率的に動くオブジェクト(一般的に)とラインストリップ(特に)のこつをつかもうとしているので、複数の線分が右から左に一定の速度で移動するアプリケーションを書いています。すべての時点で、最も左のポイントが削除され、ライン全体が左にシフトされ、新しいポイントがラインの右端に追加されます (この新しいデータ ポイントは、オンザフライでストリーミング/受信/計算されます)。 、約 10ms ごと)。私が何を意味するかを説明するには、次の画像を参照してください。
多くのオブジェクトを操作したいので、gl*
呼び出しの量を最小限に抑えるために、頂点バッファー オブジェクトを使用することにしました。私の現在のコードは次のようになります。
A) 初期頂点の設定:
# calculate my_func(x) in range [0, n]
# (could also be random data)
data = my_func(0, n)
# create & bind buffer
vbo_id = GLuint()
glGenBuffers(1, vbo_id);
glBindBuffer(GL_ARRAY_BUFFER, vbo_id)
# allocate memory & transfer data to GPU
glBufferData(GL_ARRAY_BUFFER, sizeof(data), data, GL_DYNAMIC_DRAW)
B) 頂点を更新する:
draw():
# get new data and update offset
data = my_func(n+dx, n+2*dx)
# update offset 'n' which is the current absolute value of x.
n = n + 2*dx
# upload data
glBindBuffer(GL_ARRAY_BUFFER, vbo_id)
glBufferSubData(GL_ARRAY_BUFFER, n, sizeof(data), data)
# translate scene so it looks like line strip has moved to the left.
glTranslatef(-local_shift, 0.0, 0.0)
# draw all points from offset
glVertexPointer(2, GL_FLOAT, 0, n)
glDrawArrays(GL_LINE_STRIP, 0, points_per_vbo)
どこでmy_func
次のようにしますか:
my_func(start_x, end_x):
# generate the correct x locations.
x_values = range(start_x, end_x, STEP_SIZE)
# generate the y values. We could be getting these values from a sensor.
y_values = []
for j in x_values:
y_values.append(random())
data = []
for i, j in zip(x_values, y_values):
data.extend([i, j])
return data
これは問題なく動作しますが、たとえば 20 本のライン ストリップが画面全体に広がると、処理速度が大幅に低下します。 したがって、私の質問:
1) glMapBuffer を使用して GPU 上のバッファをバインドし、(glBufferSubData を使用する代わりに) データを直接埋める必要がありますか? それとも、これはパフォーマンスに関して何の違いもありませんか?
2) glTranslatef を呼び出す代わりに、オブジェクト (ここではライン ストリップ) を移動するためにシェーダーを使用する必要がありますか? もしそうなら、そのようなシェーダーはどのように見えますか? (私のライン ストリップは周期関数ではなく、ランダム データが含まれているため、シェーダーは間違った方法であると思われます)。
3) ウィンドウのサイズが変更されるとどうなりますか? 縦横比を維持し、それに応じて頂点をスケーリングするにはどうすればよいですか? glViewport() は、x 方向ではなく、y 方向のスケーリングのみに役立ちます。ウィンドウが x 方向に再スケーリングされた場合、現在の実装では、ライン ストリップ全体の位置を再計算し (my_func
新しい x 座標を取得するために呼び出します)、それを GPU にアップロードする必要があります。これはもっとエレガントにできると思いますか?どうすればいいですか?
glTranslatef
4)非整数値で使用すると、ライン ストリップが数千のポイントで構成されている場合、画面がちらつき始めることに気付きました。これはおそらく、ライン ストリップを計算するために使用する細かい解像度が画面のピクセル解像度と一致しないためです。そのため、一部のポイントが前に表示されたり、他のポイントの後ろに表示されることがあります (これは、レンダリングを行わない場合に特に厄介です)。正弦波ですが、いくつかの「ランダム」データ)。これが起こらないようにするにはどうすればよいですか (1 ピクセルの整数倍で変換するという明白な解決策以外に)? ウィンドウのサイズが元の 800x800 ピクセルから 100x100 ピクセルに変更された場合でも、20 秒のライン ストリップを視覚化したい場合、x 方向にシフトすると、ちらつきがなくサブピクセル精度で何らかの方法で動作する必要があります。
5) ご覧のとおり、私は常に電話をかけますglTranslatef(-local_shift, 0.0, 0.0)
。反対のことは決してしません。したがって、ビュー全体を右にシフトし続けます。そのため、絶対 x 位置を追跡する必要があります (新しいデータを正しい位置に配置するため)。この問題は最終的に、ラインがウィンドウの端と重なるアーティファクトにつながります。これを行うためのより良い方法があるに違いないと思いますよね?x 値を固定したまま、y 値を移動して更新するだけですか?
編集正弦波の例を削除し、より良い例に置き換えました。私の質問は、一般的に、スペース内でライン ストリップを最も効率的に移動する方法 (新しい値を追加しながら) に関するものです。したがって、「t -> 無限大の値を事前計算する」などの提案はここでは役に立ちません (家の前で測定された現在の温度を描画することもできます)。
EDIT2 各時間ステップの後、最初のポイントが削除され、新しいポイントが最後に追加されるこのおもちゃの例を検討してください。
t = 0
*
* * *
* **** *
1234567890
t = 1
*
* * * *
**** *
2345678901
t = 2
* *
* * *
**** *
3456789012
ここではシェーダーを使用できないと思いますよね?
編集 3: 2 つのライン ストリップの例。
編集 4:ティムの回答に基づいて、次のコードを使用しています。これはうまく機能しますが、行が 2 つに分かれています ( の呼び出しが 2 つあるためglDrawArrays
)。次の 2 つのスクリーンショットも参照してください。
# calculate the difference
diff_first = x[1] - x[0]
''' first part of the line '''
# push the matrix
glPushMatrix()
move_to = -(diff_first * c)
print 'going to %d ' % (move_to)
glTranslatef(move_to, 0, 0)
# format of glVertexPointer: nbr points per vertex, data type, stride, byte offset
# calculate the offset into the Vertex
offset_bytes = c * BYTES_PER_POINT
stride = 0
glVertexPointer(2, GL_FLOAT, stride, offset_bytes)
# format of glDrawArrays: mode, Specifies the starting index in the enabled arrays, nbr of points
nbr_points_to_render = (nbr_points - c)
starting_point_in_above_selected_Vertex = 0
glDrawArrays(GL_POINTS, starting_point_in_above_selected_Vertex, nbr_points_to_render)
# pop the matrix
glPopMatrix()
''' second part of the line '''
# push the matrix
glPushMatrix()
move_to = (nbr_points - c) * diff_first
print 'moving to %d ' %(move_to)
glTranslatef(move_to, 0, 0)
# select the vertex
offset_bytes = 0
stride = 0
glVertexPointer(2, GL_FLOAT, stride, offset_bytes)
# draw the line
nbr_points_to_render = c
starting_point_in_above_selected_Vertex = 0
glDrawArrays(GL_POINTS, starting_point_in_above_selected_Vertex, nbr_points_to_render)
# pop the matrix
glPopMatrix()
# update counter
c += 1
if c == nbr_points:
c = 0
EDIT5結果のソリューションは、明らかに画面全体に1行をレンダリングする必要があり、接続が失われている2行はありません。Tim による循環バッファー ソリューションは、プロットを移動する方法に関するソリューションを提供しますが、1 行ではなく 2 行になってしまいます。