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...

相关推荐
晴殇i28 分钟前
3 分钟掌握图片懒加载核心技术:面试攻略
前端·面试·trae
Running_C37 分钟前
一文读懂vite和webpack,秒拿offer
前端
咸鱼青菜好好味37 分钟前
node的项目实战相关
前端
hqsgdmn39 分钟前
自动导入插件unplugin-auto-import/unplugin-vue-components
前端
bo5210040 分钟前
vue3单元测试-初步了解
vue.js·单元测试
不知火_caleb1 小时前
前端应用更新提示的优雅实现:如何让用户及时刷新页面?
前端
前端小巷子1 小时前
跨标签页通信(四):SharedWorker
前端·面试·浏览器
风铃喵游1 小时前
平地起高楼: 环境搭建
前端·架构
昌平第一王昭君1 小时前
基于antd pro封装的一个可拖动的modalform
前端
JiaLin_Denny1 小时前
css 制作一个可以旋转的水泵效果
前端·css·动画·animation·transition