なたで日記

いろいろな思ったこと書きますヽ(^▽^ゞ) by natade

Posts Tagged ‘セカンドライフ

セカンドライフで自分の行動の色々な情報を取得する

leave a comment »


次のような自分の行動の情報を取得してみます。
・自分が喋った回数
・人から聞いた回数(アバターが話したものに限定)
・空を飛んでいる時間(ホバーは含めない)
・海の中を泳いでいる時間(ホバーは含めない)
・歩いている時間
・走った時間
・ジャンプ中の時間
・テレポートした回数
・SIMをまたいだ回数
・人や当たり判定がある物体と衝突した回数

以下、サンプルです。
このコードを適当なオブジェクトに入れた後、HUDとして装備してください。
drawStatusと発言すると、自分の情報が表示されます。

ログインした回数とかも取れるかなと思ったのですが、
装備した回数と見分けがつかないので諦めました。

integer count_attack		= 0;
integer count_speak			= 0;
integer count_listen		= 0;
integer count_flying		= 0;
integer count_swimming		= 0;
integer count_running		= 0;
integer count_walking		= 0;
integer count_teleport		= 0;
integer count_simtraversal	= 0;
integer count_jumping		= 0;

integer TYPE_ATTACK			= 0;
integer TYPE_SPEAK			= 1;
integer TYPE_LISTEN			= 2;
integer TYPE_FLYING			= 3;
integer TYPE_SWIMMING 		= 4;
integer TYPE_RUNNING		= 5;
integer TYPE_WALKING		= 6;
integer TYPE_TELEPORT		= 7;
integer TYPE_SIMTRAVERSAL	= 8;
integer TYPE_JUMPING		= 9;

integer show_dialog		= FALSE;
integer listen_handle	= -1;

addAction(integer action) {
//	llOwnerSay("action : "+ (string)action);
	if(action == TYPE_ATTACK) {
		count_attack	= count_attack + 1;
	}
	else if(action == TYPE_SPEAK) {
		count_speak		= count_speak + 1;
	}
	else if(action == TYPE_LISTEN) {
		count_listen	= count_listen + 1;
	}
	else if(action == TYPE_FLYING) {
		count_flying	= count_flying + 1;
	}
	else if(action == TYPE_SWIMMING) {
		count_swimming	= count_swimming + 1;
	}
	else if(action == TYPE_RUNNING) {
		count_running	= count_running + 1;
	}
	else if(action == TYPE_WALKING) {
		count_walking	= count_walking + 1;
	}
	else if(action == TYPE_TELEPORT) {
		count_teleport	= count_teleport + 1;
	}
	else if(action == TYPE_SIMTRAVERSAL) {
		count_simtraversal	= count_simtraversal + 1;
	}
	else if(action == TYPE_JUMPING) {
		count_jumping	= count_jumping + 1;
	}
}

showStatus() {
	llWhisper(0,
		"attack\t:"			+ (string) count_attack			+ " times\n" +
		"speak\t:"			+ (string) count_speak			+ " times\n" +
		"listen\t:"			+ (string) count_listen			+ " times\n" +
		"teleport\t:"		+ (string) count_teleport		+ " times\n" +
		"simtraversal\t:"	+ (string) count_simtraversal	+ " times\n" +
		"flying\t\t:"		+ (string) count_flying			+ " sec\n" +
		"swimming\t\t:"		+ (string) count_swimming		+ " sec\n" +
		"running\t:"		+ (string) count_running		+ " sec\n" +
		"walking\t:"		+ (string) count_walking		+ " sec\n" +
		"jumping\t:"		+ (string) count_jumping		+ " sec"
	);
}

openDialog() {
	if(listen_handle != -1) {
		llListenRemove(listen_handle);
	}
	listen_handle = llListen(-100, "", llGetOwner(), "");
	llDialog( llGetOwner(), "Which operation?\nYou say \"drawStatus\"... or \"resetStatus\"", ["status", "reset", "cancel"], -100 );
	llSetTimerEvent(0.0);
	llSetTimerEvent(30.0);
	show_dialog = TRUE;
}

closeDialog() {
	if(listen_handle != -1) {
		llListenRemove(listen_handle);
	}
	show_dialog = FALSE;
	listen_handle = llListen(0, "", "", "");
	llSetTimerEvent(0.0);
	llSetTimerEvent(1.0);
}

