Three.js 古怪的艺术画廊

文化   2024-10-26 10:04   河南  

使用 Three.js 的 WebGL 小实验。古怪的艺术画廊。

实现代码

HTML:

<div id="scene-container"     aria-label="A quirky 3d scene consisting of animated geometric shapes: spheres, boxes, cones, vases and sunny side up eggs."      role="img"></div>
CSS:
body {  margin: 0;  padding: 0;  overflow: hidden;  background: #b89287;}

#scene-container { position: absolute; width: 100vw; height: 100vh;}

JAVASCRIPT:

import * as THREE from "https://unpkg.com/three@0.123.0/build/three.module.js";import { OrbitControls } from "https://unpkg.com/three@0.123.0/examples/jsm/controls/OrbitControls";import { SVGLoader } from "https://unpkg.com/three@0.123.0/examples/jsm/loaders/SVGLoader.js";import * as TWEEN from "https://cdnjs.cloudflare.com/ajax/libs/tween.js/18.6.4/tween.esm.js";

'use strict';

let container, camera, controls, scene, renderer;

// GLOBAL MESHES

let ball, hoopBig, hoopSmall, hoopSmaller, hoopSmallest;let artwork;

// ASSETS

let lettersURL = 'https://s3-us-west-2.amazonaws.com/s.cdpn.io/911157/good-morning-new.svg';

// MOUSE

let mouseX = 0;let mouseY = 0;

function init() { container = document.querySelector('#scene-container'); scene = new THREE.Scene(); createCamera(); createControls(); createLights(); createRenderer(); createGeometries(); createMaterials(); createSVG(lettersURL); createMeshes(); initTweens(); document.addEventListener("mousemove", onDocumentMouseMove); window.addEventListener("resize", onWindowResize); renderer.setAnimationLoop(() => { render(); update(); animate(); }); }

function createCamera() {

camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000);

camera.position.set(-7, 5, 12);

}

function createControls() {

controls = new OrbitControls(camera, container);

}

function createLights() {

const ambientLight = new THREE.HemisphereLight(0xddeeff, 0x202020, 5);

const mainLight = new THREE.DirectionalLight( 0xffffff, 5 ); mainLight.position.set(10, 10, 10);

scene.add(ambientLight, mainLight); }

function createGeometries() { const box = new THREE.BoxBufferGeometry( 1, 1, 1 ); const ball = new THREE.SphereBufferGeometry( 0.5, 32, 32 ); const cylinderTall = new THREE.CylinderBufferGeometry( 0.5, 0.5, 3, 64 ); const cone = new THREE.ConeBufferGeometry( 1, 4, 64 ); const coneSmall = new THREE.ConeBufferGeometry( 0.5, 1.5, 64 ); const disc = new THREE.CylinderBufferGeometry( 0.65, 0.5, 0.25, 64 ); const hoopBig = new THREE.TorusBufferGeometry( 1.25, 0.15, 32, 100 ); const hoopSmall = new THREE.TorusBufferGeometry( 0.5, 0.025, 32, 100 ); const path = new CustomSinCurve( 0.5 ); const tube = new THREE.TubeGeometry( path, 256, 0.05, 64, false ); const lathe = createLathe(); return { box, ball, cylinderTall, cone, coneSmall, disc, hoopBig, hoopSmall, tube, lathe };

}

function createMaterials() { const black = new THREE.MeshStandardMaterial({ color: 0x000000, roughness: 0.8,metalness: 0.6,flatShading: true}); black.color.convertSRGBToLinear();

const seagreen = new THREE.MeshStandardMaterial({ color: 0x00B5CC, roughness: 0.1,metalness: 0.6,flatShading: true}); seagreen.color.convertSRGBToLinear(); const brick = new THREE.MeshStandardMaterial({ color: 0xc0392b, roughness: 0.1,metalness: 0.6,flatShading: true}); brick.color.convertSRGBToLinear(); const lemon = new THREE.MeshStandardMaterial({ color: 0xe87e04, roughness: 0.1,metalness: 0.6,flatShading: true}); lemon.color.convertSRGBToLinear(); const green = new THREE.MeshStandardMaterial({ color: 0xf7d63, roughness: 0.1,metalness: 0.6,flatShading: true}); green.color.convertSRGBToLinear(); const bordeaux = new THREE.MeshStandardMaterial({ color: 0x55091d, roughness: 0.9,metalness: 0.6,flatShading: true}); bordeaux.color.convertSRGBToLinear(); const textureLoader = new THREE.TextureLoader(); const texture = textureLoader.load( 'https://assets.codepen.io/911157/textureWine_512_optimized.jpg' ); texture.encoding = THREE.sRGBEncoding; texture.anisotropy = 16; const marbled = new THREE.MeshStandardMaterial({ map: texture });

return {

lemon, brick, marbled, bordeaux, seagreen, green, black

};

}

function CustomSinCurve( scale ) { THREE.Curve.call( this );this.scale = ( scale === undefined ) ? 1 : scale; }

