DirectXとOpenGLの回転行列、回転軸、回転方向

投稿日: 更新日:


はじめに

こんにちは!
ハンドスピナーを最近買ったなたでです!

今日も勉強会をしましょう。
本日は、DirectXとOpenGLの軸の違いに焦点をあてて
回転行列をおさらいしてみましょう。


掛ける順序

3D環境といえば、DirectX、OpenGLです。
今回、回転用の標準関数を調査して比較していきたいと思います。

比較する前に基本的な掛け算のおさらいをしましょう。


上記を確認してください。
これは、DirectX と OpenGL の計算方法の違いを表しています。

DirectX では、横行列に対して正方行列を掛け算して、横行列を求めます。
OpenGL では、正方行列に縦行列を掛け算して、縦行列を求めます。


標準関数の中身

3D環境といえば、DirectX、OpenGLです。
それぞれ、回転用の標準関数が用意されています。
.

DirectX

DirectXでは回転させる命令として、
D3DXMatrixRotation(D3DXMatrixRotationX/D3DXMatrixRotationY/D3DXMatrixRotationZ) が用意されています。

トランスフォーム (Direct3D 9) より
.

OpenGL

OpenGLでは、
glRotate(glRotated/glRotatef) が用意されています。
OpenGL Programming Guide に、標準関数の中の回転行列が書いてあります。
一見、DirectXの回転行列の転置行列となっていますが、
OpenGLでは、「掛ける順序」のように異なるため、この違いは吸収されます。
混乱しないように、DirectXのように掛け算した場合を考えたいと思います。

.

回転行列の特徴

X、Y、Zを順番に見ていくと、
対角成分が1となっている箇所があります。
回転行列は、ベクトルに掛け算することで回転させることができますが、
1となっている部分は、以前の位置と変わらないことを示します。

例えば、ベクトルa=[x,y,z]に行列rotateYをかけた場合、
a[y] の位置は1を掛けるだけであるため、変わらないわけです。
たしかに、Y軸を中心に回転させるので、yの値が変わってしまうのはおかしいですよね。

ところで、Wikipediaを見ると2次元の回転行列が書いてあります。

Wikipediaの2次元の回転行列は、θはどちらの方向に回転するのでしょうか
Wikipediaでは、下記のように書いてあります。
> ユークリッド空間の2次元空間では、原点中心の θ 回転(反時計回りを正とする)の回転行列」

この記述はすこし正確ではありませんが(後述)、
教科書でよくある次のような座標系での反時計回りを指します。

.

転置行列の特徴

Wikipediaの2次元の回転行列と、
D3DXMatrixRotationやglRotateの回転行列は似ています。

具体的には、転置行列のような関係になっていることに気が付くと思います。

ところで、回転行列は、直行行列です。
直行行列の特徴は、転置させると逆行列となります。

ここから、単純に考えるとWikipediaの2次元の回転行列は、反時計回りなので、
その転置行列に似た、D3DXMatrixRotationやglRotateは、時計回り?といった疑問がわきます。
.

回転方向

D3DXMatrixRotationやglRotateは、
実際にどのような回転方向になっているでしょうか。
同様の行列のため、同じ回転方向なのでは?
と一見思えるかもしれません……が

実はこれを考えるためには、
軸の方向の定義を考える必要があります。

例えば、

上記のような場合と

上記のような場合の2種類の座標系があった場合、
X軸の正方向で時計回りした場合、
上の図と下の図とで、体感的に回転方向が変わってくるはずです。

つまり、X軸での時計回りについては、
Y軸とZ軸の正の方向を定義しないといけません。

そして、先ほど触れた Wikipediaに記述されている
「 ユークリッド空間の2次元空間では、原点中心の θ 回転(反時計回りを正とする)」
という記述は、右がX、上がYと定義しておかないと、
「反時計回りを正とする」とは言えないのです……。


軸の定義

DirectXが左手座標系
OpenGLは右手座標系
です。
座標系 (Direct3D 9) より
.

DirectXの座標系

座標系 (Direct3D 9)を見ると、
次のような座標系となることが分かります。

上の図の座標系で各軸の方向を考慮すると、
DirectX の回転は次のようになります。

D3DXMatrixRotationX
回転方向のプラスは、原点からX軸の正の方向を向いたときに、反時計回りをさす。

D3DXMatrixRotationY
回転方向のプラスは、原点からY軸の正の方向を向いたときに、反時計回りをさす。

D3DXMatrixRotationZ
回転方向のプラスは、原点からZ軸の正の方向を向いたときに、反時計回りをさす。