default {
	state_entry() {
		llOwnerSay("Start Script !\nYou say \"drawStatus\"... or \"resetStatus\"");
		closeDialog();
	}
	
	touch_start(integer total_number) {
		openDialog();
	}
	
	timer() {
		if(show_dialog) {
			closeDialog();
		}
		else {
			string my_animatin = llGetAnimation(llGetOwner());
			if(my_animatin == "Flying") {
				vector v = llGetPos();
				if(llWater(v) > v.z) {
					addAction(TYPE_SWIMMING);
				}
				else {
					addAction(TYPE_FLYING);
				}
			}
			else if(my_animatin == "Running") {
				addAction(TYPE_RUNNING);
			}
			else if(my_animatin == "Walking") {
				addAction(TYPE_WALKING);
			}
			else if(my_animatin == "Jumping") {
				addAction(TYPE_JUMPING);
			}
		}
	}
	
	listen( integer channel, string name, key id, string message ) {
//		llOwnerSay(""+ (string)channel +" " + name + " " + (string)id + " " + message);
		if(show_dialog) {
			if(message == "status") {
				showStatus();
			}
			else if(message == "reset") {
				llResetScript();
			}
			else if(message == "cancel") {
			}
			closeDialog();
		}
		else {
			if(id == llGetOwner()) {
				if(message == "drawStatus") {
					showStatus();
				}
				else if(message == "resetStatus") {
					llResetScript();
				}
				else {
					addAction(TYPE_SPEAK);
				}
			}
			else if(llGetAgentSize(id)){
				addAction(TYPE_LISTEN);
			}
		}
	}
	
	changed(integer mask) {
		if(mask & CHANGED_OWNER) {
			llResetScript();
		}
		if((mask & CHANGED_TELEPORT) && (mask & CHANGED_REGION)) {
			addAction(TYPE_TELEPORT);
		}
		else if(mask & CHANGED_TELEPORT) {
			addAction(TYPE_TELEPORT);
		}
		else if(mask & CHANGED_REGION) {
			addAction(TYPE_SIMTRAVERSAL);
		}
	}
	
	collision_start(integer num) {
		if(llDetectedType(0) & ACTIVE) {
			vector my_velocity = llGetVel();
			vector velocity = llDetectedVel(0);
			float power = llVecMag(my_velocity - velocity);
			if(power >= 1.0) {
				addAction(TYPE_ATTACK);
			}
		}
	}
	
	on_rez(integer param) {
		// 1 ログイン、装備、rez
	}
	
	attach(key attached) {
		if(attached) {
			// 2 ログイン、装備
		}
	}
}

Written by なたで

2017年1月15日 at 00:00

カテゴリー: memo, program

Tagged with ,

セカンドライフのプリムの当たり判定の検証

with one comment


こんにちは!

昨日から引き続き、「セカンドライフ技術系 Advent Calendar 2016」です!

すみません。
昨日の記事は、役に立つ情報もあったかもしれませんが、
今回は、テクニック系と違い、前々から気になっていたことを検証する……といった内容となります。
なんかすごいの来る!と期待していた方はスミマセン。(いないと思われますが)

というわけで、

あんまり面白いものではないかもしれませんが、
調査結果の報告というわけで、話していきます……。


はじめに

昨年に、当たり判定の記事を書きました。
セカンドライフでメッシュに物理形状を設定しよう1

今回は、その記事のさらに「実像の種類」について気になったことを確認していきたいと思います。
最初に少し解説しますが、より理解を深めたい方は前回の記事から読むことをお勧めします。

「実像の種類」というのはオブジェクトの設定から選べるものです。
実像というのは、当たり判定用に用いるオブジェクトです。

gui_jituzo_1 → gui_jituzo_2

ここで実像を「なし」「プリム」「凸状の外殻構造」の3種類選べます。
「なし」にした場合は、当たり判定用のオブジェクトを用意しないということになります。

ここで「プリム」「凸状の外殻構造」の違いについて説明するために、
3次元だと書きにくいので、2次元で考えたいと思います。

atari_hazimeni_1
2枚の壁が90度の垂直であることとします。

atari_hazimeni_2
実像の種類を「凸状の外殻構造」にすると、
黄色の囲んだ部分のように当たり判定が生まれます。
※中身すべてにすべて当たり判定を持つことになります。

atari_hazimeni_3
実像の種類を「プリム」にすると、
そのプリムそのものが1枚の壁のように当たり判定を持ちます。
※中身というものは存在しません。


実像の種類「プリム」の疑問

ここで、私の中では素朴な疑問が生まれました。
当たり判定の計算方法により、アバターが透けることがないのかということです。

たまーに、なぜかよくわからないですが、壁の中に入って閉じ込められることありませんか。
それが、このこの設定によるものなのかなーと、疑問に思ったわけです。

セカンドライフの実際の当たり判定のアルゴリズムについては知らないので少し、推測した話となります。
まず、当たり判定といえば、レイと3角ポリゴンとの衝突の検出※が有名です。
そこで、これをまず使用していると仮定します。
(※アルゴリズム、計算方法については、「はじめての3Dゲーム開発」)

