Three.js 勇敢牛牛

文化   2024-10-30 07:00   河南  

使用 Three.js 的 WebGL 小实验。勇敢牛牛特效。

实现代码

HTML:

<div class="world"></div><a class="toggle toggle-night"></a><a class="toggle-music"></a><audio class="world-music" src="https://res.cloudinary.com/liza/video/upload/v1492853426/sheep_qr3tus.m4a"></audio>
CSS:
body {  margin: 0;}.world {  position: absolute;  overflow: hidden;  width: 100%;  height: 100%;  background-color: #CCDCDA;}.world-night {  background-color: #1E0F4C;}.toggle {  position: absolute;  top: 1rem;  left: 1rem;  width: 3rem;  height: 3rem;  border-radius: 50%;  box-shadow: 0 4px 16px #C40062;  background: #F8007E url('https://res.cloudinary.com/elliepooh/image/upload/v1491841750/sun_nyr4z3.svg') center center / 60% no-repeat;  cursor: pointer;}.toggle-night {  box-shadow: 0 4px 16px #626EC9;  background: #9A96E8 url('https://res.cloudinary.com/elliepooh/image/upload/v1491841746/star_u865qq.svg') center 46% / 60% no-repeat;}.toggle-music {  position: absolute;  top: 5rem;  left: 1rem;  width: 3rem;  height: 3rem;  background: url('https://res.cloudinary.com/elliepooh/image/upload/v1491889929/music_s7aiet.svg') center center / 60% no-repeat;  cursor: pointer;}.music-off {  background: url('https://res.cloudinary.com/elliepooh/image/upload/v1491890054/music-off_nrnsni.svg') center center / 60% no-repeat;}

JAVASCRIPT:

'use strict';

let scene, camera, renderer, controls, mouseDown, world, night = false;

let sheep, cloud, sky;

let width, height;

function init() { width = window.innerWidth, height = window.innerHeight; scene = new THREE.Scene(); camera = new THREE.PerspectiveCamera(75, width / height, 0.1, 1000); camera.lookAt(scene.position); camera.position.set(0, 0.7, 8);

renderer = new THREE.WebGLRenderer({ alpha: true }); renderer.setPixelRatio(window.devicePixelRatio); renderer.setSize(width, height); renderer.shadowMap.enabled = true; renderer.shadowMap.type = THREE.PCFSoftShadowMap; // controls = new THREE.OrbitControls(camera, renderer.domElement); // controls.enableZoom = false; addLights(); drawSheep(); drawCloud(); drawSky(); world = document.querySelector('.world'); world.appendChild(renderer.domElement); document.addEventListener('mousedown', onMouseDown); document.addEventListener('mouseup', onMouseUp); document.addEventListener('touchstart', onTouchStart);document.addEventListener('touchend', onTouchEnd); window.addEventListener('resize', onResize);}

function addLights() { const light = new THREE.HemisphereLight(0xffffff, 0xffffff, 0.9); scene.add(light);

const directLight1 = new THREE.DirectionalLight(0xffd798, 0.8); directLight1.castShadow = true; directLight1.position.set(9.5, 8.2, 8.3); scene.add(directLight1);

const directLight2 = new THREE.DirectionalLight(0xc9ceff, 0.5); directLight2.castShadow = true; directLight2.position.set(-15.8, 5.2, 8); scene.add(directLight2);}

function drawSheep() { sheep = new Sheep(); scene.add(sheep.group);}

function drawCloud() { cloud = new Cloud(); scene.add(cloud.group);}

function drawSky() { sky = new Sky(); sky.showNightSky(night); scene.add(sky.group);}

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

function onMouseDown(event) { mouseDown = true;}function onTouchStart(event) { const targetClass = event.target.classList[0]; if (targetClass === 'toggle' || targetClass === 'toggle-music') return; event.preventDefault(); mouseDown = true;}

function onMouseUp() { mouseDown = false;}function onTouchEnd(event) { const targetClass = event.target.classList[0]; if (targetClass === 'toggle' || targetClass === 'toggle-music') return; event.preventDefault(); mouseDown = false;}

function rad(degrees) { return degrees * (Math.PI / 180);}

function animate() { requestAnimationFrame(animate);

render();}

function render() { sheep.jumpOnMouseDown(); if (sheep.group.position.y > 0.4) cloud.bend(); sky.moveSky(); renderer.render(scene, camera);}

class Sheep { constructor() { this.group = new THREE.Group(); this.group.position.y = 0.4; this.woolMaterial = new THREE.MeshStandardMaterial({ color: 0xffffff, roughness: 1, shading: THREE.FlatShading }); this.skinMaterial = new THREE.MeshStandardMaterial({ color: 0xffaf8b, roughness: 1, shading: THREE.FlatShading }); this.darkMaterial = new THREE.MeshStandardMaterial({ color: 0x4b4553, roughness: 1, shading: THREE.FlatShading }); this.vAngle = 0; this.drawBody(); this.drawHead(); this.drawLegs(); } drawBody() { const bodyGeometry = new THREE.IcosahedronGeometry(1.7, 0); const body = new THREE.Mesh(bodyGeometry, this.woolMaterial); body.castShadow = true; body.receiveShadow = true; this.group.add(body); } drawHead() { const head = new THREE.Group(); head.position.set(0, 0.65, 1.6); head.rotation.x = rad(-20); this.group.add(head); const foreheadGeometry = new THREE.BoxGeometry(0.7, 0.6, 0.7); const forehead = new THREE.Mesh(foreheadGeometry, this.skinMaterial); forehead.castShadow = true; forehead.receiveShadow = true; forehead.position.y = -0.15; head.add(forehead); const faceGeometry = new THREE.CylinderGeometry(0.5, 0.15, 0.4, 4, 1); const face = new THREE.Mesh(faceGeometry, this.skinMaterial); face.castShadow = true; face.receiveShadow = true; face.position.y = -0.65; face.rotation.y = rad(45); head.add(face); const woolGeometry = new THREE.BoxGeometry(0.84, 0.46, 0.9); const wool = new THREE.Mesh(woolGeometry, this.woolMaterial); wool.position.set(0, 0.12, 0.07); wool.rotation.x = rad(20); head.add(wool); const rightEyeGeometry = new THREE.CylinderGeometry(0.08, 0.1, 0.06, 6); const rightEye = new THREE.Mesh(rightEyeGeometry, this.darkMaterial); rightEye.castShadow = true; rightEye.receiveShadow = true; rightEye.position.set(0.35, -0.48, 0.33); rightEye.rotation.set(rad(130.8), 0, rad(-45)); head.add(rightEye); const leftEye = rightEye.clone(); leftEye.position.x = -rightEye.position.x; leftEye.rotation.z = -rightEye.rotation.z; head.add(leftEye); const rightEarGeometry = new THREE.BoxGeometry(0.12, 0.5, 0.3); rightEarGeometry.translate(0, -0.25, 0); this.rightEar = new THREE.Mesh(rightEarGeometry, this.skinMaterial); this.rightEar.castShadow = true; this.rightEar.receiveShadow = true; this.rightEar.position.set(0.35, -0.12, -0.07); this.rightEar.rotation.set(rad(20), 0, rad(50)); head.add(this.rightEar); this.leftEar = this.rightEar.clone(); this.leftEar.position.x = -this.rightEar.position.x; this.leftEar.rotation.z = -this.rightEar.rotation.z; head.add(this.leftEar); } drawLegs() { const legGeometry = new THREE.CylinderGeometry(0.3, 0.15, 1, 4); legGeometry.translate(0, -0.5, 0); this.frontRightLeg = new THREE.Mesh(legGeometry, this.darkMaterial); this.frontRightLeg.castShadow = true; this.frontRightLeg.receiveShadow = true; this.frontRightLeg.position.set(0.7, -0.8, 0.5); this.frontRightLeg.rotation.x = rad(-12); this.group.add(this.frontRightLeg); this.frontLeftLeg = this.frontRightLeg.clone(); this.frontLeftLeg.position.x = -this.frontRightLeg.position.x; this.frontLeftLeg.rotation.z = -this.frontRightLeg.rotation.z; this.group.add(this.frontLeftLeg); this.backRightLeg = this.frontRightLeg.clone(); this.backRightLeg.position.z = -this.frontRightLeg.position.z; this.backRightLeg.rotation.x = -this.frontRightLeg.rotation.x; this.group.add(this.backRightLeg); this.backLeftLeg = this.frontLeftLeg.clone(); this.backLeftLeg.position.z = -this.frontLeftLeg.position.z; this.backLeftLeg.rotation.x = -this.frontLeftLeg.rotation.x; this.group.add(this.backLeftLeg); } jump(speed) { this.vAngle += speed; this.group.position.y = Math.sin(this.vAngle) + 1.38;

const legRotation = Math.sin(this.vAngle) * Math.PI / 6 + 0.4;

this.frontRightLeg.rotation.z = legRotation; this.backRightLeg.rotation.z = legRotation; this.frontLeftLeg.rotation.z = -legRotation; this.backLeftLeg.rotation.z = -legRotation;

const earRotation = Math.sin(this.vAngle) * Math.PI / 3 + 1.5;

this.rightEar.rotation.z = earRotation; this.leftEar.rotation.z = -earRotation; } jumpOnMouseDown() { if (mouseDown) { this.jump(0.05); } else { if (this.group.position.y <= 0.4) return; this.jump(0.08); } }}

class Cloud { constructor() { this.group = new THREE.Group(); this.group.position.y = -2; this.group.scale.set(1.5, 1.5, 1.5); this.material = new THREE.MeshStandardMaterial({ color: 0xacb3fb, roughness: 1, shading: THREE.FlatShading }); this.vAngle = 0; this.drawParts(); this.group.traverse((part) => { part.castShadow = true; part.receiveShadow = true; }); } drawParts() { const partGeometry = new THREE.IcosahedronGeometry(1, 0); this.upperPart = new THREE.Mesh(partGeometry, this.material); this.group.add(this.upperPart); this.leftPart = this.upperPart.clone(); this.leftPart.position.set(-1.2, -0.3, 0); this.leftPart.scale.set(0.8, 0.8, 0.8); this.group.add(this.leftPart); this.rightPart = this.leftPart.clone(); this.rightPart.position.x = -this.leftPart.position.x; this.group.add(this.rightPart); this.frontPart = this.leftPart.clone(); this.frontPart.position.set(0, -0.4, 0.9); this.frontPart.scale.set(0.7, 0.7, 0.7); this.group.add(this.frontPart); this.backPart = this.frontPart.clone(); this.backPart.position.z = -this.frontPart.position.z; this.group.add(this.backPart); } bend() { this.vAngle += 0.08; this.upperPart.position.y = -Math.cos(this.vAngle) * 0.12; this.leftPart.position.y = -Math.cos(this.vAngle) * 0.1 - 0.3; this.rightPart.position.y = -Math.cos(this.vAngle) * 0.1 - 0.3; this.frontPart.position.y = -Math.cos(this.vAngle) * 0.08 - 0.3; this.backPart.position.y = -Math.cos(this.vAngle) * 0.08 - 0.3; }}

class Sky { constructor() { this.group = new THREE.Group(); this.daySky = new THREE.Group(); this.nightSky = new THREE.Group(); this.group.add(this.daySky); this.group.add(this.nightSky); this.colors = { day: [0xFFFFFF, 0xEFD2DA, 0xC1EDED, 0xCCC9DE], night: [0x5DC7B5, 0xF8007E, 0xFFC363, 0xCDAAFD, 0xDDD7FE], }; this.drawSky('day'); this.drawSky('night'); this.drawNightLights(); } drawSky(phase) { for (let i = 0; i < 30; i ++) { const geometry = new THREE.IcosahedronGeometry(0.4, 0); const material = new THREE.MeshStandardMaterial({ color: this.colors[phase][Math.floor(Math.random() * this.colors[phase].length)], roughness: 1, shading: THREE.FlatShading }); const mesh = new THREE.Mesh(geometry, material);

mesh.position.set((Math.random() - 0.5) * 30, (Math.random() - 0.5) * 30, (Math.random() - 0.5) * 30); if (phase === 'day') { this.daySky.add(mesh); } else { this.nightSky.add(mesh); } } } drawNightLights() { const geometry = new THREE.SphereGeometry(0.1, 5, 5); const material = new THREE.MeshStandardMaterial({ color: 0xFF51B6, roughness: 1, shading: THREE.FlatShading }); for (let i = 0; i < 3; i ++) { const light = new THREE.PointLight(0xF55889, 2, 30); const mesh = new THREE.Mesh(geometry, material); light.add(mesh); light.position.set((Math.random() - 2) * 6, (Math.random() - 2) * 6, (Math.random() - 2) * 6); light.updateMatrix(); light.matrixAutoUpdate = false; this.nightSky.add(light); } } showNightSky(condition) { if (condition) { this.daySky.position.set(100, 100, 100); this.nightSky.position.set(0, 0, 0); } else { this.daySky.position.set(0, 0, 0); this.nightSky.position.set(100, 100, 100); } } moveSky() { this.group.rotation.x += 0.001; this.group.rotation.y -= 0.004; }}

const toggleBtn = document.querySelector('.toggle');toggleBtn.addEventListener('click', toggleNight);

const worldMusic = document.querySelector('.world-music');const btnMusic = document.querySelector('.toggle-music');let playMusic = false;btnMusic.addEventListener('click', toggleMusic);worldMusic.volume = 0.3;worldMusic.loop = true;

function toggleNight() { night = !night; toggleBtn.classList.toggle('toggle-night'); world.classList.toggle('world-night'); sky.showNightSky(night);}

function toggleMusic() { playMusic = !playMusic; btnMusic.classList.toggle('music-off'); playMusic ? worldMusic.play() : worldMusic.pause();}

init();animate();




源码:

https://codepen.io/elliezen/pen/GWbBrx


体验:

https://codepen.io/elliezen/full/GWbBrx





感谢您的阅读      

在看点赞 好文不断  

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