Three.js 泡泡场景

文化   2024-10-20 09:00   河南  

使用 Three.js 的 WebGL 小实验。泡泡场景。

实现代码

HTML:

<div class="container">  <div class="sky"></div>  <div id="canvas"></div></div><script type="x-shader/x-vertex" id="vertexshader">

varying vec2 vUv;

void main() {

vUv = uv;

gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );

}</script>

<script type="x-shader/x-fragment" id="fragmentshader">

uniform sampler2D baseTexture; uniform sampler2D bloomTexture;

varying vec2 vUv;

void main() {

gl_FragColor = ( texture2D( baseTexture, vUv ) + vec4( 1.0 ) * texture2D( bloomTexture, vUv ) );

}</script>
CSS:
html,body {  margin: 0;  height: 100%;  background: #22124a;  overflow: hidden;  perspective: 10rem;}

.container { width: 100%; height: 100%; display: block; position: relative;}

#canvas { position: absolute; width: 100%; height: 100%; overflow: hidden;}

.sky { width: 100%; height: 100%; opacity: 0.5; background: url("https://stivs-assets.s3.us-east-2.amazonaws.com/mrp/background.png") repeat; background-size: cover; position: absolute; right: 0; top: 0; bottom: 0;}

JAVASCRIPT:

let scene,  camera,  controls,  fieldOfView,  aspectRatio,  nearPlane,  farPlane,  renderer,  container,  hdrCubeRenderTarget,  HEIGHT,  WIDTH,  hdrEquirect,  tinky,  particles,  raycaster;

const params = { color: 0x21024f, transmission: 0.9, envMapIntensity: 10, lightIntensity: 1, exposure: 0.5};

const spheres = [];

const meshes = {};

const generateTexture = () => { const canvas = document.createElement("canvas"); canvas.width = 2; canvas.height = 2;

const context = canvas.getContext("2d"); context.fillStyle = "white"; context.fillRect(0, 1, 2, 1);

return canvas;};

const createScene = () => { HEIGHT = window.innerHeight; WIDTH = window.innerWidth;

raycaster = new THREE.Raycaster();

scene = new THREE.Scene();

aspectRatio = WIDTH / HEIGHT; fieldOfView = 60; nearPlane = 1; farPlane = 10000; camera = new THREE.PerspectiveCamera( fieldOfView, aspectRatio, nearPlane, farPlane );

camera.position.x = 0; camera.position.z = 500; camera.position.y = -10;

renderer = new THREE.WebGLRenderer({ alpha: true, antialias: true }); renderer.setPixelRatio(window.devicePixelRatio); renderer.setSize(WIDTH, HEIGHT);

renderer.shadowMap.enabled = true; renderer.shadowMap.type = THREE.PCFSoftShadowMap; renderer.toneMapping = THREE.ACESFilmicToneMapping; renderer.toneMappingExposure = 2;

container = document.getElementById("canvas"); container.appendChild(renderer.domElement);

window.addEventListener("resize", handleWindowResize, false);

scene.add(tinky);

controls = new THREE.OrbitControls(camera, renderer.domElement); controls.maxDistance = 1000; controls.maxAzimuthAngle = 1; controls.minAzimuthAngle = -1;};

const positionElements = () => { meshes.bigStar.position.y = -1.7; meshes.bigStar.position.x = -2.2; meshes.bigStar.position.z = 0.8; meshes.bigStar.rotation.z = -0.5;

meshes.littleStar.position.y = -1.75; meshes.littleStar.position.x = 1.75; meshes.littleStar.position.z = 0.6; meshes.littleStar.rotation.z = 0.5;

meshes.planet.position.y = 1.3; meshes.planet.position.x = 2.6; meshes.planet.position.z = 1;

meshes.ClosedLeftEye.visible = false; meshes.ClosedRightEye.visible = false;};

const handleWindowResize = () => { HEIGHT = window.innerHeight; WIDTH = window.innerWidth; renderer.setSize(WIDTH, HEIGHT); camera.aspect = WIDTH / HEIGHT; camera.updateProjectionMatrix();};

const createLights = () => { const ambientLight = new THREE.AmbientLight(0xaa54f0, 1);

const directionalLight1 = new THREE.DirectionalLight(0xffffff, 1); directionalLight1.position.set(-2, 2, 5);

const directionalLight2 = new THREE.DirectionalLight(0xfff000, 1); directionalLight2.position.set(-2, 4, 4); directionalLight2.castShadow = true;

scene.add(ambientLight, directionalLight1, directionalLight2);};

const createBubbles = () => { const pmremGenerator = new THREE.PMREMGenerator(renderer); hdrCubeRenderTarget = pmremGenerator.fromEquirectangular(hdrEquirect); hdrEquirect.dispose(); pmremGenerator.dispose();

const bubbleTexture = new THREE.CanvasTexture(generateTexture()); bubbleTexture.repeat.set(1);

const bubbleMaterial = new THREE.MeshPhysicalMaterial({ color: params.color, metalness: 0, roughness: 0, alphaMap: bubbleTexture, alphaTest: 0.5, envMap: hdrCubeRenderTarget.texture, envMapIntensity: params.envMapIntensity, depthWrite: false, transmission: params.transmission, opacity: 1, transparent: true });

const bubbleMaterial1b = new THREE.MeshPhysicalMaterial().copy( bubbleMaterial ); bubbleMaterial1b.side = THREE.BackSide;

const bubbleGeometry1 = new THREE.SphereBufferGeometry(170, 64, 32); const bubbleGeometry2 = new THREE.SphereBufferGeometry(55, 64, 32); const bubbleGeometry3 = new THREE.SphereBufferGeometry(30, 64, 32); const bubbleGeometry4 = new THREE.SphereBufferGeometry(70, 64, 32);

let bubble1 = new THREE.Mesh(bubbleGeometry1, bubbleMaterial1b); bubble1.position.z = 15;

let bubble2 = new THREE.Mesh(bubbleGeometry2, bubbleMaterial1b); bubble2.position.y = -135; bubble2.position.x = -175; bubble2.position.z = 75;

let bubble3 = new THREE.Mesh(bubbleGeometry3, bubbleMaterial1b); bubble3.position.y = -136; bubble3.position.x = 137; bubble3.position.z = 50;

let bubble4 = new THREE.Mesh(bubbleGeometry4, bubbleMaterial1b); bubble4.position.y = 100; bubble4.position.x = 210; bubble4.position.z = 70;

scene.add(bubble1, bubble2, bubble3, bubble4);};

const createParticles = () => { const particlesGeometry = new THREE.BufferGeometry();

const color = new THREE.Color(); let components = [];

const count = 400; const positions = new Float32Array(count * 3); const colors = new Float32Array(count * 3);

for (let i = 0; i < count; i++) { if (i % 3 === 0) { color.setHSL(Math.random(), 1, 0.5); components = [color.r, color.g, color.b]; } positions[i] = (Math.random() - 0.5) * 1000; colors[i] = components[i % 3]; }

particlesGeometry.setAttribute( "position", new THREE.BufferAttribute(positions, 3) );

particlesGeometry.setAttribute( "color", new THREE.BufferAttribute(colors, 3, true) );

const textureLoader = new THREE.TextureLoader(); const particlesTexture = textureLoader.load( "https://mrp.vercel.app/magic_05.png" ); const particlesMaterial = new THREE.PointsMaterial({ size: 17, alphaMap: particlesTexture, transparent: true, depthWrite: false, blending: THREE.AdditiveBlending, vertexColors: true });

particles = new THREE.Points(particlesGeometry, particlesMaterial);

scene.add(particles);};

window.addEventListener("click", (event) => { raycaster.setFromCamera( new THREE.Vector2( (event.clientX / window.innerWidth) * 2 - 1, -(event.clientY / window.innerHeight) * 2 + 1 ), camera );

const intersects = raycaster.intersectObjects(spheres);

for (let i = 0; i < intersects.length; i++) { const sphere = intersects[i].object; scene.remove(sphere); spheres.splice(spheres.indexOf(sphere), 1); }});

const time = new THREE.Clock();

const loop = () => { const elapsedTime = time.getElapsedTime(); const elapsedTimeInMs = Math.round(elapsedTime * 1000);

controls.update();

particles.rotation.y = elapsedTime * 0.02;

for (const sphere of spheres) { const radius = 350; const speed = 0.02 + 0.01 * sphere.randomness; const heightAngle = elapsedTime * speed + sphere.randomness; const thetaAngle = elapsedTime * -speed + sphere.randomness * 0.5;

sphere.position.x = radius * Math.cos(thetaAngle) * Math.sin(heightAngle); sphere.position.y = radius * Math.sin(thetaAngle) * Math.sin(heightAngle); sphere.position.z = radius * Math.cos(heightAngle); }

if (meshes.planet) { meshes.planet.rotation.y += 0.002; meshes.planet.rotation.z += 0.002; }

if (elapsedTimeInMs % 3000 > 2750) { meshes.RightEye.visible = false; meshes.LeftEye.visible = false; meshes.ClosedLeftEye.visible = true; meshes.ClosedRightEye.visible = true; } else { meshes.ClosedLeftEye.visible = false; meshes.ClosedRightEye.visible = false; meshes.RightEye.visible = true; meshes.LeftEye.visible = true; }

renderer.render(scene, camera);

requestAnimationFrame(loop);};

const main = async () => { hdrEquirect = await new THREE.RGBELoader() .setDataType(THREE.UnsignedByteType) .load("https://stivs-assets.s3.us-east-2.amazonaws.com/mrp/env.hdr");

await new Promise((resolve) => { new THREE.GLTFLoader().load("https://stivs-assets.s3.us-east-2.amazonaws.com/mrp/model.gltf", (gltf) => { tinky = gltf.scene; tinky.castShadow = true; tinky.receiveShadow = true; tinky.scale.set(80, 80, 80);

tinky.children.forEach((el) => { el.receiveShadow = true; meshes[el.name] = el; });

resolve(); }); });

positionElements(); createScene(); createLights(); createBubbles(); createParticles();

const bubbleGeometry5 = new THREE.SphereBufferGeometry(10, 64, 32); const pmremGenerator = new THREE.PMREMGenerator(renderer); hdrCubeRenderTarget = pmremGenerator.fromEquirectangular(hdrEquirect); hdrEquirect.dispose(); pmremGenerator.dispose();

const bubbleTexture = new THREE.CanvasTexture(generateTexture()); bubbleTexture.repeat.set(1);

const bubbleMaterial = new THREE.MeshPhysicalMaterial({ color: params.color, metalness: 0, roughness: 0, alphaMap: bubbleTexture, alphaTest: 0.5, envMap: hdrCubeRenderTarget.texture, envMapIntensity: params.envMapIntensity, depthWrite: false, transmission: params.transmission, opacity: 1, transparent: true });

const bubbleMaterial1b = new THREE.MeshPhysicalMaterial().copy( bubbleMaterial ); bubbleMaterial1b.side = THREE.BackSide;

setInterval(() => { if (spheres.length > 20) return;

const mesh = new THREE.Mesh(bubbleGeometry5, bubbleMaterial1b);

mesh.position.x = Math.random() * 1350 - 725; mesh.position.y = Math.random() * 1350 - 725; mesh.position.z = Math.random() * 1350 - 725;

mesh.scale.x = mesh.scale.y = mesh.scale.z = Math.random() * 3 + 1;

mesh.randomness = Math.random() * 50;

spheres.push(mesh);

scene.add(mesh); }, 2000);

renderer.render(scene, camera); loop();};

main();


源码:

https://codepen.io/stivaliserna/pen/GRNxGrR


体验:

https://codepen.io/stivaliserna/full/GRNxGrR



感谢您的阅读      

在看点赞 好文不断  

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