CustomSinCurve.prototype = Object.create( THREE.Curve.prototype );CustomSinCurve.prototype.constructor = CustomSinCurve;

CustomSinCurve.prototype.getPoint = function ( t ) {

var tx = t * 2 - 1.5;var ty = 0.35 * Math.sin( 5.9 * Math.PI * t);var tz = 0;

return new THREE.Vector3( tx, ty, tz ).multiplyScalar( this.scale );

};

function createSVG( url ) { const loader = new SVGLoader(); loader.load( url, function ( data ) {

let paths = data.paths;let group = new THREE.Group(); group.scale.multiplyScalar( 0.011 ); group.position.x = -9; group.rotation.x = Math.PI; group.position.y = 5; group.position.z = -3; for ( let i = 0; i < paths.length; i ++ ) { let path = paths[ i ]; let material = new THREE.MeshBasicMaterial( {color: path.color,side: THREE.DoubleSide,depthWrite: false} );

let shapes = path.toShapes( true );

for ( let j = 0; j < shapes.length; j ++ ) {

let shape = shapes[ j ];let geometry = new THREE.ShapeBufferGeometry( shape );let mesh = new THREE.Mesh( geometry, material );group.add( mesh );

}

}

artwork.add( group );

}, function ( xhr ) {console.log( ( xhr.loaded / xhr.total * 100 ) + '% loaded' );}, function ( error ) {console.log( 'An error happened' );} );

} // createSVG

function createLathe() { const points = []; let pointsNum = 20; for ( let i = 0; i < pointsNum; i ++ ) { points.push( new THREE.Vector2( Math.sin( 0.7 * i ) * 3 + 4, ( i - 5 ) * 2 )); } const lathe = new THREE.LatheBufferGeometry( points, 32 ); return lathe; }

