<div class="headerContainer">
<div id="container">
<button onClick="init()">New Mesh</button>
</div>
</div>
body {
margin: 0;
background-color: #fff;
font-family: "Oswald", sans-serif;
}
button {
position: fixed;
left: 10px;
top: 10px;
padding: 1rem;
}
.headerContainer {
height: 100vh;
min-height: 700px;
width: 100%;
overflow: hidden;
position: relative;
}
.invitationOverlay {
position: absolute;
padding: 24px;
//background-color: rgba(255, 255, 255, 0.5);
left: 65%;
bottom: 30%;
color: #fff;
}
h1 {
font-size: 85px;
line-height: 87px;
font-weight: 800;
margin: 0 0 20px 0;
text-shadow: 0 0 5px rgb(0 0 0 / 50%);
}
h2 {
font-size: 50px;
line-height: 52px;
font-weight: 400;
margin: 0 0 40px 0;
text-shadow: 0 0 5px rgb(0 0 0 / 50%);
}
p {
font-size: 2rem;
color: rgba(255, 255, 255, 0.7);
margin: 0;
}
.statsBarOuter {
width: 100%;
position: absolute;
bottom: 0;
left: 0;
display: flex;
background: rgba(12, 22, 45, 0.3);
}
.statsBarInner {
padding: 20px;
display: flex;
justify-content: space-evenly;
width: 100%;
}
.statItem {
text-shadow: 0 0 5px rgb(0 0 0 / 50%);
}
.light {
font-weight: 300;
color: #a675b3;
font-size: 1.5rem;
}
.button {
display: inline;
}
.noselect {
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
.button {
box-shadow: inset 0 1px 1px rgb(111 55 125 / 80%),
inset 0 -1px 0px rgb(63 59 113 / 20%), 0 9px 16px 0 rgb(0 0 0 / 30%),
0 4px 3px 0 rgb(0 0 0 / 30%), 0 0 0 1px #150a1e;
background-image: linear-gradient(#3b2751, #271739);
text-shadow: 0 0 21px rgb(223 206 228 / 50%), 0 -1px 0 #311d47;
animation: bounceInDown 900ms 200ms ease-in-out both;
width: 150px;
height: 40px;
text-decoration: none;
outline-width: 0px;
z-index: 990;
color: #a675b3;
text-align: center;
line-height: 1;
display: inline-flex;
align-items: center;
justify-content: center;
cursor: pointer;
border: none;
padding: 10px 40px;
font-size: 1rem;
}
.button.active,
.button:active {
box-shadow: 0 9px 16px 0 rgba(0, 0, 0, 0.1), 0 0 0 1px #170c22,
0 2px 1px 0 rgba(121, 65, 135, 0.5), inset 0 0 4px 3px rgba(15, 8, 22, 0.2);
background-image: linear-gradient(#1f132e, #311d47);
text-shadow: 0 0 21px rgba(223, 206, 228, 0.5),
0 0 10px rgba(223, 206, 228, 0.4), 0 0 2px #2a153c;
color: #e4e3ce;
}
.blue {
box-shadow: inset 0 1px 1px rgb(95 96 151 / 80%),
inset 0 -1px 0px rgb(63 59 113 / 20%), 0 9px 16px 0 rgb(0 0 0 / 30%),
0 4px 3px 0 rgb(0 0 0 / 30%), 0 0 0 1px #5f5f97;
background-image: linear-gradient(#272951, #171c39);
text-shadow: 0 0 21px rgb(223 206 228 / 50%), 0 -1px 0 #1d2047;
color: #9293b7;
}
.blue.active,
.blue:active {
box-shadow: 0 9px 16px 0 rgba(0, 0, 0, 0.1), 0 0 0 1px #0c0d22,
0 2px 1px 0 rgba(160, 160, 195, 0.5), inset 0 0 4px 3px rgba(15, 8, 22, 0.2);
background-image: linear-gradient(#13132e, #1e1d47);
text-shadow: 0 0 21px rgba(223, 206, 228, 0.5),
0 0 10px rgba(223, 206, 228, 0.4), 0 0 2px #16153c;
color: #e4e3ce;
}
let container, stats;
let camera, scene, renderer;
let windowHalfX = window.innerWidth / 2;
let windowHalfY = window.innerHeight / 2;
init();
animate();
var headerContainer = document.querySelector(".headerContainer");
function init() {
var headerCanvas = document.getElementById("headerCanvas");
if (headerCanvas != null) {
headerCanvas.remove();
}
// SETUP
// ======================
headerContainer = document.querySelector(".headerContainer");
camera = new THREE.PerspectiveCamera(
5,
headerContainer.offsetWidth / headerContainer.offsetHeight,
1,
10000
);
camera.position.z = 1800 - camera.aspect * 200;
scene = new THREE.Scene();
scene.background = new THREE.Color(0x000000);
// LIGHTS
// ======================
const light = new THREE.DirectionalLight(0xcccccc);
light.position.set(0, 0, 1);
scene.add(light);
// TEMPORARY CANVAS
// this is the canvas for mapping the gradient
// ======================
const tempCanvas = document.createElement("canvas");
var ctx = tempCanvas.getContext("2d");
const width = headerContainer.offsetWidth;
const height = headerContainer.offsetWidth;
const halfWidth = Math.floor(width * 0.5);
const halfHeight = Math.floor(height * 0.5);
const gradientRange = Math.sqrt(width ** 2 + height ** 2);
// Size the canvas to match viewport
tempCanvas.width = width;
tempCanvas.height = height;
// Create a gradient for the fill
var grd = ctx.createRadialGradient(
THREE.Math.randFloat(-halfWidth / 2, halfWidth / 2) + halfWidth,
height,
THREE.Math.randFloat(5, 100),
halfWidth,
height,
gradientRange
);
var firstColorR = Math.floor(Math.random() * 255);
var firstColorG = Math.floor(Math.random() * 255);
var firstColorB = Math.floor(Math.random() * 255);
var numOfGradientBands = Math.floor(THREE.Math.randFloat(2, 4));
grd.addColorStop(
0.25,
"rgb(" + firstColorR + "," + firstColorG + ", " + firstColorB
);
if (numOfGradientBands == 3) {
grd.addColorStop(
0.35,
"rgb(" +
Math.floor(Math.random() * 255) +
"," +
Math.floor(Math.random() * 255) +
", " +
Math.floor(Math.random() * 255)
);
}
grd.addColorStop(
0.45,
"rgb(" +
Math.floor(Math.random() * 255) +
"," +
Math.floor(Math.random() * 255) +
", " +
Math.floor(Math.random() * 255)
);
// Render gradient across whole fill covering canvas
ctx.fillStyle = grd;
ctx.fillRect(0, 0, width, height);
var pixels = [];
// Map the pixel data (RGB) to an array
for (var a = 1; a < 33; a++) {
for (var b = 1; b < 33; b++) {
var pixel = ctx.getImageData(
(tempCanvas.width / 32) * a,
(tempCanvas.height / 32) * b,
1,
1
).data;
pixels.push(pixel);
}
}
// * For debugging, render the gradient canvas to see the gradient
//document.body.appendChild(tempCanvas);
// GENERATE LOW-POLY MESH
// ======================
var pointsCount = 1000;
var points3d = [];
const visibleHeightAtZDepth = (depth, camera) => {
// compensate for cameras not positioned at z=0
const cameraOffset = camera.position.z;
if (depth < cameraOffset) depth -= cameraOffset;
else depth += cameraOffset;
// vertical fov in radians
const vFOV = (camera.fov * Math.PI) / 180;
// Math.abs to ensure the result is always positive
return 2 * Math.tan(vFOV / 2) * Math.abs(depth);
};
const visibleWidthAtZDepth = (depth, camera) => {
const height = visibleHeightAtZDepth(depth, camera);
return height * camera.aspect;
};
var visibleWidth = visibleWidthAtZDepth(camera.position.z, camera);
if (visibleWidth < 800) {
visibleWidth = 500;
}
var variance = THREE.Math.randFloat(1, 10);
// generate 1024 verticies (32 * 32)
for (let i = 1; i < 33; i++) {
for (let j = 1; j < 33; j++) {
// width/height of screen / 32 segents * index / 5 (used to scale the mesh. Larger values = smaller mesh)
let x = (visibleWidth / 32) * i + THREE.Math.randFloatSpread(variance);
let z = (visibleWidth / 32) * j + THREE.Math.randFloatSpread(variance);
let y = THREE.Math.randFloatSpread(4);
points3d.push(new THREE.Vector3(x, y, z));
}
}
var geometry1 = new THREE.BufferGeometry().setFromPoints(points3d);
// DELAUNAY / APPLY FACES TO MESH
// ======================
// triangulate x, z
var indexDelaunay = Delaunator.from(
points3d.map((v) => {
return [v.x, v.z];
})
);
// delaunay index => three.js index
var meshIndex = [];
for (let i = 0; i < indexDelaunay.triangles.length; i++) {
meshIndex.push(indexDelaunay.triangles[i]);
}
// add three.js index to the existing geometry
geometry1.setIndex(meshIndex);
geometry1.computeVertexNormals();
// get the geometry points using attributes.position.count
const count = geometry1.attributes.position.count;
// assign a color attribute to geometry points
geometry1.setAttribute(
"color",
new THREE.BufferAttribute(new Float32Array(count * 3), 3)
);
const color = new THREE.Color();
const positions1 = geometry1.attributes.position;
const colors1 = geometry1.attributes.color;
// Generate color
color1 =
"rgb(" +
Math.floor(Math.random() * 255) +
"," +
Math.floor(Math.random() * 255) +
"," +
Math.floor(Math.random() * 255);
for (let i = 0; i < count; i++) {
color.setRGB(pixels[i][0] / 255, pixels[i][1] / 255, pixels[i][2] / 255);
colors1.setXYZ(i, color.r, color.g, color.b);
}
var mesh = new THREE.Mesh(
geometry1,
new THREE.MeshPhongMaterial({
color: 0xffffff,
opacity: 1,
vertexColors: true,
flatShading: true,
shininess: 30
})
);
mesh.rotation.x = Math.PI / 2;
mesh.position.y = 200;
mesh.position.x -= visibleWidth / 2.5;
mesh.scale.set(0.8, 0.8, 0.8);
scene.add(mesh);
renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(headerContainer.offsetWidth, headerContainer.offsetHeight);
var canvas = renderer.domElement;
canvas.id = "headerCanvas";
headerContainer.appendChild(canvas);
// * DEBUG enable orbit controls
//var controls = new THREE.OrbitControls(camera, canvas);
window.addEventListener("resize", onWindowResize);
}
function onWindowResize() {
camera.aspect = headerContainer.offsetWidth / headerContainer.offsetHeight;
camera.updateProjectionMatrix();
renderer.setSize(headerContainer.offsetWidth, headerContainer.offsetHeight);
}
function onDocumentMouseMove(event) {
vector.unproject(camera);
var dir = vector.sub(camera.position).normalize();
var distance = -camera.position.z / dir.z;
var pos = camera.position.clone().add(dir.multiplyScalar(distance));
}
//
function animate() {
requestAnimationFrame(animate);
render();
}
function render() {
camera.lookAt(scene.position);
renderer.render(scene, camera);
}
This Pen doesn't use any external CSS resources.