そして、もしこのようなアルゴリズムを使って単純に実装している場合、
つまり、1本のレイを飛ばすような当たり判定を使っているのであれば、
次のような現象が起きるのでは……と思いつきます。

atari_nazo_1
壁1と壁2が90度で交わっているとします。

atari_nazo_2
ここで Ray1 を飛ばします。
つまりアバターが壁1に向かって斜めに突進した感じです。

atari_nazo_3
Ray1は、壁1と壁2の2種類に当たり判定チェックを行います。
その結果、壁1との衝突を検知することができます。
そして、めり込まないように、斜めの成分だけを抜き出します。

atari_nazo_4
次に、壁1との衝突点からRay2を飛ばします。
ここで、問題なのですが、Ray2が壁2との当たり判定を正しく行えるかという謎です。

普通は壁1と壁2は接続されているはずなので、
Ray2は壁2と衝突するはずですが、
実際は壁1と壁2は、別のポリゴンとして管理しており、
これらのポリゴンの位置は、実数による誤差の関係上、微妙に隙間が空いているはずなのです。

1本のレイを使用した単純な当たり判定ならば、上記のような問題は起きるのですが、
もちろん、レイを複数とばす。あるいは、そもそも球とポリゴンとの当たり判定を使用するなど
いろいろな工夫を行っていれば、このようなことになりません。
なので、有名なセカンドライフであれば、すり抜けることはおそらくないはずですが。

というわけで、前置きがすごく長くはありましたが、
当たり判定用のプリム=壁のプリムを作成して、
衝突した場合どうなるか検証するのが、この記事の目的となります!


すり抜けの検証

次のような壁(1辺が10m)を作成して、
atari_test_1

下記のように10分間壁に向かって当たり続けて、すりぬけるか確認します。
atari_test2

ビューアは2種類で確認しました。
公式ビューア(v4.1.2.321518)
Catznipビューア(R10.0)の両方で確認しました。

元となるメッシュデータについては、頂点を共有しないメッシュと、
共有するメッシュの2種類について確認。

また、当たり判定のデータは、dae情報で直接設定する方法と、
表示用メッシュから自動生成する方法の2種類を試しました。

それで早速、結果なのですが、

.
..

なんと

.
..

なんとッ!

.
..

すり抜けませんでした!

まあ、そうですよねー。
期待していた方?はスミマセンでした。

ちなみにさらに検証していったのですが、
隙間がわずかにあってもすり抜けませんでした。

どの程度の隙間からすり抜けるようになるのか気になる点ではありますが、
まずは、接続された面であれば、当たり判定を「プリム」にしていても
問題ないということは確実ということが分かり、安心しました。

たまーにすり抜けるのは、
たぶん、幽霊の仕業か本当に実像的にスキマがあるのでしょう。きっと……


一応、これで2日間にわたる記事は終わりです。
ですが!まだまだ12月が始まったばかりです!

ではみなさま!
セカンドライフ技術系 Advent Calendar 2016」を楽しんでいきましょう!

.
..

.
..

.
..

オマケ

私は、Advent Calendar以外にも普段気が向いたときに、セカンドライフ系の記事をかいております。
その中で、私としてヨイデキな記事をいくつか紹介します。(宣伝デス)

2016年4月21日 セカンドライフのスカイボックスに太陽の光を!
シンプルながらも、知っておくと良いテクニック!

2014年12月29日 セカンドライフの関数逆引きメモ
セカンドライフにはどういった機能があるのか、逆引きをみて一通り覚えておこう!

2014年12月12日 セカンドライフでアニメーション
セカンドライフでアニメーションを作る方法!

Written by なたで

2016年12月2日 at 00:00

カテゴリー: 3D, memo, program

Tagged with , ,

セカンドライフのオブジェクトとの衝突音

leave a comment »


みなさん。お久しぶりです。

ついに今年も「Advent Calendar」の季節がやってきました!

イエーイ!

このようなイベントを開催していただいて、sabro様ありがとうございます!

私は去年から初参戦しているのですが、今年はナント1日目を予約してしまいました。
こんな私が1日目をとってしまってよかったのだろうか……といろいろありますが、
気にせずドンドン始めていきたいと思います!


衝突音

今回のテーマは、音関係の話、具体的には衝突音の話をしたいと思います。

みなさん、衝突音って気にしたことありますか……?

……

あんまり、気にしていない人多いかも。
でも、私はたまーに気になったりします。

例えばですが、プリムで階段を作ったとします。
kaidan

 

こういう階段にダッシュして登ってみましょう!

ゴツッ!

ほら!衝突音しましたよね。

なんとなくどんくさい感じがします。
他にも、柔らかなそうなソファーとかぶつかった時も、
ゴツッ!とか出るのも何だか不自然な感じがします。

