Create circle with fuzzy edge made of individual random particles

Hi everybody,

I want to create a circle that is made up of individual particles which are randomly positioned on the edge.
Most of the particles are close to the edge of the circle but some are spread more far away.

Just like this image.

I’ve created a CircleGeometry with made up of individual points using the PointsMaterial but they’re all located on the circles segments obviously. I don’t know how to position them rather roughly on the circle.

Also I’m not sure if I should rather use the SphereGeometry than the Circle but I guess the Circle is more correct since I’m only interested in the outer edge and I dont want points everywhere on a sphere.

It would be nice if someone could give some recommendations on how to archive a result like on the image.

Thanks a lot,
Alex

I took the example @looeee How move all points to sphere - #2 by looeee ( see also discourse.threejs.hofk.de ) and changed it a bit. It’s not quite what you want yet. You still have to add a random distribution function for the distance from the center of the circle.


<!DOCTYPE html>
<!-- https://discourse.threejs.org/t/how-move-all-points-to-sphere/1836/2 -->
<!-- https://codepen.io/looeee/pen/LQLQRd -->
<head>
  <title> Points to Sphere </title>
  <meta charset="utf-8" />
  <style>
	body {
	margin: 0;
	overflow: hidden;
	}
  </style>
</head>

<body> </body>
<script src="../js/three.min.89.js"></script>
<script src="../js/OrbitControls.js"></script>

<script>

// @author  looeee  --- changed by hofk

let camera, renderer, scene;

function init() {

  renderer = new THREE.WebGLRenderer();
  renderer.setSize( window.innerWidth, window.innerHeight );
  
  renderer.setPixelRatio( window.devicePixelRatio );

  document.body.appendChild( renderer.domElement );

  scene = new THREE.Scene();


  camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 1, 1000 );
  camera.position.set( 100, 0, 100 );
  
  const controls = new THREE.OrbitControls( camera );
  
}

const v = new THREE.Vector3();

function randomPointInSphere( radius ) {

  const x = THREE.Math.randFloat( -1, 1 );
  const y = THREE.Math.randFloat( -1, 1 );
  //const z = THREE.Math.randFloat( -1, 1 );
  const normalizationFactor = 1 / Math.sqrt( x * x + y * y );

  v.x = x * normalizationFactor * THREE.Math.randFloat( 0.5 * radius, 1.2 * radius );
  v.y = y * normalizationFactor *  THREE.Math.randFloat( 0.5 * radius, 1.2 * radius );
  v.z = 0; // z * normalizationFactor * radius;

  return v;
}

function initPoints() {
  
  const geometry = new THREE.BufferGeometry();
  
  var positions = [];
  
  for (var i = 0; i < 50000; i ++ ) {
    
    var vertex = randomPointInSphere( 50 );
    positions.push( vertex.x, vertex.y, 0 );
    
  }
  
  geometry.addAttribute( 'position', new THREE.Float32BufferAttribute( positions, 3 ) );

  material = new THREE.PointsMaterial( { color: 0xff00ff, size: 0.1 } );
  particles = new THREE.Points(geometry, material);
  scene.add( particles );


}

function animate() {
  
  requestAnimationFrame( animate );
  
  renderer.render( scene, camera );

}

function onWindowResize() {
  
  camera.aspect = window.innerWidth / window.innerHeight;
  camera.updateProjectionMatrix();

  renderer.setSize( window.innerWidth, window.innerHeight );
  
}

window.addEventListener( 'resize', onWindowResize );

init();
initPoints();

animate();
</script>
</html>
3 Likes

As a module version for current three.js


 <!DOCTYPE html>
<!-- https://discourse.threejs.org/t/create-circle-with-fuzzy-edge-made-of-individual-random-particles/30150 -->

<!-- see also 
	https://discourse.threejs.org/t/how-move-all-points-to-sphere/1836/2 
	and 
	https://codepen.io/looeee/pen/LQLQRd
-->

<head>
  <title> PointsToCircleRandom </title>
  <meta charset="utf-8" />
  <style>
	body {
	margin: 0;
	overflow: hidden;
	}
  </style>
</head>

<body> </body>
<script type="module">

// @author  looeee - changed by hofk

import * as THREE from "../jsm/three.module.132.js";
import { OrbitControls } from "../jsm/OrbitControls.132.js";

let camera, renderer, scene;

