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() {
...