function createMeshes() {

artwork = new THREE.Group(); scene.add( artwork );

const geometries = createGeometries(); const materials = createMaterials(); // CYLINDER + TORUS SCULPTURE const cylinderTall = new THREE.Mesh( geometries.cylinderTall, materials.bordeaux ); cylinderTall.position.x = -2; cylinderTall.rotation.x = Math.PI; hoopBig = new THREE.Mesh( geometries.hoopBig, materials.black ); hoopBig.position.x = -2; hoopBig.position.y = 1.15; const disc = new THREE.Mesh( geometries.disc, materials.black ); disc.position.x = -2; disc.position.y = 1.15; const cylinderGroup = new THREE.Group(); cylinderGroup.add( cylinderTall, hoopBig, disc ); // SHELL + ROTATING HOOPS const shell = new THREE.Mesh( geometries.ball, materials.marbled ); shell.position.set( -5, -0.1, 0 ); shell.scale.set( 2.5, 2.0, 1 ); shell.rotation.y = Math.PI; const stand = new THREE.Mesh( geometries.box, materials.black ); stand.position.set( -5, -1, 0 ); hoopSmall = new THREE.Mesh( geometries.hoopSmall, materials.black ); hoopSmall.position.set( -5, 1.75, 0 );

hoopSmaller = hoopSmall.clone(); hoopSmaller.scale.set( 1.5, 1.5, 1 ); hoopSmallest = hoopSmall.clone(); hoopSmallest.scale.set( 0.75, 0.75, 0.75 ); const hoopGroup = new THREE.Group(); hoopGroup.add( hoopSmall, hoopSmaller, hoopSmallest ); const shellGroup = new THREE.Group(); shellGroup.add( shell, stand, hoopGroup ); // BALL + BOXES GROUP const ball2 = new THREE.Mesh( geometries.ball, materials.seagreen ); ball2.position.set( -3.5, -0.65, 1 ); ball2.scale.multiplyScalar( 0.85 ); const box = new THREE.Mesh( geometries.box, materials.black ); box.position.set( -3.5, -1.5, 1 ); const box2 = box.clone(); box2.position.y = -2.25; box2.scale.y = 0.5; box2.material = materials.green; const boxGroup = new THREE.Group(); boxGroup.add( ball2, box, box2 ); // BOXES GROUP 2 const box3 = new THREE.Mesh( geometries.box, materials.lemon ); box3.position.y = -2; const box4 = box3.clone(); box4.material = materials.bordeaux; box4.position.y = box3.position.y - 0.7; box4.scale.y = 0.35; const box5 = box4.clone(); box5.material = materials.black; box5.position.y = box3.position.y - 0.9; box5.scale.set( 1.25, 0.15, 1.25 ); const boxGroup2 = new THREE.Group(); boxGroup.add( box3, box4, box5 ); boxGroup.position.x = -0.5; boxGroup.position.z = 1; // EYE + EYELASHES GROUP const eye = new THREE.Mesh( geometries.ball, materials.brick ); eye.position.set( 0.5, 1.15, -1); const hoopExtra = new THREE.Mesh( geometries.hoopSmall, materials.black ); hoopExtra.position.set( 0.5, 1.15, -1 ); hoopExtra.scale.set( 2, 1.5, 1 ); const tube = new THREE.Mesh( geometries.tube, materials.green ); tube.position.set( 0.5, 2.9, -1.05 ); tube.rotation.z = 0.5 * Math.PI; tube.scale.x = 1.5; const tube2 = tube.clone(); tube2.position.set( -0.2, 2, -1.05 ); tube2.rotation.z = -0.25 * Math.PI; const tube3 = tube.clone(); tube3.position.set( 1.5, 2.65, -1.05 ); tube3.rotation.z = 0.35 * Math.PI; const eyeGroup = new THREE.Group(); eyeGroup.add( eye, hoopExtra, tube, tube2, tube3 ); // CONE BIG const cone = new THREE.Mesh( geometries.cone, materials.black ); cone.position.set( 2, 0, 0 ); // HOURGLASS + FALLING BALL const coneSmall = new THREE.Mesh( geometries.coneSmall, materials.seagreen ); coneSmall.position.y = -1; const coneSmallUp = coneSmall.clone(); coneSmallUp.position.y = 0.45; coneSmallUp.rotation.x = Math.PI; ball = new THREE.Mesh( geometries.ball, materials.green ); ball.position.y = 3; const hourglassGroup = new THREE.Group(); hourglassGroup.position.x = 4; hourglassGroup.add( coneSmall, coneSmallUp, ball ); // VASE const vase = new THREE.Mesh( geometries.lathe, materials.marbled ); vase.scale.set( 0.14, 0.08, 0.14 ); vase.position.set( 4.5, -1.5, 1.75 ); vase.rotation.y = 0.75 * Math.PI; vase.material.side = THREE.DoubleSide; // EGGS const egg = new THREE.Mesh( geometries.ball, materials.lemon ); egg.position.set( -5.25, 3.6, -3); egg.scale.set( 1, 1, 0.45 ); const egg2 = egg.clone(); egg2.position.set( -3, 3.6, -3); const egg3 = egg.clone(); egg3.position.set( 3.5, 3.8, -3); artwork.add( shellGroup, boxGroup, boxGroup2, cylinderGroup, eyeGroup, cone, hourglassGroup, vase, egg, egg2, egg3 ); }

function initTweens() {

let tween = new TWEEN.Tween(ball.position) .to({ y: 1.5 }, 3000) .easing(TWEEN.Easing.Bounce.Out) .yoyo(false) .start() ; let tween2 = new TWEEN.Tween(hoopBig.rotation) .to({ x: Math.PI / 2 }, 6000) .easing(TWEEN.Easing.Sinusoidal.InOut) .repeat(3) .yoyo(true) .start() ; let tween3 = new TWEEN.Tween(hoopSmaller.rotation) .to({ y: 2 * Math.PI }, 5000) .easing(TWEEN.Easing.Linear.None) .repeat(Infinity) .yoyo(false) .start() ; let tween4 = new TWEEN.Tween(hoopSmallest.rotation) .to({ y: -2 * Math.PI }, 3000) .easing(TWEEN.Easing.Linear.None) .repeat(Infinity) .yoyo(false) .start() ; let tween5 = new TWEEN.Tween(hoopSmall.rotation) .to({ y: -2 * Math.PI }, 6000) .easing(TWEEN.Easing.Linear.None) .repeat(Infinity) .yoyo(false) .start() ; }

function createRenderer() {

renderer = new THREE.WebGLRenderer( { antialias: true, alpha: true } ); renderer.setSize( window.innerWidth, window.innerHeight ); renderer.setPixelRatio( window.devicePixelRatio ); renderer.gammaFactor = 2.2; renderer.outputEncoding = THREE.GammaEncoding; renderer.physicallyCorrectLights = true; renderer.shadowMap.autoUpdate = false;

container.appendChild( renderer.domElement );

}

function animate( time ) { TWEEN.update(time); }

function update() { let factorX = .001; let factorY = .001; if ( artwork ) { artwork.rotation.y += 0.1 * ( factorX * mouseX - artwork.rotation.y ); artwork.rotation.x += 0.05 * ( factorY * mouseY - artwork.rotation.x ); } }

function render() { renderer.render( scene, camera ); }

function onWindowResize() { camera.aspect = window.innerWidth / window.innerHeight; camera.updateProjectionMatrix(); renderer.setSize(window.innerWidth, window.innerHeight); }

function onDocumentMouseMove( event ) { mouseX = (event.clientX - (window.innerWidth / 2));mouseY = (event.clientY - (window.innerWidth / 2)); }

init();



源码:

https://codepen.io/ScavengerFrontend/pen/xxKPLrL


体验:

https://codepen.io/ScavengerFrontend/full/xxKPLrL



感谢您的阅读      

在看点赞 好文不断  

初识Threejs
初识 Three.js 的奇妙世界,走进三维空间,与小编一起拥抱前端技术,涉及WebGL、WebGPU、Threejs、Shader、GIS、VR/AR、数字孪生、3D图形学等。
 最新文章