<script src="https://threejs.org/build/three.min.js"></script>
<script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.7.5/dat.gui.min.js"></script>
html, body {
  height: 100%;
  margin: 0;
  overflow: hidden;
}
canvas {
  width: 100%;
  height: 100%;
  display; block;
}
var materialShader;

var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(60, 1, 1, 5000);
camera.position.setScalar(125);
var renderer = new THREE.WebGLRenderer({
  antialias: true
});
renderer.setClearColor(0x404040);
renderer.setPixelRatio( window.devicePixelRatio );
var canvas = renderer.domElement
document.body.appendChild(canvas);

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

var MAX_POINTS = 10000;

var colors = [
  new THREE.Color(1, 0, 1),
  new THREE.Color(1, 1, 0),
  new THREE.Color(0, 1, 1)
];

var pts = [];
var clrs = [];
var shpIdx = [];
var shpScl = [];

for(let i = 0; i < MAX_POINTS; i++){
  pts.push(new THREE.Vector3(
    THREE.Math.randFloatSpread(250),
    THREE.Math.randFloatSpread(250),
    THREE.Math.randFloatSpread(250))
  );
  let clrIdx = THREE.Math.randInt(0, 2);
  clrs.push(
    colors[clrIdx].r,
    colors[clrIdx].g,
    colors[clrIdx].b
  );
  shpIdx.push(
    clrIdx // correlation between shape and colour :)
  );
  shpScl.push(
    1 // all have the same scale
  )
}

var geometry = new THREE.BufferGeometry().setFromPoints(pts);
geometry.addAttribute("color", new THREE.BufferAttribute(new Float32Array(clrs), 3));
geometry.addAttribute("shapeIdx", new THREE.BufferAttribute(new Float32Array(shpIdx), 1));
geometry.addAttribute("shapeScale", new THREE.BufferAttribute(new Float32Array(shpScl), 1));

var material = new THREE.PointsMaterial({size: 5, vertexColors: THREE.VertexColors});

material.onBeforeCompile = shader => {
  shader.uniforms.inner = {value: 0.3};
  shader.uniforms.outer = {value: 0.45};
  shader.vertexShader = `
    attribute float shapeIdx;
    attribute float shapeScale;
    
    varying float vShapeIdx;
  ` + shader.vertexShader;
  shader.vertexShader = shader.vertexShader.replace(
     "gl_PointSize = size;", 
    `gl_PointSize = size * shapeScale;
    vShapeIdx = shapeIdx;
    `);
  shader.fragmentShader = `
    uniform float inner;
    uniform float outer;
    varying float vShapeIdx;
  ` + shader.fragmentShader;
  shader.fragmentShader = shader.fragmentShader.replace(
    "void main() {",
    `
    float circle(float inner, float outer, vec2 uv){
      float dist = length(uv);
      return smoothstep(inner - 0.01, inner, dist) - smoothstep(outer - 0.01, outer,  dist);
    }
    
    float square(float inner, float outer, vec2 uv){
      vec2 absUv = abs(uv);
      float maxUv = abs(max(absUv.x, absUv.y));
      return smoothstep(inner - 0.01, inner, maxUv) - smoothstep(outer - 0.01, outer, maxUv);
    }
    
    // honestly stolen from https://www.shadertoy.com/view/lsBfRc
    float triangle(float inner, float outer, vec2 uv){
        uv *= 2.0;
        float a = atan(uv.x,-uv.y)+PI;
        float r = 3.1415926 * 2.0 / 3.0;

        float d = cos( floor( .5 + a/r ) * r - a ) * length( uv );

        return smoothstep(inner - 0.01, inner, d) - smoothstep(outer - 0.01, outer,d);
    }
    
    void main() {`
  );
  shader.fragmentShader = shader.fragmentShader.replace(
    `gl_FragColor = vec4( outgoingLight, diffuseColor.a );`,
    `vec2 uv = gl_PointCoord;
    uv -= 0.5;

    float color = 0.0;
    float shape = 0.0;

    float shapeIdx = floor(vShapeIdx + 0.5);

    if (shapeIdx == 0.0){
      shape = circle(0.0, 0.5, uv);
      color = circle(inner, outer, uv);
    } else if (shapeIdx == 1.0) {
      shape = 1.0;
      color = square(inner, outer, uv);
    } else if (shapeIdx == 2.0){
      shape = triangle(0.0, 0.5, uv);
      color = triangle(inner, outer, uv);
    }

    if (shape < 0.5) discard;

    vec3 baseColor = vec3(0);
    vec3 col = mix(baseColor, vColor.rgb, color);
    gl_FragColor = vec4( col, diffuseColor.a );
    `
  );
  
  materialShader = shader;
  var gui = new dat.GUI();
  gui.add(materialShader.uniforms.inner, "value", 0.0, 0.4).name("inner");
}

var points = new THREE.Points(geometry, material);

scene.add(points);



render();

function render() {
  if (resize(renderer)) {
    camera.aspect = canvas.clientWidth / canvas.clientHeight;
    camera.updateProjectionMatrix();
  }
  renderer.render(scene, camera);
  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.