Three.js 虫洞特效

作者:初识Threejs

原文:mp.weixin.qq.com/s/yqCS5t_kJ...

" 使用 Three.js 的 WebGL 小实验。虫洞特效。 "

实现代码

HTML:

xml 复制代码
<canvas class="experience"></canvas><div class="scrollTarget"></div><div class="vignette-radial"></div>

CSS:

css 复制代码
body {  margin: 0;}
.experience {  position: fixed;  top: 0;  left: 0;  width: 100%;  height: 100vh;  z-index: 2;}
.scrollTarget {  position: absolute;  height: 1000vh;  width: 100px;  top: 0;  z-index: 0;}
.vignette-radial {  position: fixed;  z-index: 1;  top: 0;  left: 0;  height: 100vh;  width: 100%;  pointer-events: none;}
.vignette-radial:after {  pointer-events: none;  content: " ";  position: absolute;  top: 0;  left: 0;  bottom: 0;  right: 0;}

JAVASCRIPT:

ini 复制代码
//Wormhole by Kurt Grüng
const MathUtils = {  normalize: (value, min, max) => (value - min) / (max - min),  interpolate: (normValue, min, max) => min + (max - min) * normValue,  map: (value, min1, max1, min2, max2) => {    value = Math.min(Math.max(value, min1), max1);    return MathUtils.interpolate(      MathUtils.normalize(value, min1, max1),      min2,      max2    );  }};
let w = window.innerWidth;let h = window.innerHeight;
const renderer = new THREE.WebGLRenderer({  canvas: document.querySelector("canvas"),  antialias: true,  shadowMapEnabled: true,  shadowMapType: THREE.PCFSoftShadowMap});renderer.setSize(w, h);
const scene = new THREE.Scene();scene.background = new THREE.Color(0x000000);
const camera = new THREE.PerspectiveCamera(45, w / h, 0.001, 200);let cameraRotationProxyX = Math.PI;let cameraRotationProxyY = 0;camera.rotation.y = cameraRotationProxyX;camera.rotation.z = cameraRotationProxyY;
const cameraGroup = new THREE.Group();cameraGroup.position.z = 400;cameraGroup.add(camera);scene.add(cameraGroup);
const generatePathPoints = (count = 10, spacing = 25) => {  const points = [];  for (let i = 0; i < count; i++) {    const x = i * spacing;    const y = Math.sin(i * 0.5 + Math.random()) * 100 + 50;    const z = Math.cos(i * 0.3 + Math.random()) * 100 + 50;    points.push(new THREE.Vector3(x, z, y));  }  return points;};
const points = generatePathPoints(10);const path = new THREE.CatmullRomCurve3(points);path.closed = true;path.tension = 1;
const ringCount = 600;const ringRadius = 3;const ringSegments = 32;
const geometry = new THREE.TubeGeometry(  path,  ringCount,  ringRadius,  ringSegments,  true);const wireframe = new THREE.LineSegments(  new THREE.EdgesGeometry(geometry),  new THREE.LineBasicMaterial({ linewidth: 0.1, opacity: 0.1 }));scene.add(wireframe);
const ringMaterial = new THREE.LineBasicMaterial({ color: 0xffffff });
const ringMaterial1 = new THREE.LineBasicMaterial({  color: 0xffffff,  transparent: true,  opacity: 0.8,  depthWrite: false});
const frenetFrames = path.computeFrenetFrames(ringCount, true);
for (let i = 0; i <= ringCount; i++) {  const t = i / ringCount;  const pos = path.getPointAt(t);  const normal = frenetFrames.normals[i];  const binormal = frenetFrames.binormals[i];
  const ringPoints = [];  for (let j = 0; j <= ringSegments; j++) {    const theta = (j / ringSegments) * Math.PI * 2;    const x = Math.cos(theta) * ringRadius;    const y = Math.sin(theta) * ringRadius;
    const point = new THREE.Vector3().addVectors(      pos,      new THREE.Vector3()        .addScaledVector(normal, x)        .addScaledVector(binormal, y)    );
    ringPoints.push(point);  }
  const ringGeometry = new THREE.BufferGeometry().setFromPoints(ringPoints);  const ringMesh = new THREE.LineLoop(ringGeometry, ringMaterial);  scene.add(ringMesh);}
const light = new THREE.PointLight(0xffffff, 0.1, 4, 0);light.castShadow = true;scene.add(light);
const renderScene = new THREE.RenderPass(scene, camera);const bloomPass = new THREE.UnrealBloomPass(  new THREE.Vector2(w, h),  1.5,  0.4,  0.5);bloomPass.renderToScreen = true;
const composer = new THREE.EffectComposer(renderer);composer.setSize(w, h);composer.addPass(renderScene);composer.addPass(bloomPass);
let cameraTargetPercentage = 0;let currentCameraPercentage = 0;
function updateCameraPercentage(percentage) {  const p1 = path.getPointAt(percentage % 1);  const p2 = path.getPointAt((percentage + 0.01) % 1);
  cameraGroup.position.set(p1.x, p1.y, p1.z);  cameraGroup.lookAt(p2);  light.position.set(p2.x, p2.y, p2.z);}
const tubePerc = { percent: 0 };
function render(time) {  cameraTargetPercentage = (cameraTargetPercentage + 0.001) % 1;  updateCameraPercentage(cameraTargetPercentage);  composer.render();
  camera.rotation.y += (cameraRotationProxyX - camera.rotation.y) / 15;  camera.rotation.x += (cameraRotationProxyY - camera.rotation.x) / 15;
  requestAnimationFrame(render);}
function render() {  cameraTargetPercentage = (cameraTargetPercentage + 0.001) % 1;
  camera.rotation.y += (cameraRotationProxyX - camera.rotation.y) / 15;  camera.rotation.x += (cameraRotationProxyY - camera.rotation.x) / 15;  updateCameraPercentage(cameraTargetPercentage);  composer.render();  requestAnimationFrame(render);  console.log(cameraTargetPercentage);}
requestAnimationFrame(render);
window.addEventListener("resize", () => {  w = window.innerWidth;  h = window.innerHeight;  camera.aspect = w / h;  camera.updateProjectionMatrix();  renderer.setSize(w, h);  composer.setSize(w, h);});

源码:

codepen.io/kurtgrung/p...

体验:

codepen.io/kurtgrung/f...

相关推荐
ANYOLY30 分钟前
Redis 面试宝典
数据库·redis·面试
珍宝商店36 分钟前
前端老旧项目全面性能优化指南与面试攻略
前端·面试·性能优化
bitbitDown36 分钟前
四年前端分享给你的高效开发工具库
前端·javascript·vue.js
gnip2 小时前
实现AI对话光标跟随效果
前端·javascript
脑花儿2 小时前
ABAP SMW0下载Excel模板并填充&&剪切板方式粘贴
java·前端·数据库
ShineSpark3 小时前
C++面试11——指针与引用
c++·面试
lumi.3 小时前
Vue.js 从入门到实践1:环境搭建、数据绑定与条件渲染
前端·javascript·vue.js
二十雨辰3 小时前
vue核心原理实现
前端·javascript·vue.js
影子信息3 小时前
[Vue warn]: Error in mounted hook: “ReferenceError: Jessibuca is not defined“
前端·javascript·vue.js
BTU_YC4 小时前
FastAPI+Vue前后端分离架构指南
vue.js·架构·fastapi