常に最新の Arduboy ライブラリを使う

概要

Arduino IDE によるライブラリ管理では満足出来ない御仁のための TIPS です。

はじめに

Arduboy ことはじめ

上記の記事で、Arduino IDE の Manage Libraries を使って、Arduboy のコアライブラリをインストールしましたが、残念ながらあそこから入るのは最新ではありません。

最新は、Github 上のリポジトリから入手可能です。

入手方法

古いライブラリを消す

Arduino IDE を終了させた状態で、 ~/Documents/Arduino/libraries/Arduboy フォルダをざっくり消します。

新しいライブラリを入れる

ターミナルを起動する

黒い画面を開きます。

Github から clone する

1
2
3
4
5
6
7
8
9
10
11
12
[k-ya@meteor] ~
% cd Documents/Arduino/libraries
[k-ya@meteor] ~/Documents/Arduino/libraries
% git clone https://github.com/Arduboy/Arduboy.git
Cloning into 'Arduboy'...
remote: Counting objects: 1148, done.
remote: Total 1148 (delta 0), reused 0 (delta 0), pack-reused 1148
Receiving objects: 100% (1148/1148), 295.96 KiB | 160.00 KiB/s, done.
Resolving deltas: 100% (664/664), done.
Checking connectivity... done.
[k-ya@meteor] ~/Documents/Arduino/libraries
%

~/Documents/Arduino/libraries へ移動し、Github からクローンします。

これで、最新のライブラリがインストールされました。

常に最新に保つ

最新だから良いというわけではありませんが、最新版を追っかけたい場合は、ターミナルから、 ~/Documents/Arduino/libraries/Arduino へ移動し、

1
2
[k-ya@meteor] ~/Documents/Arduino/libraries/Arduino
% git pull origin master

とすると、更新のあったファイルを落としてくることが出来ます。

おわり

Arduino IDE からインストール出来る 1.1.0 から最新の 1.2.0 では、文字表示ライブラリが追加されていたり、サウンドの処理からデフォルトの割り込みベクタがなくなっていたりして、結構変わっているようです。

Arduboy への道

概要

Arduboy が届かなさすぎておかしくなった頭を冷やすための記事です。

小ネタ

外部エディタで書きたい

  • 書いてもいいけど、そのままだと IDE に変更が反映されない
  • Preferences > Use external editor を有効にする
  • IDE から編修出来なくなるけど、外部エディタで編集して IDE に戻るとファイルを読み込み直してくれる

逆アセンブルしたい or コンパイルした結果がどうなってるか知りたい

  1. コンパイルした結果を取得出来るようにする
  • Arduino/Genuino 101 の追加メモ 2016/02 を参考にする。この記事は Windows 版向けだけど、OSX 版でも同じようにできる。
  • ~/Library/Arduino15/preferences.txt を編集。 build.path=ビルド結果を出力するパス を追加する
  • 権限的に問題ないパスを指定するよう注意 ex) /Users/Hoge/Documents/Arduino/build
  1. 逆アセンブルする
  • /Applications/Arduino.app/Contents/Java/hardware/tools/avr/bin に色々入ってるのでそれを使う
  • 適当にパスを通すなどして
  • % avr-objdump -hdSC hoge.elf > hoge.txt
  1. 読む
  • 実際に転送されるのは、 hoge.ino.hex なのかな?
  • この辺は Arduino の文献を漁った方がよさそう

早く受け取りたい

  • 日本の国際交換局に到着した段階で直接交換局に行き、受け取ることが可能らしい -> 早期通関申請
  • 行く気満々だったが、タッチの差で発送されてしまった

GBuffer helper – Packing integer and float together

この記事は、こちらの記事を日本語に翻訳したものです(翻訳の許可は取っています)。

Version : 1.0 – Living blog

With GBuffer approach, it is often required to pack values together. One useful case is to be able to pack an integer value (often matching an enum like material ID) and a float value (inside 0…1 range).

GBufferを利用するアプローチにおいて頻繁に様々な値をパックする必要がある。特によく使われるのは、整数値(マテリアルIDのような列挙型)と浮動小数値(0〜1の間)をパックするようなケースだ。

For example in Unity High Definition Render Pipeline we have pack inside the GBuffer:

