読者です 読者をやめる 読者になる 読者になる

そら飛ぶタピオカ

映画だったり技術系だったりいろんなモノのごった煮メモ的なソレ

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

球表面・球内に一様分布する点の生成方法を調べたのでメモ。
下記のページを参考にしました。
球面上にランダムで置いたプロットをなるべく分散させたい - 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();
}