そら飛ぶタピオカ

メディアアートネタとかを書きます

球表面・球内に一様分布する点をいっぱい生成する

球表面・球内に一様分布する点の生成方法を調べたのでメモ。
下記のページを参考にしました。
球面上にランダムで置いたプロットをなるべく分散させたい - Qiita
http://apollon.issp.u-tokyo.ac.jp/~watanabe/pdf/prob.pdf

球表面に一様分布する点

単位球表面に一様に分布する点の座標  (x, y, z) は、2つの一様乱数

{
\begin{eqnarray}
\left\{ \begin{array}{ll} 
  z & (-1 \leq r \leq 1) \\
  \phi & (0 \leq \phi < 2 \pi) 
\end{array}\right.
\end{eqnarray}
}

を用いて、

{
\begin{eqnarray}
\left\{ \begin{array}{ll}
  x = \sqrt{1-z^2} \cos \phi \\
  y = \sqrt{1-z^2} \sin \phi \\
  z = z
\end{array}\right.
\end{eqnarray}
}

で求められます。
任意の半径の球を考える場合は、各座標に半径をかければ求められます。

球内に一様分布する点

単位球内に一様に分布する点の座標  (x, y, z) は、3つの一様乱数

{
\begin{eqnarray}
\left\{ \begin{array}{ll} 
  z & (-1 \leq r \leq 1) \\
  \phi & (0 \leq \phi < 2 \pi) \\
  r & (0 \leq r \leq 1)
\end{array}\right.
\end{eqnarray}
}

を用いて、

{
\begin{eqnarray}
\left\{ \begin{array}{ll}
  x = r^{1/3} \sqrt{1-z^2} \cos \phi \\
  y = r^{1/3} \sqrt{1-z^2} \sin \phi \\
  z = r^{1/3} z
\end{array}\right.
\end{eqnarray}
}

で求められます。
球表面と同様に任意の半径の球を考える場合は、各座標に半径をかければ求められます。

実際に生成してみる

openFrameworksで実際に生成してみるとこんな感じになります。
緑の点が球表面、赤の点が球内に一様分布する点になります。
ちゃんと一様に分布してるっぽい。
f:id:tapioca24:20170219010351g:plain

ofApp.h

#pragma once
#include "ofMain.h"

class ofApp : public ofBaseApp{
public:
  void setup();
  void draw();
  
  ofEasyCam cam;
  ofMesh mesh;
  int num; // 頂点数
  float radius; // 半径
};

ofApp.cpp

#include "ofApp.h"

//--------------------------------------------------------------
void ofApp::setup(){
  ofBackground(0x32);
  ofEnableDepthTest();
  cam.setDistance(480);

  num = 2000;
  radius = 200;
  
  // 球表面に一様な分布する点を生成
  for (int i = 0; i < num; i++) {
    float z = ofRandom(-1.0, 1.0);
    float phi = ofDegToRad(ofRandom(360));
    float x = sqrt(1 - z * z) * cos(phi);
    float y = sqrt(1 - z * z) * sin(phi);
    mesh.addVertex(radius * ofVec3f(x, y, z));
    mesh.addColor(ofColor::fromHsb(100, 200, 250));
  }
  
  // 球内に一様に分布する点を生成
  for (int i = 0; i < num; i++) {
    float r = ofRandom(1.0);
    float z = ofRandom(-1.0, 1.0);
    float phi = ofDegToRad(ofRandom(360));
    float x = cbrt(r) * sqrt(1 - z * z) * cos(phi);
    float y = cbrt(r) * sqrt(1 - z * z) * sin(phi);
    z = cbrt(r) * z;
    mesh.addVertex(radius * ofVec3f(x, y, z));
    mesh.addColor(ofColor::fromHsb(240, 200, 250));
  }
}

//--------------------------------------------------------------
void ofApp::draw(){
  cam.begin();
  ofRotate(ofGetElapsedTimef()*10, 1, 1, 0);
  glPointSize(4.0);
  mesh.drawVertices();
  cam.end();
}