BJ Patel is an expert user of Umbraco. Always keen to share hints and tips on getting the best out of Umbraco.
This webpage sets up a basic 3D scene using Three.js, with a canvas to display the 3D content and buttons to control different effects like animation, rotation, stop, shadows, lighting, and textures. The functionality is split across several JavaScript files to keep things organized.
Cube with Three.js
"JStrial1V2.js"
This code sets up a 3D scene using Three.js with a camera and a renderer to display the scene on a webpage. It animates a 3D object (a cube) along a path and changes its color when the mouse pointer hovers over it. The scene adjusts automatically if the window is resized,
var GUI = lil.GUI;
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
const renderer = new THREE.WebGLRenderer({ canvas: document.getElementById('canvas') });
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
document.getElementById('container').appendChild(renderer.domElement);
camera.position.z = 5;
let isAnimating = false;
let isRotating = true;
let rotationDirectionX = 1, rotationDirectionY = 1, rotationDirectionZ = 1;
const raycaster = new THREE.Raycaster();
const pointer = new THREE.Vector2();
function onPointerMove(event) {
pointer.x = (event.clientX / window.innerWidth) * 2 - 1;
pointer.y = - (event.clientY / window.innerHeight) * 2 + 1;
}
window.addEventListener('mousemove', onPointerMove);
function animate() {
if(isAnimating) {
const time = Date.now();
const t = (time / 2000 % 6 ) / 6;
const position = path.getPointAt(t);
cube.position.copy(position);
const tangent = path.getTangentAt(t).normalize();
cube.lookAt(position.clone().add(tangent));
}
raycaster.setFromCamera(pointer, camera);
const intersects = raycaster.intersectObjects([cube], true);
if (intersects.length > 0) {
intersects[0].object.material.color.set(0xff0000);
} else {
cube.material.color.set(0x00ff00);
}
renderer.render(scene, camera);
requestAnimationFrame(animate);
}
animate();
window.addEventListener('resize' , function () {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
});
"TextureGround.js"
This code makes a green cube that can have shadows and then adds a picture texture ('images.jpeg') to it. It also creates a ground with a grid pattern ('grid.png') below the cube that can show shadows.
const textureLoader = new THREE.TextureLoader();
const geometry = new THREE.BoxGeometry(1.3, 1.3, 1.3);
const material = new THREE.MeshStandardMaterial({ color: 0x00ff00 });
const cube = new THREE.Mesh(geometry, material);
cube.castShadow = true;
cube.receiveShadow = true;
scene.add(cube);
const texture = textureLoader.load('images.jpeg');
const texturedMaterial = new THREE.MeshStandardMaterial({ map: texture });
cube.material.needsUpdate = true;
const groundGeometry = new THREE.PlaneGeometry(30, 30);
const gridTexture = textureLoader.load('grid.png');
const groundMaterial = new THREE.MeshBasicMaterial({
map: gridTexture,
side: THREE.DoubleSide,
});
const ground = new THREE.Mesh(groundGeometry, groundMaterial);
ground.rotation.x = -Math.PI / 2;
ground.position.y = -1.5;
ground.receiveShadow = true;
scene.add(ground);
"path.js"
This code creates a curved path using specific points in 3D space and adds it to the scene as a red line. The cube is set to cast shadows, and an 'animateCubeonPath' function is defined to move the cube smoothly along this curved path. As the cube moves, it rotates to follow the direction of the path.
const curve = new THREE.CatmullRomCurve3( [
new THREE.Vector3( -10, 0, 10 ),
new THREE.Vector3( -5, 5, 5 ),
new THREE.Vector3( 0, 0, 0 ),
new THREE.Vector3( 5, -5, 5 ),
new THREE.Vector3( 10, 0, 10 ),
] );
const points = curve.getPoints( 100 );
cube.traverse(function(node){
if(node.isMesh)
node.castShadow = true;
});
const path = new THREE.CatmullRomCurve3(points);
const pathGeometry = new THREE.BufferGeometry().setFromPoints(path.getPoints(50));
const pathMaterial = new THREE.LineBasicMaterial({color: 0xff0000});
const pathObject = new THREE.Line(pathGeometry , pathMaterial);
scene.add(pathObject);
function animateCubeonPath(cube , path) {
const time = Date.now();
const t = (time / 2000 % 6 ) / 6;
const position = path.getPointAt(t);
cube.position.copy(position);
const tangent = path.getTangentAt(t).normalize();
cube.lookAt(position.clone().add(tangent));
}
"animate.js"
This code manages the cube's movement, rotation, and appearance in a 3D scene. It allows you to start and stop the cube's animation, change its texture, and toggle shadows. You can also add different types of lights to the scene, such as ambient, directional, and spotlights, to create various lighting effects.
function startAnimate() {
isAnimating = true;
animate();
}
function animate() {
requestAnimationFrame(animate);
if (isAnimating) {
const time = Date.now();
const t = (time / 2000 % 6) / 6;
const position = path.getPointAt(t);
cube.position.copy(position);
const tangent = path.getTangentAt(t).normalize();
cube.lookAt(position.clone().add(tangent));
}
raycaster.setFromCamera(pointer, camera);
const intersects = raycaster.intersectObjects([cube], true);
if (intersects.length > 0) {
intersects[0].object.material.color.set(0xff0000);
} else {
cube.material.color.set(0x00ff00);
}
renderer.render(scene, camera);
}
animate();
function stopAnimate() {
isRotating = false;
isAnimating = false;
cube.castShadow = true;
}
function toggleRotation() {
rotationDirectionX *= -1;
rotationDirectionY *= -1;
rotationDirectionZ *= -1;
}
function showTexture() {
const texture = textureLoader.load('images.jpeg', function () {
const texturedMaterial = new THREE.MeshStandardMaterial({ map: texture });
cube.material = texturedMaterial;
cube.material.needsUpdate = true;
renderer.render(scene, camera);
});
}
let originalMaterial = cube.material;
function hideTexture() {
cube.material = originalMaterial;
cube.material.needsUpdate = true;
renderer.render(scene, camera);
}
let shadowVisible = cube.castShadow && ground.receiveShadow;
function showShadow() {
cube.castShadow = !cube.castShadow;
}
let ambientLight, directionalLight, spotLight;
function showAmbientLight() {
if (!ambientLight) {
ambientLight = new THREE.AmbientLight(0xffffff, 0.1);
scene.add(ambientLight);
}
renderer.render(scene, camera);
}
function showDirectionalLight() {
if (!directionalLight) {
directionalLight = new THREE.DirectionalLight(0xFFFFFF, 1);
directionalLight.position.set(10, 20, 10);
directionalLight.target.position.set(0, 0, 0);
directionalLight.castShadow = true;
directionalLight.shadow.mapSize.width = 2048;
directionalLight.shadow.mapSize.height = 2048;
directionalLight.shadow.camera.left = -10;
directionalLight.shadow.camera.right = 10;
directionalLight.shadow.camera.top = 10;
directionalLight.shadow.camera.bottom = -6;
directionalLight.shadow.camera.near = 0.5;
directionalLight.shadow.camera.far = 500;
scene.add(directionalLight);
}
renderer.render(scene, camera);
}
function showSpotLight() {
if (!spotLight) {
spotLight = new THREE.SpotLight(0xffffff, 0.5);
spotLight.position.set( -1, 5, -1 );
spotLight.angle = Math.PI / 3;
spotLight.penumbra = 0.05;
spotLight.decay = 2;
spotLight.distance = 200;
spotLight.castShadow = false;
spotLight.shadow.bias = - 0.005;
scene.add( spotLight );
const lightHelper = new THREE.SpotLightHelper( spotLight );
scene.add( lightHelper );
}
renderer.render(scene, camera);
}
"environment.js"
function showEnvMap() {
const loader = new THREE.TextureLoader();
loader.load('https://png.pngtree.com/thumb_back/fw800/background/20230803/pngtree-sunset-over-a-river-in-a-beautiful-country-environment-image_12997139.jpg',
function (texture) {
const pmremGenerator = new THREE.PMREMGenerator(renderer);
pmremGenerator.compileEquirectangularShader();
const envMap = pmremGenerator.fromEquirectangular(texture).texture;
scene.background = envMap;
scene.environment = envMap;
ground.material = new THREE.MeshStandardMaterial({
envMap: envMap,
map: gridTexture,
side: THREE.DoubleSide,
roughness: 0,
envMapIntensity:0.1
});
texture.dispose();
pmremGenerator.dispose();
renderer.render(scene, camera);
});
}
"GUI.js"
This code creates a graphical user interface (GUI) for adjusting various properties of a 3D cube and other settings. You can rotate the cube, change its size, and move the camera using the GUI. It also allows you to select colors and adjust other settings like speed and size using simple dropdowns and sliders.
const gui = new GUI();
const cubeFolder = gui.addFolder( 'Cube' );
cubeFolder.add(cube.rotation, 'x', 0, Math.PI * 2);
cubeFolder.add(cube.rotation, 'y', 0, Math.PI * 2);
cubeFolder.add(cube.rotation, 'z', 0, Math.PI * 2);
cubeFolder.open();
const cameraFolder = gui.addFolder('Camera');
cameraFolder.add(camera.position, 'z', 0, 20);
cameraFolder.open();
const cubeScaleFolder = gui.addFolder('Scale');
cubeScaleFolder.add(cube.scale, 'x', 0, 10);
cubeScaleFolder.add(cube.scale, 'y', 0, 10);
cubeScaleFolder.add(cube.scale, 'z', 0, 10);
cubeScaleFolder.add(cube, 'visible', true);
cubeScaleFolder.open();
obj = {
color1: '#AA00FF',
color2: '#a0f',
color3: 'rgb(170, 0, 255)',
color4: 0xaa00ff,
color5: 0xaa00ff,
color6: 0xaa00ff
};
gui.addColor( obj, 'color1' );
gui.addColor( obj, 'color2' );
gui.addColor( obj, 'color3' );
gui.addColor( obj, 'color4' );
gui.addColor( obj, 'color5' );
gui.addColor( obj, 'color6' );
const myObject = {
myBoolean: true,
myFunction: function() { },
myString: 'lil-gui',
myNumber: 1
};
obj = { size: 'Medium', speed: 1 };
gui.add( obj, 'size', [ 'Small', 'Medium', 'Large' ] );
gui.add( obj, 'speed', { Slow: 0.1, Normal: 1, Fast: 5 } );
"trial3JSmain.css"
body {
margin: 0;
padding: 0;
overflow: hidden;
}
#container {
position: relative;
width: 100vw;
height: 100vh;
overflow: hidden;
}
#rotationSpeedRange {
position: fixed;
top: 10px;
right: 10px;
width: 200px;
}
canvas {
display: block;
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
#btnRotate {
position: absolute;
bottom: 50px;
left: 10.5%;
transform: translate(50% );
z-index: 1;
}
#btnAnimate {
position: absolute;
bottom: 50px;
left: 23%;
transform: translate(-215% );
z-index: 1;
}
#btnStop {
position: absolute;
bottom: 50px;
left: 17.5%;
transform: translate(55%);
z-index: 1;
}
#btnShadow {
position: absolute;
bottom: 50px;
left: 22%;
transform: translate(60%);
z-index: 1;
}
#btnAmbientLight {
position: absolute;
bottom: 50px;
left: 27.5%;
transform: translate(65%);
z-index: 1;
}
#btnDirectionalLight {
position: absolute;
bottom: 50px;
left: 37%;
transform: translate(70%);
z-index: 1;
}
#btnSpotLight {
position: absolute;
bottom: 50px;
left: 51.4%;
transform: translate(75%);
z-index: 1;
}
#btnTexture {
position: absolute;
bottom: 50px;
left: 58.5%;
transform: translate(80%);
z-index: 1;
}
#btnHideTexture {
position: absolute;
bottom: 50px;
left: 70%;
transform: translate(85%);
z-index: 1;
}
#btnEnvMap {
position: absolute;
bottom: 50px;
left: 82%;
transform: translate(90%);
z-index: 1;
}
Join Our Community
Your contributions help us continue creating valuable content. Consider supporting us by sharing our content.
Junagadh, Gujarat
Latest Blog Posts