Skip to content

Instantly share code, notes, and snippets.

@NOUIY
Created January 31, 2022 01:18
Show Gist options
  • Save NOUIY/7a9b1d0a3a5440454bb1ede489b328ab to your computer and use it in GitHub Desktop.
Save NOUIY/7a9b1d0a3a5440454bb1ede489b328ab to your computer and use it in GitHub Desktop.
3D Fractal Tree
<!-- #CodePenChallenge: Fractals -->
import * as THREE from "https://cdn.skypack.dev/three@0.136.0";
import OrbitControls from "https://cdn.skypack.dev/threejs-orbit-controls@1.0.3";
let getWindowWidth = () => window.innerWidth;
let getWindowHeight = () => window.innerHeight;
let getCamAspect = () => getWindowWidth() / getWindowHeight();
let cam_fov = 75;
let cam_near = 0.1;
let cam_far = 1000;
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(
cam_fov,
getCamAspect(),
cam_near,
cam_far
);
const renderer = new THREE.WebGLRenderer({ alpha: false, antialias: true });
let controls = new OrbitControls(camera, renderer.domElement);
////////////////////////////////////////////////////////////////////////////////
let lineSegments = 16;
let lineSegmentLength = 1;
let segmentRatio = 1 / 1.3;
let lineStartPoint = new THREE.Vector3(0, -3, 0);
let branchesPerNode = 3;
let maxBranchesCount = 1000;
let lineGeometrySet = [];
controls.autoRotate = true;
function createLinePositions(lineStartPoint, lineSegments, lineSegmentLength) {
let postionsCount = (lineSegments + 1) * 3;
let postions = new Float32Array(postionsCount);
let angle = Math.PI / 3;
let angleDirection = () => (Math.random() < 0.5 ? -1 : 1);
postions[0] = lineStartPoint.x;
postions[1] = lineStartPoint.y;
postions[2] = lineStartPoint.z;
for (let i = 3; i < postionsCount; i = i + 3) {
lineSegmentLength = lineSegmentLength * segmentRatio;
postions[i] =
lineSegmentLength * angleDirection() * Math.cos(angle) + postions[i - 3];
postions[i + 1] = lineSegmentLength * Math.sin(angle) + postions[i - 2];
postions[i + 2] =
lineSegmentLength * angleDirection() * Math.cos(angle) + postions[i - 1];
}
return new THREE.BufferAttribute(postions, 3);
}
function createLine(
lineStartPoint,
lineSegments,
lineSegmentLength,
branchesPerNode
) {
const lineBasicMaterial = new THREE.LineBasicMaterial({
color: `hsl(${Math.random() * 30}, 50%, 50%)`,
transparent: true,
opacity: 0.5
});
const lineGeometry = new THREE.BufferGeometry();
const linePositions = createLinePositions(
lineStartPoint,
lineSegments,
lineSegmentLength
);
lineGeometry.setAttribute("position", linePositions);
lineGeometry.setDrawRange(0, 0);
lineGeometrySet.push(lineGeometry);
const line = new THREE.Line(lineGeometry, lineBasicMaterial);
scene.add(line);
let branchSegmentLength = lineSegmentLength * segmentRatio;
let branchLineSegments = lineSegments / 2;
branchesPerNode = branchesPerNode / 2;
for (let i = 1; i < linePositions.count; i++) {
if (branchLineSegments < 1) break;
if (branchesPerNode < 1) break;
if (maxBranchesCount <= 0) break;
let startPoint = new THREE.Vector3(
linePositions.getX(i),
linePositions.getY(i),
linePositions.getZ(i)
);
// console.log(startPoint);
for (let j = 0; j < branchesPerNode; j++) {
createLine(
startPoint,
branchLineSegments,
branchSegmentLength,
branchesPerNode
);
maxBranchesCount--;
}
}
}
createLine(lineStartPoint, lineSegments, lineSegmentLength, branchesPerNode);
////////////////////////////////////////////////////////////////////////////////
camera.position.set(0, 0, 5);
camera.lookAt(0, 0, 0);
controls.update();
let linesCount = lineGeometrySet.length;
let currentLine = 0;
let postionsCount = lineGeometrySet[0].attributes.position.count;
let drawPoints = 0;
function animate() {
if (drawPoints++ >= postionsCount + 1) {
drawPoints = 0;
currentLine++;
}
if (currentLine < linesCount) {
lineGeometrySet[currentLine].setDrawRange(0, drawPoints);
}
renderer.render(scene, camera);
controls.update();
requestAnimationFrame(animate);
}
window.addEventListener("resize", () => {
renderer.setSize(getWindowWidth(), getWindowHeight());
camera.aspect = getCamAspect();
camera.updateProjectionMatrix();
});
renderer.setSize(getWindowWidth(), getWindowHeight());
var canvas = document.body.appendChild(renderer.domElement);
animate();
* {
box-sizing: border-box;
}
body,
html {
height: 100vh;
display: grid;
margin: 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment