JSFiddle - React, Tailwind, and code Playground

by pehrlich

HTML

<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/84/three.min.js"></script>

<script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script>

todo: 
fade on edges
√ rounded  peeling up corner
"crunch" effect
reflection :-)
physical-based rendering
todo: cull transparent fragments for rounded corners
self shadowing


<script type="text/vertexShader" id="vertexShader">
varying vec3 pos;
uniform float width;

varying vec3 vPos;
varying vec3 vNormal;

// todo: use proper GL math for everything.

void main() {

  pos = position;  
  float peelHeight = 30.;
  float maxDistance =  120.;
  float xDist = pos.x - width / 2.;
  float yDist = pos.y - width / 2.;
  float x = sqrt(xDist * xDist + yDist * yDist);
  pos.z = (maxDistance - x) / maxDistance;
	pos.z = max(0., pos.z);
	pos.z *= pos.z * peelHeight;
  
  
	vPos = (modelMatrix * vec4(pos, 1.0 )).xyz;
	vNormal = normalMatrix * normal;
      
  gl_Position = projectionMatrix * modelViewMatrix * vec4(pos,1.0);
}
</script>

<script type="text/fragmentShader" id="fragmentShader">
uniform vec3 diffuse;
varying vec3 pos;
uniform float width;
uniform float borderWidth;



varying vec3 vPos;
varying vec3 vNormal;
struct PointLight {
  vec3 position;
  vec3 color;
};
uniform PointLight pointLights[ NUM_POINT_LIGHTS ];
 


void main() {
	vec4 addedLights = vec4(0.2, 0.1, 0.1, 1.0);
  for(int l = 0; l < NUM_POINT_LIGHTS; l++) {
    vec3 adjustedLight = pointLights[l].position + cameraPosition;
    vec3 lightDirection = normalize(vPos - adjustedLight);
    addedLights.rgb += clamp(dot(-lightDirection, vNormal), 0.0, 1.0) * pointLights[l].color;
  }

   float borderStart = ( width / 2. - borderWidth );
   float radius = 100.;
   float yOffset = abs(pos.y) - borderStart;
   float xOffset = abs(pos.x) - borderStart;
    if ( abs(pos.x) >  borderStart && abs(pos.y) >  borderStart &&  
    	xOffset * xOffset + yOffset * yOffset > borderWidth * borderWidth 
      ){
    }else {
//      gl_FragColor =...

JavaScript

THREE.PointLight.prototype.addSphere = function () {
  this.sphere = new THREE.Mesh(new THREE.SphereGeometry(2, 16, 16), new THREE.MeshBasicMaterial({ color: this.color }))
  this.add(this.sphere);
}

function init() {
  camera = new THREE.PerspectiveCamera( 70, window.innerWidth / window.innerHeight, 1, 1000 );
  camera.position.set(50,100,400);
  camera.lookAt(new THREE.Vector3(0,100,0));

  scene = new THREE.Scene();
  var width = 200;
  var segments = 100;
  var geometry = new THREE.PlaneBufferGeometry( width, width, segments, segments );
  var material = new THREE.MeshPhongMaterial( { color: 0x333340, side: THREE.DoubleSide } );
  
  
  var light = new THREE.PointLight( 0xff0000, 1 );
  light.position.set( 0, 50, 50 );
  light.addSphere();
  scene.add( light );
  
  material2 = new THREE.ShaderMaterial({
	  side: THREE.DoubleSide,
    uniforms: THREE.UniformsUtils.merge([
      THREE.UniformsLib['lights'],
      {
        diffuse: { type: "c", value: new THREE.Color(0x442222) },
        width: { type: 'f', value: width },
        borderWidth: { type: 'f', value: 20 }
      }
    ]),
    attributes: {},
    vertexShader: document.getElementById( 'vertexShader' ).textContent,
    fragmentShader: document.getElementById( 'fragmentShader' ).textContent,
    lights: true
  });
  window.mat = material2
  
  mesh = new THREE.Mesh( geometry, material2 );
  mesh.position.set(0,100,0)
  scene.add( mesh );
  
  var wfh = new THREE.WireframeHelper( mesh, 0xcc0000 );
	wfh.material.linewidth = 3; // looks much better if your PC will support it
//  mesh.add( wfh );


  
  
  var axisHelper = new THREE.AxisHelper( 100 );
	scene.add( axisHelper );
  
  renderer = new THREE.WebGLRenderer({antialias: true });
  renderer.setPixelRatio( window.devicePixelRatio );
  renderer.setSize( window.innerWidth, window.innerHeight );
  document.body.appendChild( renderer.domElement );
  
  controls = new THREE.OrbitControls(camera, renderer.domElement);
}


function animate() {
 ...