実は、このような衝突音、簡単に消せるんです! \テテーン!/

プリムを選択して、スクリプトを追加。

gui_script_1 → gui_script_2
スクリプトでは次のように llCollisionSound で衝突音の設定をします。

default
{
	state_entry()
	{
		llCollisionSound("", 0.0);
	}
}

さあ、これで保存して、同じように階段を上ってみましょう!
衝突音がないため、スムーズに駆け上っている感じがでましたね。


簡単に衝突音設定しよう!

スクリプト面倒という方もいらっしゃると思います。
無音にする方法でなければ、実は手軽に衝突音を変えられるってご存知でしょうか?

具体的には、「オブジェクトの特徴」の設定で、
実像のタイプを選択することができます。

gui_sound_1 → gui_sound_2

デフォルトは「木」となっているのですが、
他にも、いくつか選べて、全部で7種類を選ぶことができます。
それぞれの設定の音は、次のようになります。

  • 石   … 低いゴツ!
  • 金属  … カン!(グレーチングを踏んだような音)
  • ガラス … カン!(氷をアイスピックでたたいたような音)
  • 木   … ゴツ!
  • 肌   … コツ!
  • プラスチック … ドン!
  • ゴム  … コツ!(硬くゴムを机に落としたような音)

※音の印象は個人差があります。

音は用意するの面倒だけど、衝突音を変えたいという方は
ここでGUI上でポチポチ触って変更することをオススメします!


他の音はないの?

先ほどは、簡単に音を設定する方法説明しましたが、
コツ、ゴツ系のみで種類がすくない~(><)と思う方もいると思います。

ただ llCollisionSound を使うにしても、
音を用意するのって結構面倒ですよね。

というわけで、
少し早いですがクリスマスプレゼント!
自由に使える音素材を収録して用意しました!

許可なしで有料/無料問わず自由に使える音のUUIDを公開します。
衝突音以外に使用してもらってもかまいません。

下記のスクリプトを入れれば、衝突音が変わります。
「1.0」と書いてある部分を小さな値にすれば、衝突音の音量を小さくもできます。

床1 … ドン!(低く、重たい音)

default{state_entry(){llCollisionSound("56fa194a-b894-043a-c1fe-04ab0ead663e", 1.0);}}

床2 … ドン!(普通の音1)

default{state_entry(){llCollisionSound("aab46478-b006-6fdb-1c2c-ac04dfa4c1ca", 1.0);}}

床3 … ドン!(普通の音2)

default{state_entry(){llCollisionSound("52dd9553-5c75-6fc8-5573-32df7b56d14d", 1.0);}}

床4 … ドン!(軽く、すこし硬い音)

default{state_entry(){llCollisionSound("88c0cae6-3c0d-6b6c-edee-85a3f1237828", 1.0);}}

アルミホイル … グシャ!

default{state_entry(){llCollisionSound("801f106b-d391-0214-cdd1-709b072b46aa", 1.0);}}

ビニール … グシャ!

default{state_entry(){llCollisionSound("e40d1ca7-d37f-1332-2513-37a8e209dbc6", 1.0);}}

金属音1 … コンッ!(すこし鈍い音)

default{state_entry(){llCollisionSound("b55200e1-b1bc-5302-ada4-ed559671225c", 1.0);}}

金属音2 … カッ!(軽い音)

default{state_entry(){llCollisionSound("739756e2-a4e1-7ab0-879e-118c458e545d", 1.0);}}

布1 … 軽く触れる音

default{state_entry(){llCollisionSound("a7451a80-e9f5-315a-92b2-f96253578077", 1.0);}}

布2 … バサ!

default{state_entry(){llCollisionSound("d64718b2-5923-ff14-c6d5-0ed9dda9d7c4", 1.0);}}

液体1 … ピトッ!

default{state_entry(){llCollisionSound("3bb0d4fc-d738-d04c-22f1-cd35ede613a4", 1.0);}}

液体2 … ペトッ!

default{state_entry(){llCollisionSound("75374bdf-c059-d568-bfe1-093cef58a0fd", 1.0);}}

というわけで、音の話……主に衝突音の話を終わります!

ありがとうございました!

イベントがあると、色々な人に知識を共有出来たり、
自分もさらに調べていこうと啓発もできて、とてもいいものです!
このような場を設けていただき、改めてありがとうございました!

Written by なたで

2016年12月1日 at 00:00

カテゴリー: memo, program

Tagged with ,

セカンドライフのスクリプトのエラー処理について

leave a comment »


セカンドライフでは、スクリプトでエラーが起きるとどうなるかみなさん知っていますか。
何かエラーが出ること自体は知っていると思いますが、
そのエラーが発生後、どうなるのかはそんなに知らないと思います。