図に表すとこのようになります。

先ほどの数式を見る限り、時計回りに回転すると思いきや、
軸を考慮すると反時計回りをしめしていることが分かります。
.

OpenGLの座標系

OpenGL座標系は、右手座標系です。

これも同様に、
OpenGL の glRotate をあてはめると、

glRotate X
回転方向のプラスは、原点からX軸の正の方向を向いたときに、時計回りをさす。

glRotate Y
回転方向のプラスは、原点からY軸の正の方向を向いたときに、時計回りをさす。

glRotate Z
回転方向のプラスは、原点からZ軸の正の方向を向いたときに、時計回りをさす。

図に表すとこのようになります。
右ねじの法則のようですね。

座標系の違いと回転行列

回転行列を見ると、DirectXとOpenGLは同じにみえました。
しかし、座標系が異なっており、
Z軸のみが反転している関係があることから、
DirectXとOpenGLとで回転の向きが逆向きになっています。

ただし!
反時計回りとは言っても
Z軸のみに関しては、そもそも方向自体が逆にになっているため、
見た目上の動きは、DirectXとOpenGLとで同様の回転方向に見えます。


おわりに

今回、DirectXとOpenGLの標準関数の回転行列の式の解説、
そして、DirectXとOpenGLの座標系を比べて、
正方向が時計回りか反時計回りかを調べました。

DirectXとOpenGLは、Z軸の方向が逆という話はよくききますが、
見た目上の回転方向にも違いがあるという点はそんなにききません。

両方を考える場合は、これらを注意する必要があります。

広告

セカンドライフのテクスチャの秘密1

投稿日: 更新日:


はじめに

こんにちは!

久しぶりのブログです。

今日は、セカンドライフのテクスチャ、ノーマルマップ  の秘密について話します。
続かないかもしれませんが、とりあえず第1弾です。


ノーマルマップの色成分の基礎

ノーマルマップといえば、平面の凹凸を現した画像です。
通常グレースケールで高さを表した画像から、このノーマルマップを作成します。
おそらく、少し詳しzい方はこの辺までは、ご存知かと思います。

しかし、具体的に
赤色成分は、何をセカンドライフ上の何を示すのか、
青色成分は……?
といったことまで知っている方はそんなにいないのでは?

ということで、この色成分が何を表しているかの解説となります。

ノーマルマップの英語の解説を確認してみましょう。
これを見ると、次のような情報が分かります。
赤成分R = 方向成分X
緑成分G = 方向成分Y
青成分B = 方向成分Z

ただし、方向とは言ってもX+がR+に
対応しているのかといったことはテキストには書いてありません。
なぜ、それが重要かといいますと、ソフトによって色成分との対応が異なるためです。

>・ZBrush:(-X,+Y,+Z)
>・CrazyBump:(+X,+Y,+Z)
>・Blender 2.4x:(-X,-Y,+Z)
>・Blender 2.57以降:(+X,+Y,+Z)
Sandy Virtual City様のブログから引用
※XYZ軸がどちらに対応しているかも重要となるため、ブログ内の軸の解説も参照しましょう。
ブログでは、X軸のプラスをUV座標系のU方向、Y軸のプラスをUV座標系のV方向、Z軸のプラスを凸方向としています。

では、セカンドライフを調べてみましょう。
公式ページにあるノーマルマップの画像から、
凸に対して、画素の色が次のようになっています。
上が RGB(126,178,212)
右が RGB(169,124,210)
下が RGB(109, 75,206)
左が RGB( 69,124,212)

つまり、セカンドライフ(+X, -Y, +Z)
となることが分かります。

これは、NormalMap-Onlineでは、
Invert の R にチェックが入った場合と同様になります。

これらをおさらないすると、たとえば下記のようなバンプマップ/ハイトマップを用意したとすると、

次のような画像がセカンドライフ上でのノーマルマップとなります。

本日は、ここまで!

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

投稿日: 更新日:


次のような自分の行動の情報を取得してみます。
・自分が喋った回数
・人から聞いた回数(アバターが話したものに限定)
・空を飛んでいる時間(ホバーは含めない)
・海の中を泳いでいる時間(ホバーは含めない)
・歩いている時間
・走った時間
・ジャンプ中の時間
・テレポートした回数
・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 ログイン、装備
		}
	}
}

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

投稿日: 更新日:


こんにちは!

昨日から引き続き、「セカンドライフ技術系 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日 セカンドライフでアニメーション
セカンドライフでアニメーションを作る方法!

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

