2

opengl-es 2.0 を使用して、GPU 上の画像の勾配ベクトル フィールドを計算しようとしています。GPU実装との比較として使用するCPU実装を見つけました。ここでの課題は、CPU 実装が Java 型 float (32 ビット) に依存しているのに対し、私の gpu 実装は lowp float (8 ビット) を使用していることです。より良い結果を得るために mediump または highp を使用できることはわかっていますが、コードが可能な限り最も貧弱なハードウェアで実行できることを確認するために、引き続き lowp float を使用したいと考えています。

勾配ベクトル場を計算するための最初のいくつかの手順は非常に簡単です。

  1. 正規化されたグレースケール (赤 + 緑 + 青)/3.0 を計算します。
  2. エッジ マップ (右ピクセル - 左ピクセル)/2.0 および (上ピクセル - 下ピクセル)/2.0 を計算します。
  3. ラプラシアンを計算します (もう少し複雑ですが、ここで詳細を説明する必要はありません)

現在、派手なことをしなくても、CPU実装からの画像結果がGPUからの画像結果と同じになるように、ステップ1を正確に模倣することができます。

残念ながら、私のエッジ マップの計算は GPU で十分に正確ではないため、既にステップ 2 に行き詰まっています。

そこで、 http://andrewthall.org/papers/df64_qf128.pdfから着想を得て、拡張精度浮動小数点を実装しようとしました。

私はopengl-esにかなり慣れていないので、ここで正しく行ったかどうかさえわかりませんが、現在苦しんでいるこの精度の低下を解決するためにコーディングするつもりだった操作を以下に示します.

    vec2 split(float a)
{
    float   t   =   a * (2e-8+1.0);
    float   aHi =   t - (t -a);
    float   aLo =   a - aHi;

    return vec2(aHi,aLo);
}

vec2 twoProd(float a, float b)
{
    float   p   = a * b;
    vec2    aS  = split(a);
    vec2    bS  = split(b);
    float   err = ( ( (aS.x * bS.x) - p) + (aS.x * bS.y) + (aS.y * bS.x) ) + (aS.y * bS.y);

    return vec2(p,err);
}

vec2 FMAtwoProd(float a,float b)
{
    float   x   =   a * b;
    float   y   =   a * b - x;

    return vec2(x,y);
}

vec2 div(vec2 a, vec2 b)
{
    float   q   = a.x / b.x;
    vec2    res = twoProd( q , b.x );
    float   r   = ( a.x - res.x ) - res.y ;

    return vec2(q,r);
}

vec2 div(vec2 a, float b)
{
    return div(a,split(b));
}

vec2 quickTwoSum(float a,float b)
{
    float   s   =   a + b;
    float   e   =   b - (s-a);

    return vec2(s,e);
}

vec2 twoSum(float a,float b)
{
    float   s   =   a + b;
    float   v   =   s - a;
    float   e   =   ( a - (s - v)) + ( b - v );

    return vec2(s,e);
}

vec2 add(vec2 a, vec2 b)
{
    vec2    s   =   twoSum(a.x , b.x);
    vec2    t   =   twoSum(a.y , b.y);

    s.y     +=  t.x;
    s       =   quickTwoSum(s.x,s.y);
    s.y     +=  t.y;
    s       =   quickTwoSum(s.x,s.y);

    return s;
}

vec2 add(vec2 a,float b)
{
    return add(a,split(b));
}

vec2 mult2(vec2 a,vec2 b)
{
    vec2    p   =   twoProd(a.x,b.x);
    p.y     +=  a.x * b.y;
    p.y     +=  a.y * b.x;
    p       =   quickTwoSum(p.x,p.y);

    return p;
}

vec2 mult(vec2 a,float b)
{
    return mult2(a, split(b));
}

明らかに、単純な操作を使用しているか、拡張浮動小数点操作を使用しているかに関係なく、同じ結果が得られるため、ここで何か間違っているか、いくつかの非常に基本的な概念を見逃している必要があります...

4

1 に答える 1

0

ここでの課題は、CPU 実装が Java 型 float (32 ビット) に依存しているのに対し、私の gpu 実装は lowp float (8 ビット) を使用していることです。

lowp実際には、浮動小数点演算に使用されるビット数を意味するものではありません。これは、表現可能でなければならない値の範囲と最小の識別可能な値 (精度) に関係しています。これを使用して最小ビット数を計算できますが、GLSL ではそのように議論することはありません。

現在、派手なことをしなくても、CPU実装からの画像結果がGPUからの画像結果と同じになるように、ステップ1を正確に模倣することができます。

あなたの説明の差し迫った問題は、範囲 [ -2.02.0 ]lowpの値を表すことのみが保証されているという事実に起因するため、これは幸運です。低精度の浮動小数点値を3で割って正規化しようとすると(手順 1を参照)、うまくいかない場合があります。最悪の場合、浮動小数点値が3.0に到達しないため、これは機能しません。ただし、GPUによっては、GLSL ES 1.00 仕様の4.5.2 Precision Qualifiersで概説されている最小要件を超えている可能性があるため、GPUとの間に違いがないため、機能する場合があります。lowpmediumplowp

...それでも、コードが可能な限り貧弱なハードウェアで実行できることを確認するために、lowp float を使用し続けたいと考えています。

可能な限りローエンドのハードウェアをターゲットにしている場合は、ES 2.0 がmediumpすべてのシェーダー ステージでサポートされている必要があることに注意してください。lowp一部の GPU でパフォーマンスが向上することだけが得られる可能性がありますが、ES 2.0 をホストできる GPU はすべて、中精度の浮動小数点をサポートする GPU であり、アルゴリズムはlowp保証よりも大きな範囲を必要とするものです。

于 2015-02-28T21:33:36.477 に答える