ためしに、次のようなスクリプトを実行してみましょう。

hoge() {
	integer x = 0;
	llOwnerSay("hoge_start");
	x = 1 / 0;
	llOwnerSay("hoge_end");
}

default {
	state_entry() {
		llSetTimerEvent(3.0);
	}
	
	touch_start(integer total_number) {
		llOwnerSay("touch_start");
		hoge();
		llOwnerSay("touch_end");
	}
	
	timer() {
		llOwnerSay("clock");
	}
}

このスクリプトは、3秒に1度、発言が行われて、
また、クリックすると0の割り算エラーを発生させるスクリプトです。

これでクリックするとどうなると思いますか。三択問題です。

A. hoge関数内でエラーが発生するので、例外で終了するため”hoge_end”は表示されずに、関数を抜ける
B. touch_startイベント関数内でエラーが発生するので、そこでtouch_startイベント関数が強制終了する
C. プログラム自体が強制終了し、タイマーイベントなど全てが止まる

さて、どうなるでしょう。

正解は・・・


 

 

テテテテテテテ

 

 

ジャン!

Cでした!

なんと、すべて止まってしまうのです。
無限ループもタイマーもすべて止まってしまうので、手動リセットさせるしかありません。
なお、エラーの種類は「LSL Errors」でまとまっていますので、一度見ておくといいかもしれません。
注意点として、メモリリークの「Script run-time error: Stack-Heap Collision」なのですが、
これも発生してしまうと、プログラムが止まってしまいます。
そこで、止まるよりはみずから llGetFreeMemory で定期チェックをして、
llResetScript でリセットさせたほうが安全です。


オマケ

次のスクリプトはエラーが発生するでしょうか……

default {
	state_entry() {
		llOwnerSay((string)((integer)"ABC"));
		llOwnerSay((string)((integer)"123ABC456"));
		llOwnerSay((string)((vector)"ABC"));
		llOwnerSay((string)((vector)"<1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0>"));
	}
}


正解はこんな感じでエラーは発生しません。
文字列の左からデータとして認識できる範囲で変換してくれるようですね。

Object: 0
Object: 123
Object: <0.00000, 0.00000, 0.00000>
Object: <1.00000, 2.00000, 3.00000>

Written by なたで

2016年5月8日 at 21:06

カテゴリー: memo, program

Tagged with ,

セカンドライフのスカイボックスに太陽の光を!

leave a comment »


スカイボックスをご存知でしょうか。

セカンドライフでは地面の上に家をたてる場合と、
4000mまでの上空の間に家を浮かばせるスカイボックスがあります。

スカイボックスの最大のメリットとしては、
周りの情景に気にせず好きなものを建てられるということです。

通常、地面の上に建てる場合、お隣さんの家に気を使ったりして大きな建物を建てられなかったり、
あるいは雪の土地の上であれば、冬っぽい家を建てないと気分がでないなど
様々な制約があるのですが、スカイボックスは、自分で地面から作成して、
周りの風景まで画像を張ったりすることで楽しむこともできます。
中から外へのボックスが透過にし、解放感も味わうこともできます。

しかし1つ問題があります。
それは太陽の光をあびれないということ……。

スカイボックスは、プライバシーのために箱で包むことがよくあるのですが、
解放感を出すため内部から外へは透明にすることがよくあります。
一見、外の空が見えるので、解放感はあるのですが、なぜだか内部が暗くなってしまいます。
それは、影のせいです。

例えばですが、次のように立方体を設置すると影がでます。
secshadow1

ここで、影側のほうから壁を透明にした場合でも、やはり影ができてしまうのです。
secshadow2
スカイボックス内で内部から外を透過して解放感を出しても、
外からの光は残念ながら中に入らないのです。

 

 

実はテクニックがありまして・・・

 

 

テテーン!
secshadow3

さて、どのようにして影を出さないようにしたのでしょうか。

実は、外側のテクスチャを微妙に透過させることで、
影を出さないようにしたのです。
調査した結果、12%の透過度にすると影がでなくなるようです。

少しは透けてしまうようになるのですが、
大体中は見えないですし、二重にすることでより見づらくすることもできます。

こんな感じに
secshadow4

以上、スカイボックスの中に太陽の光を!でした。

Written by なたで

2016年4月21日 at 00:00

カテゴリー: game, memo

Tagged with ,

セカンドライフのサードパーティビューア Black Dragon の紹介

leave a comment »


ビューア関係の前回の記事「セカンドライフのサードパーティビューア Catznip の紹介」では
土地の境界線表示機能付きのカラフルなミニマップ表示
自分のオブジェクト一覧表示機能
・容姿にフォルダ構造を持たせられる機能
など他のビューアにはない機能を持つ「Catznip Viewer」について紹介しました。