UnityのHDPRにおけるGBuffer内部では以下のようなパッキングを行っている。

  • DiffusionProfile (16 values) and Subsurface Mask (Float 0…1)
  • Material Features (8 values) and Coat Mask (Float 0…1)
  • Quadrant for tangent frame (8 values) and metallic (Float 0…1)
  • 拡散反射タイプ(16個)とサブサーフェスマスク(浮動小数点数0…1)
  • マテリアル効果(8個)とコートマスク(浮動小数点数0…1)
  • 接線空間における象限(16個)とメタリック値(浮動小数点数0…1)

During development we have change several times the number of bit required for our packing and it quickly come to us that we were needed to have general packing functions to encode arbitrary values. This is the topic of this short blog post.

開発中、我々は幾度となくパック中のビット数を変更し、我々が任意の値をパックするためにパッキング関数が必要なことにすぐ気づいた。これは、このブログポストのトピックである。

Let’s say that we want to encode a Mask on 1 bit with a Scalar in range 0…1 with 8 bit of precision inside a shader. Mean in practice we pack both values in a component of a RGBA 32bit render target. Remember that the float value in the shader is convert at the end of the pipeline to corresponding render target format, in our case the float value will be multiply by 255 to fit into the 8bit precision of the component. We will have 7 bits to encode the float, this could be perform with a simple remapping:

さて、我々は1bitのマスクと0…1のスカラー値を8bit精度にエンコードしたいと思っている。これは、双方の値をRGBA32レンダーターゲットの1要素へパックすることである。忘れないで欲しいのは、シェーダ内の浮動小数点数はパイプラインの最後で対応するレンダーターゲットのフォーマットに変換される。今回の場合、浮動小数点数は255を掛けられて8bit精度の要素になる。我々は7bitを浮動小数点数のエンコードに使う。これはシンプルな変換式で実行できる。

1
(127.0 * Scalar)  / 255.0

multiply by 127 (or (1 << 7) – 1) which is 01111111 in binaries leave 1 bit available for the Mask.

二進数で0b01111111である127(もしくは(1<<7)-1)を掛けることで、1bitをマスクのために使えるようになる。

Then we divide by 255.0

そして、255で割る。

Then we need to add the bit for the mask itself at the 8th position mean value of 128 (or (1 << 8) – 1)

そして、我々はマスクのために、8番目のbitである128(もしくは(1<<8)-1)を足す。

1
(128.0 * Mask) / 255.0

So encoding is

そしてエンコード式は

1
Val = (127.0 / 255.0) * Scalar + (128.0 / 255.0) * Mask

Decoding should be the reverse of the operation above. First we need to retrieve the Mask value

デコードは、上記の式と反対を行う必要がある。まず最初に、マスク値から取得する。

1
Mask = int((255.0 / 128.0) * Val)

Note that here we use the int cast to remove all the Scalar value part.
For example if we have Scalar of 0 and Mask with 1, Val is suppose to be 128.0 / 255.0.
Mean the above code give us 1

ここでは、スカラー値部分をそぎ落とすためにintキャストを使う。
例えば、スカラー値0とマスク値1を持つ場合、Val128.0/255.0になるはずだ。
この場合、上記の式は1を返すことになる。

if Scalar is 1, Val is suppose to be 1.0, mean Mask = int(1.9921875) = 1. All good.

もし、スカラー値が1の場合、Valは1.0になるはずだ。これは、Mask = int(1.9921875) = 1となるので、バッチリだ。

We then retrieve the value of Scalar

次にスカラー値を取り出す。

1
Scalar = (Val - (128.0 / 255.0) * float(Mask)) / (127.0 / 255.0)

Now let’s consider a RGBA1010102 render target with a Mask on 4 bit. The process is exactly the same.
First remap value to cover 6 bit for the float value and 4 bit for the Mask value

今、RGBA1010102のレンダーターゲットで、4bitマスクを持つ場合を考えてみよう。手順は先ほどと同じ。
まず、浮動小数点数を6bit、マスク値を4bitに再マップする。

1
Val = (63.0 / 1023.0) * Scalar + (64.0 / 1023.0) * Mask

