<script src="https://cdn.jsdelivr.net/npm/three@0.115.0/build/three.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/three@0.115.0/examples/js/controls/OrbitControls.js"></script>
<script src="https://cdn.jsdelivr.net/npm/three@0.115.0/examples/js/libs/stats.min.js"></script>
html, body {
  height: 100%;
  margin: 0;
  overflow: hidden;
}
canvas {
  width: 100%;
  height: 100%;
  display; block;
}
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(60, 1, 0.1, 1000);
camera.position.set(0, 0, 100);
var renderer = new THREE.WebGLRenderer({
  antialias: true
});
//renderer.setClearColor(0x404040)
var canvas = renderer.domElement;
document.body.appendChild(canvas);

var controls = new THREE.OrbitControls(camera, canvas);

var sideVals = [3, 4, 5, 100];

var points = [];
var colors = [];
var c = new THREE.Color();
var sides = [];
var amount = 11000;
for (let i = 0; i < amount; i++) {
  points.push(
    new THREE.Vector3(
      THREE.Math.randFloatSpread(200),
      THREE.Math.randFloatSpread(200),
      THREE.Math.randFloatSpread(200)
    )
  );
  c.set(Math.random() * 0xffffff);
  colors.push(c.r, c.g, c.b);
  sides.push(sideVals[THREE.Math.randInt(0, 3)]);
}

var geom = new THREE.BufferGeometry().setFromPoints(points);
geom.addAttribute(
  "color",
  new THREE.BufferAttribute(new Float32Array(colors), 3)
);
geom.addAttribute(
  "sides",
  new THREE.BufferAttribute(new Float32Array(sides), 1)
);
var mat = new THREE.PointsMaterial({
  vertexColors: THREE.VertexColors,
  alphaTest: 0.5,
  size: 10
});
mat.extensions = { derivatives: true }; // adds to fragment shader: #extension GL_OES_standard_derivatives : enable
mat.onBeforeCompile = shader => {
  console.log(shader.vertexShader);
  shader.vertexShader =
    `
attribute float sides;
varying float vSides;
` + shader.vertexShader;
  shader.vertexShader = shader.vertexShader.replace(
    `gl_PointSize = size;`,
    `gl_PointSize = size;
vSides = sides;`
  );

  shader.fragmentShader =
    `
varying float vSides;
` + shader.fragmentShader;
  shader.fragmentShader = shader.fragmentShader.replace(
    `#include <clipping_planes_pars_fragment>`,
    `#include <clipping_planes_pars_fragment>

float getShape(float thickness, float outer, vec2 uv){
    uv *= 2.0;
    float a = atan(uv.x,-uv.y) + 3.1415926;
    float r = 3.1415926 * 2. / vSides;
    float d = cos( floor( .5 + a / r ) * r - a ) * length( uv );
    return smoothstep(thickness - fwidth(d), thickness + fwidth(d), d) - smoothstep(outer - fwidth(d), outer + fwidth(d), d);
}
`
  );

  shader.fragmentShader = shader.fragmentShader.replace(
    `vec4 diffuseColor = vec4( diffuse, opacity );`,
    `
vec3 base = vec3(0);
float color = getShape(0.2, 0.4, gl_PointCoord - 0.5);
vec4 diffuseColor = vec4( mix(base, diffuse, color), opacity );
float shape = getShape(0.0, 0.5, gl_PointCoord - 0.5);
diffuseColor.a = shape;

`
  );
};
var collective = new THREE.Points(geom, mat);
scene.add(collective);

var stats = new Stats();
document.body.appendChild(stats.dom);

render();

function render() {
  if (resize(renderer)) {
    camera.aspect = canvas.clientWidth / canvas.clientHeight;
    camera.updateProjectionMatrix();
  }
  renderer.render(scene, camera);
  stats.update();
  requestAnimationFrame(render);
}

function resize(renderer) {
  const canvas = renderer.domElement;
  const width = canvas.clientWidth;
  const height = canvas.clientHeight;
  const needResize = canvas.width !== width || canvas.height !== height;
  if (needResize) {
    renderer.setSize(width, height, false);
  }
  return needResize;
}
Run Pen

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.