web-dev-qa-db-ja.com

個々の変数(グラフィック)に対する行列乗算の利点は何ですか?

私はOpenGLとチュートリアル( 12 )を学習しています。オブジェクトをスケーリング/回転/変換するには、行列の乗算を知っている必要があることを教えています。どうして? 3x3マトリックスの代わりに、scale_xscale_ysin_acos_amov_xおよびmov_yの6つの浮動小数点数を使用できます。彼らは結果的に...

  • GPUとCPUの使用量が少ない(行列の乗算には9つの乗算と6つの加算が必要ですが、個々の変数には6つの乗算と4つの加算が必要です。また、オブジェクトを移動する場合は、行列全体を乗算する代わりに、2つの浮動小数点の値のみを変更する必要があります。
  • 少ないメモリ帯域幅(1/3カット!)
  • 操作が簡単(少なくともCでは、行列操作関数は不要)
  • オブジェクトの回転、オフセット、スケールを取得するためのより簡単な情報抽出

私が知っている唯一の欠点は:

  • 座標平面の原点ではなく、変換の順序を変更することはできません。最も重要なのは、ある点を中心とした回転です。ただし、それに応じてmov_xmov_yを変更することで、シミュレーションできます。
  • より多くのコード。ただし、それらを配列に入れることもできますが、1つずつ更新する機能が失われ、すべてまたは配列の最初だけを更新でき、中央または最後は更新できません。しかし、これは間違っているかもしれません。

誰か私に何が欠けているのか教えてもらえますか?

編集:以下に私のシェーダーを追加しました。

#version 330

layout (location = 0) in vec2 pos;
layout (location = 1) in vec3 clr_in;

uniform float osclx, oscly, osina, ocosa, omovx, omovy;
uniform float msclx, mscly, msina, mcosa, mmovx, mmovy;
float oldx, oldy, x, y;

out vec3 clr_out;

void main() {
    x = pos.x;
    y = pos.y;

    x = x * oscly;
    y = y * osclx;
    oldx = x;
    oldy = y;
    x = oldx * ocosa - oldy * osina;
    y = oldx * osina + oldy * ocosa;
    x = x + omovx;
    y = y + omovy;

    x = x * msclx;
    y = y * mscly;
    oldx = x;
    oldy = y;
    x = oldx * mcosa - oldy * msina;
    y = oldx * msina + oldy * mcosa;
    x = x + mmovx;
    y = y + mmovy;

    gl_Position = vec4(x, y, 0.0f, 1.0f);
    clr_out = clr_in; }
3

あなたが提供した利点は本当であると思いますが、マトリックスには特定の利点があり、それは非常に重要だと思います。グラフィックスで実行したいほとんどすべての変換-スケーリング、回転、変換など-はマトリックスとして表すことができます乗算。共通のフォーマット-できればインターフェースを用意するのはいいことです。さらに、操作の構成は、単一のマトリックスとして表すこともできます。したがって、スケーリング、回転、スケーリング、変換、および回転を実行する場合、それはまだ1つの行列にすぎません。それはかなり大きな勝利です。それは、レンダリングされるオブジェクトの階層などで、個々の変換タイプが「側面で」保持されない可能性があることを意味しているわけではありません。「mov_x」を調整できることは確かに価値がありますが、グラフィックパイプラインは内部的に行列に依存する傾向があります。

7
J Trana

変換を行列として表すには、いくつかの理由があります。

  1. ベクトルは3Dグラフィックでよく使用されます。
  2. 行列は、複数のベクトルを格納するための効率的なデータ構造です。
  3. 最新のグラフィックスパイプラインはシェーダーベースであり、クライアントアプリケーションがデータ(バッファーなど)を迅速に送信する必要があります。
  4. クライアントは単一の関数呼び出しを使用して、頂点シェーダーの変換マトリックスを単一のユニフォームに送信できますが、提案されたアプローチでは、複数の独立した頂点のユニフォームとクライアントが問題multipleglDrawArraysまたは同等のものに到達する前の関数呼び出し-これにより、GPUが長時間アイドル状態になり、効率が低下します。
  5. 計算は、提案されたアプローチよりも実行が簡単で、エラーが発生しにくくなります(複雑なシェーダーをgl_Position = projection_matrix * view_matrix * model_matrix * position_vector;のような単純なものと比較してください)
  6. 行列を使用すると、変換を1つの行列に累積し、多くの場合に数学演算の数を大幅に削減できます(例:gl_Position = pvm_matrix * position_vector;

結局のところ、あなたはより効率的になっていると考えるのは間違いです。変換/回転/拡大縮小する数学演算は、どのように見ても同じであり、コードは必要以上に複雑に見えます。ユニフォームを1つしか追跡できず、OpenGLを1回しか呼び出せないのに、4x4マトリックスのユニフォームを16個追跡する必要がある(または、コードを管理している人が)必要があるのはなぜですか?マトリックスごと?私はそのようなコードに取り組みたくありません。

また、3Dグラフィックスの効率の多くは、GPUの並列処理と、データをすばやく消費する能力に起因します。ハードウェアは特定の一般的なケースを処理するようにすでに最適化されており、変換を格納するためのデータ構造として行列を使用することは非常に一般的であるため、GLSLに独自のデータ型が組み込まれています(例:mat4)。 「なぜ_____幾何学的形状を使用できるのに、なぜ3Dモデルは常に三角形を使用して構築されるのですか?」と尋ねるようなものです。トライアングルを使用することには多くの利点があり[1]、ハードウェアはすでにトライアングル用に最適化されています。

[1]たとえば、三角形が同一平面上にあると、一部のグラフィックアルゴリズムがよりシンプルになり、コーナーケース(クリッピングなど)を心配する必要がなくなります。

6
code_dredd
  • シンプルさ。多くのコードは面倒で、シェーダーを書くには柔軟性が必要です。私はあなたが過去に示した最適化を行いました、そしてそれがしたすべては私を傷つけそして私に頭痛を与えました。

  • 並列処理。 GPUがそれを行うと言っているわけではありませんが、それは間違いなくそれを最適化する機会を与えます。これらのすべての命令を使用すると、GPUは何を実行しているのかまったくわからないため、それ自体で最適化を行うことはできません。

GPUは、遅延書き込みされたAAAエンジン用に設計された数を圧倒する獣であり、そのような自家製の最適化に対して不正であることに留意してください。それを変えることは、技術がまだ進化していて、規格が絶えず書き直されているので、まだメーカーや誰の議題にもなっていない。

マトリックスにダンプして、マシンに少し不正をさせてください。

0
demanze