function init() {

  renderer = new THREE.WebGLRenderer();
  renderer.setSize( window.innerWidth, window.innerHeight );
  
  renderer.setPixelRatio( window.devicePixelRatio );

  document.body.appendChild( renderer.domElement );

  scene = new THREE.Scene();


  camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 0.1, 1000 );
  camera.position.set( 1, -1, 3 );
  
  const controls = new OrbitControls( camera, renderer.domElement );
  
}

const v = new THREE.Vector2();

function randomPointCircle( radius ) {

  const x = THREE.Math.randFloat( -1, 1 );
  const y = THREE.Math.randFloat( -1, 1 );
  const r = THREE.Math.randFloat( 0.5 * radius, 1.2 * radius );
  
  const normalizationFactor = 1 / Math.sqrt( x * x + y * y );
 
  v.x = x * normalizationFactor * r;
  v.y = y * normalizationFactor * r;

  return v;
}

function initPoints() {
  
  const geometry = new THREE.BufferGeometry();
  
  var positions = [];
  
  for (var i = 0; i < 50000; i ++ ) {
    
    var vertex = randomPointCircle( 1 );
    positions.push( vertex.x, vertex.y, 0 );
    
  }
  
  geometry.setAttribute( 'position', new THREE.Float32BufferAttribute( positions, 3 ) );

  const material = new THREE.PointsMaterial( { color: 0xff00ff, size: 0.001 } );
  const particles = new THREE.Points(geometry, material);
  scene.add( particles );

}

function animate() {
  
  requestAnimationFrame( animate ); 
  renderer.render( scene, camera );

}

function onWindowResize() {
  
  camera.aspect = window.innerWidth / window.innerHeight;
  camera.updateProjectionMatrix();
  renderer.setSize( window.innerWidth, window.innerHeight );
  
}

window.addEventListener( 'resize', onWindowResize );

init();
initPoints();

animate();
</script>
</html>
1 Like

One more option: Edit fiddle - JSFiddle - Code Playground

And the thing you need is:

let pts = [];
let POINT_COUNT = 10000;
let mainR = 5;
let outerLimit = 1;
let innerLimit = 5;
for(let i = 0; i < POINT_COUNT; i++){

  let inout = (Math.random() - 0.5) * 2;
  let lim = (inout >= 0 ? outerLimit : innerLimit);
  let rand = mainR + Math.pow(Math.random(), 3) * lim * inout;

	pts.push(
  	  new THREE.Vector3().setFromCylindricalCoords(
    	rand,
        Math.PI * 2 * Math.random(),
        0
    )
  )
}
let g = new THREE.BufferGeometry().setFromPoints(pts);
let m = new THREE.PointsMaterial({size: 0.05, color: "aqua"});
let p = new THREE.Points(g, m);
scene.add(p);
6 Likes

This function

function randomPointCircle( radius ) {
 
  const r = THREE.Math.randFloat( 0.5 * radius, 1.2 * radius );
  const phi =  THREE.Math.randFloat( 0, Math.PI * 2 );
  
  v.x = r * Math.cos( phi );
  v.y = r * Math.sin( phi );
  
  return v;
  
}

gives

3 Likes

Another strongly modified variant.


<!DOCTYPE html>
<!-- https://discourse.threejs.org/t/create-circle-with-fuzzy-edge-made-of-individual-random-particles/30150/5 -->

<!-- see also 
	https://discourse.threejs.org/t/how-move-all-points-to-sphere/1836/2 
	and 
	https://codepen.io/looeee/pen/LQLQRd
-->

<head>
  <title> PointsToCircleRandom </title>
  <meta charset="utf-8" />
  <style>
	body {
	margin: 0;
	overflow: hidden;
	}
  </style>
</head>

<body> </body>
<script type="module">

// @author  looeee - strongly changed by hofk

import * as THREE from "../jsm/three.module.132.js";
import { OrbitControls } from "../jsm/OrbitControls.132.js";

let camera, renderer, scene;

const v = new THREE.Vector2();
let r;

window.addEventListener( 'resize', onWindowResize );
init();
randomPoints();
animate();

function onWindowResize() {
  
  camera.aspect = window.innerWidth / window.innerHeight;
  camera.updateProjectionMatrix();
  renderer.setSize( window.innerWidth, window.innerHeight );
  
}