今回は、「Black Dragon」(ブラックドラゴン)を解説します。


Black Dragonはグラフィック設定がすごい!

Black Dragonの最大の特徴は、3DCGの表示機能設定が豊富な点です。
設定できる機能の一部を紹介します。
.

テクスチャのメモリの最大値の増加

ここのメモリの容量が大きいほど、テクスチャが汚くなりません。
しかし通常、テクスチャはグラボにグラフィックメモリが何GBもつんであっても、最大512MBしか設定できません。
Black Dragonでは、グラフィックメモリの最大値を8GBまで設定することができます。
.

FXAAのアンチエイリアスが有効

これを有効にすると、ジャギー(2D座標空間の標本化歪み)が目立たなくなります。
通常のビューアでは、アンチエイリアシングは FSAA 2x/4x/… などが利用できます。
このアルゴリズムは、SSAA(Super Sampling Anti-Aliasing, Full-Scene Anti-Aliasing)を使用しています。
SSAAは綺麗にはなるのですが、大きな画面として描写するため処理が重たくなります。
一方、ここでは使用できませんが、MSAAというものあり、
MSAAは、SSAAより軽いですが、テクスチャ自体にはアンチエイリアスをかけられない特徴があります。

今回、このビューアではFXAAが利用できます。
FXAAの最大の特徴は、3DCGを描写後に行うアンチエイリアスという点です。
品質は、SSAAより悪いですが、動作は非常に軽く、MSAAと違いテクスチャ自体のギザギザも抑えられます。
それぞれの特徴については、「4Gamerの記事」を参照。
.

影テクスチャを細かく設定できる

