Are you sure you want to delete the question?

If your question is resolved, you may close it.

Leaving a resolved question undeleted may help others!

We hope you find it useful!

WebGLの精度について教えて下さい

知りたいこと

GLSLで2の23乗より大きい値を扱うときに期待通りの結果を得られないのですが、その理由が知りたいです。

背景

画像(png)の色をデータとして扱い、今まではJavaScriptのcanvas-2dで処理していたのですが、高速化したくて最近はWebGLで処理するようにしています。
画像の各ピクセルのrgbを以下の計算式で値に変換しています。

value = r * 256 * 256 + g * 256 + b;

rgbそれぞれ8ビットですので24ビット(16,777,216)まで表現することが出来ます。

GLSLでいろいろ記述していて気づいたこと

いろいろシェーダを記述して試行錯誤しているのですが、今日気づいたことがありました。

//パターン1
floor( 1.0 ) == floor( 1.5 )
//パターン2
floor( 8388608.0 ) == floor( 8388608.5 )
//パターン3
floor( 8388609.0 ) == floor( 8388609.5 )

いろいろな判定式を書いていて期待通りに動いてくれないので追試していました。例えば上記で自分の期待としてはいずれのパターンもtureになってほしかったのですが、結果はパターン1とパターン2のみtureでパターン3はfalseになることに気づきました。整数部が8,388,608つまり2の23乗を超えると期待通りの動作をしてくれないことがわかりました。

webglreport.comで確認

最初はいろいろ検索してみたのですがよくわからず、WebGLに関する各種情報を表示してくれるサイトを確認して、あることに気づきました。
https://webglreport.com/

自分の環境では以下の結果が表示されました。

スクリーンショット 2020-12-05 16.54.55.png

ここで青い楕円で示した部分に注目しました。Best float precisionとして[-2^127, 2^127]である点は良いのですが、その下にカッコ書きで23と表示されています。

結論:わからないこと

自分には上記の記述が何を意味しているかわからないのですが、これはもしかして整数部が2の23乗より大きい場合は「Best」じゃない、ということでしょうか。
どなたか詳しい方がいらっしゃったらご教示頂けないでしょうか。
どうぞよろしくお願いいたします。

0

2Answer

Wikipediaの浮動小数点数のページを見て、floatの構造を理解するのが早いと思います。

浮動小数点数のフォーマットを見ると、floatは仮数部が23bitしかありません。なので、

value = r * 256 * 256 + g * 256 + b;

は、精度が足りずfloatでは正確に表現できません。
これらは単純に int value; と符号付32bit整数があるので、それを使えば良いと思います。

ただ、色を値に持つ時はvec3やvec4を使う事が多いので、intで色を表現する事は少ないです。
vec4を使うと各RGB毎にfloat一つずつなので精度は十分足ります。

0Like

Comments

  1. @UsagiLabo

    Questioner

    教えて下さり、ありがとうございました。浮動小数点がとても苦手で・・・。
    「precision highp int;」で整数として演算することにしました。
    テクスチャからはもちろんvec4でとるのですが、私の作業としては色としての情報ではなくrgbから計算して得られる値で大小等を比較するため、前述のような演算で値を決めている次第です。追加で調べたらGLSL4.0になると倍精度が使えるようで待ち遠しいです。

    ご存知でしたら教えていただきたいのですが、webglreport.comの出力の青枠で囲った部分の意味は「仮数部23ビット」という理解で正しいでしょうか。
  2. > 「仮数部23ビット」という理解で正しいでしょうか
    そうですね。倍精度が使えたら52になるはずですが、WebGLはGLSLのバージョンも固定されているので(WebGL2でGLSL3.0)、GLSL4.0が使える事は無い気がしますが…
  3. @UsagiLabo

    Questioner

    ご回答ありがとうございます。いろいろわからないことが多かったのでとても参考になりました。

float (32 bit)が使える double (64 bit)が使える、と言うのは GPU にデータを転送するときの話 で、シェーダ内で扱う float の範囲とは異なる点に注意した方が良いと思います。
(シェーダ内ではそもそも IEEE754 である保証すらないです。)

WebGL では float について、仮数部精度は highp で 16 bit, mediump で 10 bit, lowp で 8 bit のみが最低限保証されています。
また、整数 (int) についても、highp/mediump/lowp が、それぞれ 16/10/8 bit の範囲が最低保証で、それを超える値が扱えるか否かは環境に依ります。

webglreport.com で表示される、Best precision $[-2^{127}, 2^{127}], (23)$ という表記は、表現可能な範囲が最大で $-2^{127}$ から $2^{127}$ までで、仮数部精度が 23 bit であることを表していますが、あくまで 今閲覧しているマシンの スペックでしかないので、これに合わせて作ると環境によっては正しく計算されないのでご注意ください。

ちなみに WebGL2 では float は仮数部が 24/16/8 bit に拡張されています。
int / uint に関しても highp/mediump/lowp が 32/16/8 bit になっています。

WebGL
WebGL2
(それぞれ Qualifers の部分に記載アリ)

0Like

Comments

  1. @UsagiLabo

    Questioner

    とても勉強になる回答を下さり、ありがとうございました。「WebGL 精度」などで検索していたのですが、一番確認すべきkhronosのチートシートを見ていませんでした。
    課金が発生するようなウェブアプリではなく、やや特殊な用途(学術向け)で環境指定も出来そうですので環境面は現状でもクリアできそうです。
    とはいえ早くSafariがWebGL2に対応してくれることを祈るばかりです。

Your answer might help someone💌