UnityWebRequestとWWWで画像をダウンロードしてテクスチャ化したときにミップマップの有無に違いが違ったのでメモ。 (Unity 2018.3.14)

IEnumerator DownloadTextureByUnityWebRequest(string url)
{
    var rq = UnityWebRequestTexture.GetTexture(url, true);
    yield return rq.SendWebRequest();

    Texture2D tex = DownloadHandlerTexture.GetContent(rq);
    Debug.Log($"WebRequest {tex.mipmapCount}");
}

UnityWebRequestを使った場合はミップマップカウントは無効になる。(ミップマップカウントが1)

IEnumerator DownloadTextureByWWW(string url)
{
	var www = new WWW(url);
	yield return www;

	Texture2D tex = www.textureNonReadable;
	Debug.Log($"WWW {tex.mipmapCount}");
}

WWWを使った場合は、ミップマップが有効になる。(ミップマップカウントが1より大きい)

左がUnityWebRequestでダウンロード、右がWWWでダウンロードしたテクスチャ。

実際に動かしたテストコード
https://github.com/sapphire-al2o3/DownloadTextureTest

考察

Unity2018.3ではWWWは非推奨となり中身がUnityWebRequestで実装されている。
https://github.com/Unity-Technologies/UnityCsReference/blob/053d3ce1c53ee3dd4ff1aa08522c1284453f0796/Modules/UnityWebRequestWWW/Public/WWW.cs

WWW.textureが内部でミップマップのフラグを指定せずにテクスチャを作っているためミップマップが生成されている。UnityWebRequestでもミップマップを生成したい場合は、上記のWWW.textureと同じようにTexure2Dを生成してLoadImageでバイナリデータを流し込む必要がある。

投稿者 tel | 2019年4月23日

C#でDictionaryの要素をすべて初期化する

Dictionary<string, int> dic = new Dictionary<string, int>();
dic.Add("a", 1);
dic.Add("b", 2);
dic.Add("c", 3);

foreach (var e in dic)
{
    dic[e.Key] = 0;
}

要素が追加されているDictionaryのすべての要素を0にしたい場合、上記のようにかくとInvalidOperationException例外が出る。

Keyを配列にコピーする

一度キーを配列にコピーして全要素に対して値を入れる。

string[] keys = new string[dic.Count];
int index = 0;
foreach (var e in dic)
{
    keys[index++] = e.Key;
}
foreach (var key in keys)
{
    dic[key] = 0;
}

KeyをListにコピーする

上記のリスト版。コンストラクタでKeyのリストにできるのがシンプル。

foreach (var key in new List<string>(dic.Keys))
{
    dic[e.Key] = 0;
}

KeyをToArrayする

上記と同じだがLinqを使う。

foreach (var key in new dic.Keys.ToArray())
{
    dic[e.Key] = 0;
}

2重ループして要素をたどる

余計なメモリ確保がないが、計算量がO(n^2)になるのでリストの数が多いと実用的でない。

bool finished = false;
while (!finished)
{
    finished = true;
    foreach (var e in dic)
    {
        if (dic[e.Key] != 0)
        {
            dic[e.Key] = 0;
            finished = false;
            break;
        }
    }
}

まとめ

いい方法はないので全要素を回すような場合はDictionaryを使わないのがいい。
Dictionaryの列挙で、要素の追加でなければ順序は保たれているのに代入したときに例外が出るようになっているのはどうしてだろう。

投稿者 tel | 2019年3月31日

nrLaunchのスキン

3/31でジオシティーズが閉鎖されるので置いてあったnrLaunchのスキンをいくつか移した

https://github.com/sapphire-al2o3/nrLaunchSkin

投稿者 tel | 2019年2月28日

HDRファイルの読み込み

HDRファイルの読み込み処理を作った。

https://github.com/sapphire-al2o3/hdr_loader

HDRファイルはIBL(イメージベースドライティング)などで用いる画像フォーマットで1ピクセルに仮数部(RGB)と指数部(E)の4バイトで構成されている。

http://radsite.lbl.gov/radiance/refer/Notes/picture_format.html

以下のクリエイティブコモンズのライセンスで公開されているHDRファイルを読むこむことを目的とするのでランレングス圧縮されている32-bit_rle_rgbeになっているものだけ対応する。

https://hdrihaven.com/hdris/

HDRファイルでは、RGBの値は仮数として1byteで保存されている。その後に指数部が1byteで保存されている。RGBEからもとのfloatの値に戻すには仮数部xと指数部eを使って次のように変換する。

(x / 256) * 2 ^ (e - 128)

256で割る部分をまとめると

x * 2 ^ (e - 136)

上記のような計算で1ピクセルが浮動小数のRGBとして求められる。

正しく読み込めているか確認するために、線形マッピングして24ビットRGBで出力した。高輝度領域が少ないので全体的に暗い。

 

投稿者 tel | 2019年1月11日

2019年

Happy New Year!

Node.jsのpngjsなどでPNG形式の画像を出力したとき、どんなにサイズが小さい画像でもIDATチャンクが分割されて出力されてしまうことがあったので調べてみた。