function init() {

  renderer = new THREE.WebGLRenderer();
  renderer.setSize( window.innerWidth, window.innerHeight );
  
  renderer.setPixelRatio( window.devicePixelRatio );

  document.body.appendChild( renderer.domElement );

  scene = new THREE.Scene();

  camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 0.1, 1000 );
  camera.position.set( 1, -1, 3 );
  
  const controls = new OrbitControls( camera, renderer.domElement );
  
}

function randomPoints() {
  
  const geometry = new THREE.BufferGeometry();
  
  var positions = [];
  
  for ( let i = 0; i < 50000; i ++ ) {

    const phi = Math.random( ) * Math.PI * 2;
	
 	r =  Math.pow( Math.random( ), 1 / 6 ) + 0.2;
	
    v.x = r * Math.cos( phi );
    v.y = r * Math.sin( phi );
	
    positions.push( v.x, v.y, 0 );
    
  }
  
  geometry.setAttribute( 'position', new THREE.Float32BufferAttribute( positions, 3 ) );

  const material = new THREE.PointsMaterial( { color: 0xffff34, size: 0.001 } );
  const particles = new THREE.Points(geometry, material);
  scene.add( particles );

}

function animate() {
  
  requestAnimationFrame( animate ); 
  renderer.render( scene, camera );

}

</script>
</html>

UPDATE: :mouse2: There you can experiment nicely. :slightly_smiling_face:

:woman_student: Of course, you can also derive the required functions theoretically. :man_student:


Try it out.
In the Collection of examples from discourse.threejs.org

2021 >>> PointsToCircleRandom


function randomPoints() {
  
  const geometry = new THREE.BufferGeometry();
  
  var positions = [];
  
  for ( let i = 0; i < 3000; i ++ ) {

    const phi = Math.random( ) * Math.PI * 2;	
	const rnd = Math.random( )
	
 	r = 0.2 + ( rnd < 0.6 ? Math.pow( Math.random( ), 1 / 6 ) :  Math.pow( ( 3 +  5 * Math.random( ) ) * Math.random( ), 1 / 6 ) );
	
    v.x = r * Math.cos( phi );
    v.y = r * Math.sin( phi );
	
    positions.push( v.x, v.y, 0 );
    
  }
3 Likes

If one superimposes several rings of points, it is even easier to create different structures.

For this purpose I have changed the code again.

See live PointsToCircleRandomMultiple


const distributionA = n => Math.pow( n, 1 / 4 );
const distributionB = n => 0.2 * Math.pow( n, 1 / 2 );
const distributionC = n => 0.6 * Math.pow( Math.random( ), 2 );

function randomPoints() {
  
  //      count, rMin, distribution
  pushpos( 1200, -0.1, distributionA );
  pushpos( 2000,  0.8, distributionB );
  pushpos(  300, 1.05, distributionC );
  
  const geometry = new THREE.BufferGeometry( );
  geometry.setAttribute( 'position', new THREE.Float32BufferAttribute( positions, 3 ) );
  const material = new THREE.PointsMaterial( { color: 0xffff34, size: 0.001 } );
  const particles = new THREE.Points(geometry, material);
  scene.add( particles );

}

function pushpos( count, rMin, distribution ) {

	for ( let i = 0; i < count; i ++ ) {
	
		const phi = Math.random( ) * Math.PI * 2;	
		const rnd = Math.random( )
		
		r = rMin + distribution( rnd );
		
		v.x = r * Math.cos( phi );
		v.y = r * Math.sin( phi );
		
		positions.push( v.x, v.y, 0 );
	 
  }
  
}

A different distribution


UPDATE

When transferring the example ParticleVhangeByDistance(Shader) to the collection, it occurred to me that this method would be appropriate here. Otherwise, the intensity of the point cloud is much too intense at greater distance.

2021-09-24 09.30.16

2021-09-24 09.30.51

and to get that fuzzy look you can also add a tiny bit of DOF

vec4 mvPosition = modelViewMatrix * vec4(pos, 1.0);
gl_Position = projectionMatrix * mvPosition;
vDistance = abs(uFocus - -mvPosition.z);
gl_PointSize = (step(1.0 - (1.0 / uFov), position.x)) * vDistance * uBlur;

in the video it’s not very subtle, but dialing it down should get something close to that first image. anyway, that was the demo: GPGPU Curl-noise + DOF - CodeSandbox

1 Like

Wow, thank you very much that is exactly what I was looking for. Especially since the individual particles are also moving.