Ameba Ownd

アプリで簡単、無料ホームページ作成

ぐっちーの駄弁り部屋

[Unity]球面にオブジェクトを生成する話

2020.05.15 08:57

皆さんこんにちは。ぐっちーです。

最近MacbookAirを購入したのでIOS開発をしてみたいなーということで前々からやりたかったARKit(ARFoundation)を使ってARシューティングを作ろうとしました。

そんな中で敵を球面状に出現させたいなーと思ったので今日はそれについて話して行こうかなと思います。


球面状に生成する

今回は半径が1の球について考えていきます。

球の方程式

x^2 + y^2 + z^2 = r^2

を満たすようなxyzだけ摘出していく方法があります。

ある程度の誤差は許容できるようにrの範囲を0.9≦r≦1.1としています。

まあこれでもいいのですが球面に限定する場合は極座標にすると条件分岐を設けなくていいので適しているのかと思います。

極座標で表現する場合は以下のような式になります。

x = cos(θ) + cos(φ)

y = sin(θ)

z = cos(θ) + sin(φ)

ここで登場するθ(シータ)とφ(ファイ)はそれぞれ経度と緯度に当たるものです。

図で表現すると以下のようになります。

ネットで調べるとyとzが反対だったり、yの式がSinではなくCosで表されたものが出てくると思いますがそれぞれyを上向きで考えているのとθをxz平面からの角度としているため今回は上に示した式を使っていきます。

これら2通りの方法で一応球面状に配置はできるのですが実際にやってみると一様に分布している訳ではなく北極や南極のあるあたりと赤道付近では前者の方が密集しています。

少々みづらいですが上に書いたコードで点を出現させた結果で、カメラは球を上から見下ろす形になっています。

黄色で囲った部分が二つの極に当たる部分で点が他に比べ密集していますね。

なぜこうなってしまうのか説明していきます。


偏る理由

まずは円について考えます。



この円の中には10個の点が打たれていて大体半径の半分で内側と外側で分かれています。

この図から内側の方が密集しているのがわかります。点が円の中に打たれる確率は円全体で一様なので内側と外側では点の数は同じになります。しかし面積は外側が内側の3倍の大きさがあるので密度が内側の方が高くなります。

球についても同様です。

y軸とxz平面からそれぞれθだけ開いた分の帯の面積を考えます。球の表面について点の打たれる確率は一様ですが上の画像では上の網掛け部分の方が下の網掛け部分よりも小さいのでより点が密集することになります。

このように面積の違いによって一様にならないのでこれを補正していきます。


確率を補正する

先ほどと同様に円について考えてみましょう。

円では円の内側の方が密集していたことから半径が関係していると考えられます。面積はπr^2で求められます。ここで半径1の円との面積比pを考えると

p = πr^2/π

   = r^2

です。つまり半径が0.5だとその比は0.25となるので「25%の確率で半径が0.5以下にある」ということです。ここでpは比率を示しているので範囲は0−1です。ここで逆にpをこの範囲で定めてあげることでrが決まります。上の式をrについて解くと

r = √p (∵ r>0)

となるのでpが0.5だとrは大体0.7くらいになるので半分の確率で半径が0.7以上もう半分の確率0.7以下になり面積比に比例して点を円内に分布させることができます。

球についても面積比を用いて考えます。

半径は1で固定しており、φは変化に伴って面積が変わっているのでここではθを面積比を用いて表現します。そのためにまずは球面の一部の面積を求めることから始めましょう。

下の画像の網掛け部分の面積を求めます。

下の図の赤い部分を限りなく小さくすると直線だと考えることができるので網掛け部分は長方形だと考えることができるので、(緑の部分)x(赤の部分)で求めることができます。

緑の部分は断面の円周になります。断面の半径はcos(θ)なので緑の部分は2π*cos(θ)です。

赤の部分はdθとおくとすると帯の面積は帯の下端から上端までの範囲でθについて積分すれば求めることができます。

まずは球面全体の面積を考えましょう。θの範囲は、-π/2からπ/2ですのでその範囲で(緑の部分)x(赤の部分)の式を積分すると、

2π*sin(π/2) - 2π*sin(-π/2)

= 2π - (-2π)

= 4π

と半径1の球の表面積になりましたね(球の表面積の公式S = 4πr^2)

ではxz平面からθ開いた部分までの帯の面積は-π/2からθで積分すればいいので、

2π*sin(θ) - 2π*sin(-π/2)

= 2π*sin(θ) - 2π

= 2π(sin(θ) - 1)

となります。

面積が求まったので面積比を出します。

p = 2π(sin(θ) - 1)/4π

   = 0.5 * (sin(θ) - 1)

最後にθを面積比pの関数として表すためにθについて解きます。

2p = sin(θ) - 1

sin(θ) = 2p - 1

θ = arcsin(2p - 1)

θをpの関数として表現することができたので面積に比例して点を打つことができるので球面状に一様に分布するようになります。

最終的には以下のようなコードになると思います。


終わりに

いかがでしたでしょうか。割と数学チックな話が多くなったと思いますが少しの手間で一様に分布させることができるんですねって思いました。

夜中に記事を書いてるとこのまとめの部分に何書いていいかわかんなくなりますねwww

てなわけでこの記事はここまでにしたいと思います。ではでは(`・ω・´)


参考サイト