https://www.npmjs.com/package/pngjs

はじめに

PNGは画像部分がIDATチャンクに格納されている。IDATチャンクはzlibのdeflate圧縮されたピクセル情報になっている。(インデックスカラーの場合はインデックス情報)
IDATチャンクはいくつあってもいいが、IDATチャンクの数がおおいとその分のオーバーヘッドがあるのでファイルサイズが増える。

pngjsなどのNode.jsでPNGを出力するモジュールでは画像サイズがどんなにちいさくてもIDATチャンクが分割されて出力されてしまっている。


2×2のPNGを出力してバイナリエディタで表示するとIDATチャンクが2つあるのがわかる。無駄にサイズが大きくなってしまっているのでなんとかしてIDATチャンクが分割されないようにしたい。

なぜIDATチャンクが分割されるか

Node.jsでzlibでdeflate出力したときにzlibのヘッダ部分(2byte)が最初にイベントとしてくるため、ヘッダだけIDATチャンクにしてしまっている。

this._deflate.on('data', function(compressedData) {
    this.emit('data', this._packer.packIDAT(compressedData));
}.bind(this));

IDATチャンクが分割されないようにする

あんまりかっこよくないけどzlibヘッダ部分を覚えておいてあとで本体がきたときに一緒につけてあげることにした。

https://github.com/sapphire-al2o3/node-pngjs

deflate.on('data', function(data) {
    if(this._deflateHeader === 0 &amp;&amp; !this._deflate) {
        this._deflateHeader = (data[0] &lt;&lt; 8) | data[1];
        this._deflate = true;
    } else {
        this.emit('data', this._packIDAT(data, this._deflateHeader));
        this._deflateHeader = 0;
    }
}.bind(this));


同じく2×2のPNGを出力した。IDATチャンクが一つになってファイルサイズが減っている。

 

投稿者 tel | 2018年11月30日

ParticleSystemのメッシュが残る2

https://spphire9.wordpress.com/2017/05/30/particlesystem%E3%81%AE%E3%83%A1%E3%83%83%E3%82%B7%E3%83%A5%E3%81%8C%E6%AE%8B%E3%82%8B/

アセットバンドルのビルドログになぜか使用していないはずのメッシュが入っていたので調べてみたら、ParticleSystemのRenderer以外にもメッシュが残る場合があった。

ShapeモジュールをMeshやMeshRendererに指定したあと、メッシュを使わないConeなどに変更した場合にメッシュが残ってしまう。

まとめて2017年分

続きを読む…

投稿者 tel | 2018年10月7日

C#での文字列と数字の連結

C#で文字列と数値を連結するときに数値をToStringしなくても連結できる。

string a = "a" + 1.ToString();
Console.WriteLine(a);    // a1
string a = "a" + 1;
Console.WriteLine(a);    // a1

文字列同士の連結は string.Concat(string, string) が呼び出されるが、文字列と数値を連結するときは string.Concat(object, object) が呼び出されている。

もちろん string.Concat(object, object) を呼び出した場合、boxingがされるので文字列同士の連結をしたほうがいい。普通は文字列に数値を連結する場合に直接連結することはないのでいいのだけど、以下のコードのようにパースしたあとに加算しようと思ってたのに先に加算したことで文字列連結になってしまってバグるということがあった。

string a = "10";
int b = int.Parse(a + 1);

ほんとは以下のように書きたかった。

string a = "10";
int b = int.Parse(a) + 1;

文字列同士の加算じゃない場合は、コンパイラエラーになってくれるといいのに。

投稿者 tel | 2018年9月30日

TGS2018

Tokyo Game Show 2018に行ってきた。9月21日のビジネスデイ。

毎年、いちおう行ってはいるんだけど並ぶのが嫌なのであんまりプレイしないで見て回っているだけになっている。
最近はメーカーのゲームを見るよりもインディーブースや専門学校のブースのゲームを見るのが面白くなってきた。並ばずに遊べるということもあるけど、1箇所にいろんなゲームがまとまっているのが遊びやすい。あとコンシューマでもスマートフォンゲームでもがっつりと作られたものは面白くなるまでにある程度プレイしないといけないので試遊だけだとなかなか面白さが伝わらない。

特に印象に残ったのが、ゲーム大賞のアマチュア部門の応募作品の「PROJECTION RMAINS」。
http://awards.cesa.or.jp/prize/amateur/05.html

ゲームの内容はステージクリア型のパズルゲームでキャラクターを操作して扉までたどり着けばクリアとなる。特徴的なシステムとしてスクリーンを表示するエリアが存在し、スクリーンに別のエリアを写して足場などにできる。

他にもグラフィックスのクオリティが高い作品もあったが、ドット絵がすきなのもあるけど全体的な統一感があるアートがよくできている。
レベルデザインもよくできていて、できる操作やギミックなどがそれほど多くないのに、こんな解法もあったのかと思わせる作りになっている。
全体的な完成度が高くて、また遊んでみたくなる作品だった。

Older Posts »

カテゴリー