通常のビューアでは、影の設定を「太陽/月・プロジェクタ」という1つの設定しかありません。
しかし、Black Dragonでは影の設定を細かく設定できるため、綺麗に影を落としこむこともできます。
例えば影を落とすために、シャドウマッピング用にテクスチャを用意するのですが、
このテクスチャの解像度によって、影の品質がきまります。
Black Dragonでは、このテクスチャの縦と横の大きさを自由に設定できます。
あまりに大きいと、グラフィックメモリを使用するので注意が必要です。
また、解像度以外にも、このテクスチャから
何点のポイントを使用して平均化し影をつけるかの設定ができます。(ソフトシャドウ技術
この設定を行うことで、はっきりした影や、ぼやけた影の表現もできます
.

SSAOの詳細設定ができる。

物をリアルに表示させるために明暗をつける
アンビエントオクルージョンというテクニックがあります。
例えば、近くに物があれば、その物の近くにあるオブジェクトを暗くする機能です。
通常のビューアでは「アンビエントオクルージョン」という1つのチェックボックスですが、
Black Dragonでは、Screen Space Ambient Occlusionの設定項目で、詳細設定が可能です。
ここの設定は重たくなる原因なので、チェックを外すのもアリだと思います。
.

DoFの詳細設定ができる。

DoF(Depth of Field)という技術があります。
これは、ある距離より遠いものや近いものをぼかして、被写界深度を再現します。
通常のビューアでは「フィールの遠近感」という1つのチェックボックスですが、
Black Dragonでは、Depth of Fieldの設定項目で、詳細設定が可能です。
なお、この中で「Enable Alphas in Depth of Field calculations」という設定があるのですが、
これは透明度を持つオブジェクトに対してもDoFを正しく行うという機能です。
この機能は、結構重たい処理のためFPS稼ぎに外しておくのがオススメです。
.

モーションブラーを設定できる。

カメラをぶらすと、映像の軌跡を残るように、
3DCG表示でもこれを再現させます。
モーションブラーを使用すると、アバターをアニメーションをさせたときに
滑らかに表示されるように感じたり、躍動感がうまれます。
.

レンズフレア

太陽をみたときなどに、レンズフレアを表示させることができます。
ただ、太陽との間にオブジェクトで遮っている場合でも、
レンズフレアが見えてしまうことがあるので、この設定は一長一短。
.

トーンマッピング

3DCGのレンダリング後に、色に対してエフェクタを通すことができます。
色は、線形や非線形に変更したりできます。
具体的には、コントラストを上げたり、明るくしたり、赤みをおびさせたり、
好きな雰囲気にすることができます。


さて、使いたくなってきたところで、
インストール時によくやる設定について最後に紹介します。
.

キーボード操作の設定(Keybindings)の変更

スライド移動(Strafe)と回転(Rotate)とジャンプのキーボード操作が
デフォルトと異なるため、変更すると使いやすいです。

三人称視点時設定(Third Person Keybindings)と
一人称視点(マウスルック)時設定(First Person Keybindings)の
両方で設定をする必要があります。

Third Person Keybindings
・Move Forwards [W] -> [W]
・Move Backwards [S] -> [S]
Strafe Left [A] -> [A + [Shift]]
Strafe Right [D] -> [D + [Shift]]
Rotate Left [Q] -> [A]
Rotate Right [E] -> [D]
Jump [Space] -> [E]

First Person Keybindings
・Move Forwards [W] -> [W]
・Move Backwards [S] -> [S]
・Strafe Left [A] -> [A]
・Strafe Right [D] -> [D]
・Rotate Left [Q] -> [Q]
・Rotate Right [E] -> [E]
Jump [Space] -> [E]

.

カメラの設定(Camera)の変更

下記のチェックボックスを外しましょう。
「Enable ‘realistic’ Camera behavior in Mouselook」
この設定が有効になっていると、
マウスルック時のカメラが、アバターのアニメーションに
追従するようになると思います。酔う原因になります。
.
また、Smoothingを8.00->0.50にすると、
カメラが向いた方向へ瞬時に移動するので好みで設定。
.

グラフィックの変更

Black Dragonは、デレンダラ(非描写)機能があります。
Rendering Options -> Derender … と書いてある設定です。
この設定は、指定したファイルサイズより大きかったり、
表面の面積が大きかったりしたりすると表示しなくなります。
アバターやオブジェクトが表示されなくならないように、適切な値に調節するか、
あるいは全て 0 にして無効化するといいです。


グラボをつけている方や、
綺麗なスクリーンショットを撮りたい方は
一度 Black Dragon を試してみるといいかもしれません。

以上、紹介を終わります!


自分用の設定メモ

.

髪の毛などの影も綺麗に顔に落とす(太陽の光用)

☑Deferred Rendering
・Extended Shadow Distance 128 -> 16
・Closeup Shadowmap Size 2048 -> 4096
・Close Shadowmap Size 4096 -> 4096
※Shadowの設定で上と下があるのは、おそらく上が太陽の影、下がライトによる影設定です
※「Extended Shadow Distance」は小さくなるほど、影の精度があがりますが、遠い距離にある影がおちにくくなります。
.

全体をふんわりと明るくする

☑Windlight Options
・Warmth Red 1.00 -> 2.00
・Warmth Green 0.60 -> 2.00
・Warmth Blue 0.75 -> 2.00
☑Volumetric Lighting
・中はデフォルト
☑Post Processing Effects -> Lens Flare Effect
・中はデフォルト
☑Tone Mapping
☑Custom
Linear Strength 0.44 -> 0.25
Toe Strength 0.24 -> 0.87
Toe Numerator 0.02 -> 0.87
Toe Denominator 0.18 -> 0.66
Linear White 25.00 -> 13.22
Exposure Bias 3.00 -> 0.91
☑Color Correction Linear
・中はデフォルト

Written by なたで

2016年4月17日 at 16:18

カテゴリー: game, memo, review

Tagged with

セカンドライフで綺麗な水面を作る

leave a comment »


こんにちは!

セカンドライフ技術系 Advent Calendar 2015」の12月13日(日)担当の なたで です!

木曜日の記事(セカンドライフでジャンプ台を作る)に引き続いて…
今日は、セカンドライフで綺麗な水面を作ります。
けっこう長めなので、理由はともかく作りたいという方は、読み飛ばして、
途中に張ってある画像を保存して、テクスチャに使用していってください。


綺麗な波をつくるには・・・?

綺麗な水面を作るために、水の特徴を考えます。
水といえば波があるということです。

次のように2つの波の衝突を考えます

nami001
1つは、右から左へ流れる大きな波。

nami002
もう1つは、左から右へ流れる小さい波。

nami003
この2つが、衝突しても波と波とが重なり合って、消滅するということはありません。

つまり、波を作るためには、何面もの波を重ねると本物のように作ることができます。

よって、次のように作ると良いことが分かります。
「複数の面を重ねるようにおいて、模様に波(水面)のテクスチャを設定して、それぞれ別の方向へ動かす」

ここでさらに、波のテクスチャについて考えていきます。
単純に、波の模様のテクスチャだけを用意するだけでいいのでしょうか。

一度、波を実際に目で見た時を考えてみます。

nami004
波は、うねうねした形をもっており、うねうねに合わせた法線ベクトルがあります。
光は、波に当たっときに、法線を境に反射をします。
そして、反射した光が目に入った時に、白く輝いて見えることが分かります。

これらから、水面のテクスチャは、法線マップを作成する必要があるのです。
波の模様テクスチャから、法線マップを作ります。


特徴をまとめます

・複数の波を、合成する必要がある。
・それぞれの波を別方向でスクロールさせる。
・法線マップを使用して、リアルな反射を持たせる。


波のテクスチャを用意しよう

波のテクスチャは、シームレスなパーリンノイズが適切です。
シームレスというのは、上下左右につなぎ目のないという意味です。

具体的にシームレスなパーリンノイズを作る方法ですが、
実は検索してもなかなか見つかりませんでした。
一応、下記のようなツールを発見できたものの、登録が必要であるため、ダウンロードをしていません。
Procedural seamless noise texture generator

また、CLIP STUDIOというツールにも、パーリンノイズを作成する機能があるのですが、
こちらは、シームレスなパーリンノイズを作成できません。

そこで、シームレスなパーリンノイズを作成する簡易ツールを作りました。
nami_iconダウンロード

Javaの環境があれば、MacでもWindowsでも動きます。
ここでは、Windowsの環境での説明をしていきます。

【1】
「すべてのプログラム」→「アクセサリ」→「コマンドプロンプト」を実行します。

【2】
次のように、jarファイルがおいてあるディレクトリへ移動します。

cd C:\Users\xxx\Desktop\yyy

【3】
次のように実行してください。(-help と書けば使い方がでます。)

java -jar noise.jar -width 256 -height 256 -persistence 0.6 -lacnarity 2.5 -seed 123456

【4】
noise
これがパーリンノイズ(テクスチャA)です。

次に、このパーリンノイズを「高さ(ハイトマップ)」と見立てて
ノーマルマップを作成していきます。

ノーマルマップの作成ツールは、ハイトマップから自動でノーマルマップを作成できる
NormalMap-Online」を使用させていただきます。

【5】
normalmake
左側の箇所に画像ファイルのアイコンをドラッグ&ドロップして、
画像を読み込ませて、真ん中上部のスライダーで設定をしていきます。

【6】
NormalMap
Downloadを押せば、保存ができます。
これで、ノーマルマップ(テクスチャB)の完成です。

【7】
さらに、色用のテクスチャも用意します。
具体的には、透明度だけを持つ白色テクスチャです。
透明度には、パーリンノイズのテクスチャを使用します。

ovicon64
今回は、高機能な画像補正専用ツールの「オレンジビューア」を使用します。
このツールも、Javaをインストールする必要があります。

nami100
今回は、256×256のパーリンノイズなので、256×256の白色の画像を作成して
ビューアで読み込みを行います。

nami101
「色」→「アルファチャンネル」→「ファイルから読み込む…」で
さきほどの256×256のパーリンノイズを指定します。

nami102
これで、パーリンノイズのアルファチャンネルを持つ白い画像を作成できました。
「ファイルを名前を付けて保存」で、「xxx.png」と拡張子を「png」にして保存してください。
このソフトは、保存するときの拡張子によって自動で、その形式で保存されます。
pngにする理由は、透明度情報を持たせるためです。

noise2

完成した画像がこちらになります。(テクスチャC


テクスチャを設定しよう

立方体を作成して、薄くします。
これを水面にします。

nami200
色は水のような青色にします。

nami201
テクスチャの拡散反射の設定は「テクスチャC」を使います。

nami202
バンプマップの設定は、「テクスチャB」を使います。

nami203
鏡面反射の色成分の設定は「テクスチャA」を使います。

水平スケール、垂直スケールは、面の面積に合わせて、適度な値にしておきましょう。

nami301
この時点で、だいぶ水面のようになります。

nami302
あとは、2枚重ねておきましょう。
また、2枚目に関しては、あとでスクロールの関係上、180度回転させておきましょう。


スクロールさせる

波テクスチャを設定した重ねたオブジェクトの2つ、
それぞれにスクリプトを入れて、テクスチャをスクロールさせます。

スクロールは、かなり遅いほうがそれっぽいです。
スクリプトはこんな感じです。

start() {
	integer	mode	=	ANIM_ON | SMOOTH | LOOP;
	integer	face	=	ALL_SIDES;
	integer	sizex	=	0;
	integer	sizey	=	0;
	float	start	=	0.0;
	float	length	=	0.0;
	float	rate	=	0.005;
	llSetTextureAnim(0, face, 0, 0, 0.0, 0.0, 0.0);
	llSetTextureAnim(mode, face, sizex, sizey, start, length, rate);
}
default{state_entry(){start();}}

これを入れ込んで、実行させるとスクロールが始まります!

これで綺麗な水面の完成です!

テテーン!

これで私の「セカンドライフ技術系 Advent Calendar 2015」の記事は終わりです。
このような発表のきっかけを用意していただきました、sabroさんありがとうございました。

今回の記事を通して、3Dのテクスチャの作成や設定のテクニックに興味を持っていただければ幸いです・・・!

Written by なたで

2015年12月13日 at 00:00