投稿日: 更新日:


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

ついに今年も「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);}}

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

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

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

GeForce GTX の発色を鮮やかにする方法

投稿日: 更新日:


えNVIDEA GeForce GTX のディスプレイ設定をいじって、鮮やかな画面にする方法を紹介します。

NVIDEA コントロールパネルを開いて、ディスプレイのデスクトップカラー設定の調整を開きます。
nvidea_control_panel_1

ここで、コントラストと、デジタルバイブランスという設定をいじります。

コントラストは明暗の差。デジタルバイブランスは彩度の設定です。

nvidea_control_panel_2
nvidea_control_panel_3

コントラストとデジタルバイブランスを両方とも+50%→+55%にするだけで、かなり鮮やかな画面になります。

ゲームするときは、あえてすこしいじって遊ぶと、より世界が鮮明になり
また違った楽しみ方ができるとおもいます!

Raspberry Pi はじめました (第5回) – DDNS編

投稿日: 更新日:


Raspberry Pi はじめました (第4回) – はじめから の続きです。


8. フォルダでアクセスできるようにしよう
8-1. やっぱりWindowsPC上からアクセスしてファイル編集したい
8-2. そこで、これも以前にあったように、sambaをインストール

sudo apt-get install samba

8-3. 設定ファイルを編集

sudo nano /etc/samba/smb.conf

8-4. 次を追加

[pi]
path = /home/pi
read only = No
guest ok = No
force user = pi
browseable = Yes

8-5. 【任意】パスワードを設定

sudo pdbedit -a pi

8-5. サービスの再起動

sudo service samba-ad-dc restart

●●●
9. 外からアクセスできるようにするために、ドメインを作ろう
9-1. ドメインは無料で作ることもできるのですが、安いので買ったほうがいいです。
9-2. ダイナミックDNSが使える会社のがいいです。自サーバーのIPアドレスが固定じゃなくてもよいためです。
9-3. というわけで、VALUE-DOMAIN を使用しましょう。
9-4. 「お名前.com」は、「お名前.comでドメイン取得時にWhois情報公開代行を忘れると大損」なので注意
9-5. 上位レジストラは、eNomだと「ネームサーバーが不正に変更される問題について」らしいので、GMOが安心
9-6. VALUE-DOMAINで登録を終えたら「ダイナミックDNSの設定」で、パスワードを設定する。
9-8. ダイナミックDNS機能を「無効」から「有効」にする
9-9. 「ネームサーバーの設定」→「当サービス内のネームサーバー(ns1~5.value-domain.com)を利用する」
9-10. 「DNS情報/URL転送の設定」で、「a * 1.1.1.1」と設定。アドレスは自分のアドレスとする。
9-11. これで、ダイナミックDNSが使用可能となります。
9-12. 次のURLを入力すると、0と返り、遠隔でIPアドレスを再設定できます。

https://dyn.value-domain.com/cgi-bin/dyn.fcg?d=hoge.com&p=password&h=*

●●●
10. crontabを使ってみよう
10-1. 「\\RASPBERRYPI\pi\CronJobs」というフォルダを作る
10-2. 「startup.sh」というファイルを作る
10-3. 中身は下記のようにする

#!/bin/sh
# ここにスクリプトを書いていく
exit 0

10-4. 実行権限をつける

chmod +x /home/pi/CronJobs/startup.sh

10-5. ルート権限用のcrontabを開く

sudo crontab -e

10-6. 次を追加する

@reboot su - pi -c "/home/pi/CronJobs/startup.sh"

10-7. これで起動時に実行されるスクリプトを登録できました。

sudo timedatectl set-timezone Asia/Tokyo

とかを登録しておくと、日本の日本標準時間になるのでお勧めです。

10-8. 他にも、同様の操作でDDNSの設定を定期的に実行とかも作れます。
10-9. 例えば、 VALUE-DOMAINの場合は、次のような「ddns.sh」を作成して実行権限つけて

#!/bin/sh

domain="hoge.com" 
pass="pass"
path="https://dyn.value-domain.com/cgi-bin/dyn.fcg?h=*&d=${domain}&p=${pass}"

curl -s ${path} >> /dev/null

exit 0

10-10. あとは、これをcrontabに追加すればでよいはず。未確認ですが。

* */6 * * * su - pi -c  "/home/pi/CronJobs/ddns.sh"

●●●
今日はここまで!
とりあえず、これで外から自宅サーバーへアクセスできるようになります。