5

さまざまな属性を使用してポイントスプライトをz軸を中心に回転できるようにするソリューションを見つけようとしています(つまり、均一では機能しません)。

私のアプリでは、フレームごとに数百/数千のポイントスプライトが描画され、VBOに保存されます(1,000,000を超える可能性があります)。そのため、私はメモリ使用量とパフォーマンスの間の最良の妥協点を探しています。

現在の頂点シェーダーとフラグメントシェーダーは次のようになります。

// VERTEX SHADER
attribute vec4 a_position;
attribute vec4 a_color;
attribute float a_size;
uniform mat4 u_mvpMatrix;
varying vec4 v_color;

void main()
{
    v_color = a_color;
    gl_Position = u_mvpMatrix * a_position;
    gl_PointSize = a_size;
}


// FRAGMENT SHADER
precision mediump float;
uniform sampler2D s_texture;
varying vec4 v_color;

void main()
{
    vec4 textureColor = texture2D(s_texture, gl_PointCoord);
    gl_FragColor = v_color * textureColor;
}

私は現在、次の可能性を想像することができます。

  • mat4 rotMatrixポイントスプライトデータに属性を追加します。これをフラグメントシェーダーに渡し、各フラグメントを回転させます。

    vec2 texCoord = (rotMatrix * vec4(gl_PointCoord, 0, 1)).xy
    gl_FragColor = v_color * texture2D(s_texture, texCoord);
    
    • 利点:
      • シェーダーをシンプルに保ちます。
      • シェーダーの外側の行列を計算するための単純なコード(GLKitたとえばを使用)。
    • 短所:
      • ポイントスプライトデータのサイズを大幅に増やします(4x4マトリックスの場合は16から80バイト/ポイント、3x3マトリックスの場合は52バイト/ポイントに... 3x3回転マトリックスを使用できると思いますか?)。これにより、アプリが3〜5倍早くクラッシュする可能性があります。
      • より多くの計算をCPUにプッシュします(フレームあたり数百/数千の行列計算)。


  • ポイントスプライトデータに属性を追加してfloat angleから、頂点シェーダーで回転行列を計算します。上記のように、回転行列をフラグメントシェーダーに渡します。

    • 利点:
      • ポイントスプライトのデータサイズを小さく保ちます(16〜20バイト/ポイント)。
      • 重い行列の計算をGPUにプッシュします。
    • 短所:
      • 回転行列を作成するには、カスタムGLSL関数を作成する必要があります。大きな問題ではありませんが、私の行列の計算は錆びているので、特に3x3の行列の解を理解しようとしている場合は、エラーが発生しやすい可能性があります...
      • これは数百/数千の頂点で発生する必要があることを考えると、これはパフォーマンスの深刻な低下になりますか(GPUによって処理されているにもかかわらず)?


  • 角度属性の1バイトに現実的に対処できました(255の異なる角度で十分です)。同じ回転行列を不必要に再計算する必要がないように、ある種のルックアップを使用できる方法はありますか?定数を頂点シェーダーに格納することは私の最初の考えでしたが、ブランチステートメントをシェーダーに配置し始めたくありません。

良いアプローチについて何か考えはありますか?

4

4 に答える 4

2

私が最後に行った解決策は、質問から2番目でした:頂点シェーダーで回転行列を計算します。これには次の利点があります。

  • ポイントスプライトのデータサイズを小さく保ちます。
  • 回転計算はGPUによって実行されます。

私が推測した不利な点は当てはまらないようです。第1世代のiPadで実行していても、パフォーマンスの低下に気づいていません。GLSLでの行列計算はやや面倒ですが、正常に機能します。同じことをしようとしている他の人のために、頂点シェーダーの関連部分を次に示します。

//...
attribute float a_angle;
varying mat4 v_rotationMatrix;

void main()
{
    //...

    float cos = cos(a_angle);
    float sin = sin(a_angle);
    mat4 transInMat = mat4(1.0, 0.0, 0.0, 0.0,
                           0.0, 1.0, 0.0, 0.0,
                           0.0, 0.0, 1.0, 0.0,
                           0.5, 0.5, 0.0, 1.0);
    mat4 rotMat = mat4(cos, -sin, 0.0, 0.0,
                       sin, cos, 0.0, 0.0,
                       0.0, 0.0, 1.0, 0.0,
                       0.0, 0.0, 0.0, 1.0);
    mat4 resultMat = transInMat * rotMat;
    resultMat[3][0] = resultMat[3][0] + resultMat[0][0] * -0.5 + resultMat[1][0] * -0.5;
    resultMat[3][1] = resultMat[3][1] + resultMat[0][1] * -0.5 + resultMat[1][1] * -0.5;
    resultMat[3][2] = resultMat[3][2] + resultMat[0][2] * -0.5 + resultMat[1][2] * -0.5;
    v_rotationMatrix = resultMat;

    //...
}

目立ったパフォーマンスへの影響がないことを考えると、テクスチャマップ/ルックアップを作成して追加のメモリを消費する必要がなく、コードの残りの部分をクリーンでシンプルに保つため、このソリューションは理想的です。

すべての頂点の行列を計算することに欠点がないとは言えません(たとえば、バッテリー寿命の短縮)。さまざまなシナリオでパフォーマンスが問題になる可能性がありますが、必要なものには適しています。

于 2013-01-11T13:57:59.640 に答える
1

事前に計算および回転されたさまざまなテクスチャ(テクスチャアトラス)を使用することを考えましたか?あなたが達成しようとしている効果のためにほんの少しの角度で十分であるならば、これは非常に速い解決策でしょう。

別の注意点として、フラグメントシェーダー内のテクスチャ座標の計算(間接テクスチャルックアップ)にはパフォーマンスの低下があります。これはあなたのケースにとって重要ではないかもしれませんが、覚えておく価値があります。

于 2013-01-11T02:59:59.230 に答える
1

事前に乗算された回転行列は次のとおりです。

v_rotationMatrix = mat3(cos, sin, 0.0,
                        -sin, cos, 0.0,
                        (sin-cos+1.0)*0.5, (-sin-cos+1.0)*0.5, 1.0);
于 2013-06-14T05:00:57.323 に答える
1

FWIW、これは私が入手した3x3の事前計算された行列で、Stuartのコードと一致します。

v_rotationMatrix = mat3(cos、-sin、0.0、sin、cos、0.0、(1.0-cos-sin)* 0.5、(1.0 + sin-cos)* 0.5、1.0);

glsl行列は列優先形式であることに注意してください。

于 2013-06-29T21:15:56.733 に答える