16
13

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

floatの値を4つの8bit intに変換して保持する

Posted at

概要

テクスチャにデータを詰めてGPUに送り、色ではなくデータとして扱いたいケースはままあると思います。
ただ、通常のテクスチャは各要素が8bitの精度しかなく、32bitのfloatを送ろうとしても当然、ひとつの要素では表現しきれません。

しかしテクスチャはRGBAと4つの要素があり、合計で32bitを保持することができます。
今回は32bitのfloatの値を、4つの8bit intに変換して保持する方法をメモしておこうと思います。(毎回忘れるので)

ちなみに以前にも似た記事([Shader] floatをfixedに変換し、RGBAに格納する)を書いていたんですが、少し違った方法なので改めてメモです。

コードサンプル

public class Hello
{
    public static void Main()
    {
        const float test = 0.93104938143193145f;
        float v1 = test * 255;
        int r = (int)v1;
        float v2 = (v1 - r) * 255;
        int g = (int)v2;
        float v3 = (v2 - g) * 255;
        int b = (int)v3;
        float v4 = (v3 - b) * 255;
        int a = (int)v4;
        
        float r1 = (float)r / 255f;
        float r2 = (float)g / (255f * 255f);
        float r3 = (float)b / (255f * 255f * 255f);
        float r4 = (float)a / (255f * 255f * 255f * 255f);
        
        float result = r1 + r2 + r3 + r4;
        System.Console.WriteLine(test - result);
    }
}

これの実際に動くC#のデモは以下になります。

アイデア自体はこちらの記事で書かれていたものをC#で書いただけのものです。

解説

やっていることは比較的シンプルです。
floatの値を255倍することで8bitに収まるように整数を取り出す、という操作を繰り返すだけです。

参考にした記事では 0.42889250633567028616881555337687を保存していく過程が書かれています。
まず、これを255倍してみましょう。すると、

 0.42889250633567028616881555337687 * 255 = 109.3675891155959229730479661111

となります。
そして整数部分だけを取り出すので109を保存しておきます。
試しにこれを255で割ると109 / 255 = 0.42745098039215686274509803921569という数値になります。

元のfloatの値と比較してみると誤差は0.001441525943513423423717514161となります。
ここの精度をもっと上げるために、他のテクスチャの要素にさらに値を保存していくことで達成します。

具体的には109.3675891155959229730479661111の小数点以下だけを取り出し、最初に行った操作と同じことをします。
つまり、

0.3675891155959229730479661111 * 255 = 93.7352244769603581272313583305

ということですね。そしてまたこの整数部分の93を保持します。
そしてこれを要素分(つまり4回)繰り返し、それぞれの整数部分を保持しておきます。

保存した4つの要素を使って元の32bit floatの値を復元するためには、保存している整数を255のべき乗で割ったものを足しわせることで行います。
つまり、

109/255^1 + 93/255^2 + 187/255^3 = 0.42889247725233884403434576444957

という感じです。(参考にした記事では24bitの3要素なので3つ分だけ計算しています)

元の数値と比べてみると、

元の数値: 0.42889250633567028616881555337687
復元した数値: 0.42889247725233884403434576444957

だいぶ小さい誤差まで復元できていることが分かるかと思います。

シェーダでコンパクトに計算

なお、シェーダでこれを実行する場合はとてもスマートに変換する方法がこちらの記事で書かれていました。

inline float4 EncodeFloatRGBA( float v ) {
  float4 enc = float4(1.0, 255.0, 65025.0, 16581375.0) * v;
  enc = frac(enc);
  enc -= enc.yzww * float4(1.0/255.0,1.0/255.0,1.0/255.0,0.0);
  return enc;
}

inline float DecodeFloatRGBA( float4 rgba ) {
  return dot( rgba, float4(1.0, 1/255.0, 1/65025.0, 1/16581375.0) );
}

めちゃめちゃスマート。すごい。

16
13
3

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
16
13

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?