For example if Mask is 2 (i.e 128.0 / 1023.0) and Scalar is 1.0 we get 0010 1111 11 as binaries representation.
4 bit for Mask then 6 bit for Scalar.

例えば、マスク値が2(すなわち128.0/1023.0)とスカラー値が1.0の時、0b0010_1111_11が得られる。
4bitがマスク値、6bitがスカラー値に使われている。

For decoding we first retrieve the Mask then the Scalar

デコードは、まずマスク値、そしてスカラー値の順に取り出す。

1
2
Mask = int((1023.0 / 64.0) * Val)
Scalar = (Val - (64.0 / 1023.0) * float(Mask)) / (63.0 / 1023.0)

Important addition.
Due to rounding and floating point calculation on GPU it may appear that Mask reconstruction is shifted by one value.
This can be fixed by adding the smallest epsilon allowed by the render target format. i.e

重要なことがある。
GPU中で浮動小数点演算や丸めが行われる間に、マスクの再構成は一つの値によってずらされるように見えるかもしれない。(訳注:?)
これは、レンダーターゲットに許容される最小値を足すことで回避できる。すなわち、

1
Mask = int((1023.0 / 64.0) * Val + 1.0 / 1023.0)

We can easily generalize this process for any unsigned render target format and any Mask size. Here are the functions to do the work.

我々は簡単にこれらの手順を、様々な符号なしレンダーターゲットフォーマット、及び様々なマスクサイズに対して一般化できる。
ここに動く関数がある。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
float PackFloatInt(float f, uint i, uint numBitI, uint numBitTarget)
{
// Constant optimize by compiler
float precision = float(1 << numBitTarget);
float maxi = float(1 << numBitI);
float precisionMinusOne = precision - 1.0;
float t1 = ((precision / maxi) - 1.0) / precisionMinusOne;
float t2 = (precision / maxi) / precisionMinusOne;

// Code
return t1 * f + t2 * float(i);
}

void UnpackFloatInt(float val, uint numBitI, uint numBitTarget, out float f, out uint i)
{
// Constant optimize by compiler
float precision = float(1 << numBitTarget);
float maxi = float(1 << numBitI);
float precisionMinusOne = precision - 1.0;
float t1 = ((precision / maxi) - 1.0) / precisionMinusOne;
float t2 = (precision / maxi) / precisionMinusOne;

// Code
// extract integer part
// + rcp(precisionMinusOne) to deal with precision issue
i = int((val / t2) + rcp(precisionMinusOne));
// Now that we have i, solve formula in PackFloatInt for f
//f = (val - t2 * float(i)) / t1 => convert in mads form
f = saturate((-t2 * float(i) + val) / t1); // Saturate in case of precision issue
}

// Define various variants for ease of use and code read
float PackFloatInt8bit(float f, uint i, uint numBitI)
{
return PackFloatInt(f, i, numBitI, 8);
}

void UnpackFloatInt8bit(float val, uint numBitI, out float f, out uint i)
{
UnpackFloatInt(val, numBitI, 8, f, i);
}

float PackFloatInt10bit(float f, uint i, uint numBitI)
{
return PackFloatInt(f, i, numBitI, 10);
}

void UnpackFloatInt10bit(float val, uint numBitI, out float f, out uint i)
{
UnpackFloatInt(val, numBitI, 10, f, i);
}

float PackFloatInt16bit(float f, uint i, uint numBitI)
{
return PackFloatInt(f, i, numBitI, 16);
}

void UnpackFloatInt16bit(float val, uint numBitI, out float f, out uint i)
{
UnpackFloatInt(val, numBitI, 16, f, i);
}

And example usage:

それと使い方。

1
2
3
4
5
6
7
8
9
10
11
// Encode
outSSSBuffer0.a = PackFloatInt8bit(sssData.subsurfaceMask, sssData.diffusionProfile, 4);
// Decode
UnpackFloatInt8bit(inSSSBuffer0.a, 4, sssData.subsurfaceMask, sssData.diffusionProfile);

// Encode
outGBuffer2.a = PackFloatInt8bit(coatMask, materialFeatureId, 3);
// Decode
float coatMask;
uint materialFeatureId;
UnpackFloatInt8bit(inGBuffer2.a, 3, coatMask, materialFeatureId);