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

相关推荐
大佐不会说日语~18 分钟前
JVM垃圾回收机制面试笔记
jvm·笔记·面试
三月的一天19 分钟前
在 React Three Fiber 中实现 3D 模型点击扩散波效果
前端·react.js·前端框架
爱敲代码的小冰19 分钟前
npm 切换 node 版本 和npm的源
前端·npm·node.js
DoraBigHead25 分钟前
🧠【彻底读懂 reduce】acc 是谁?我是谁?我们要干嘛?
前端·javascript·面试
future141236 分钟前
项目开发日记
前端·学习·c#·游戏开发
汪子熙1 小时前
CSS 中 td:last-child a 选择器详解
前端·javascript
北北~Simple1 小时前
第一次搭建数据库
服务器·前端·javascript·数据库
GanGuaGua1 小时前
Vue3常用指令
前端·javascript·vue.js
欧阳天风1 小时前
录音实时上传
前端·javascript
江号软件分享1 小时前
从DNS到防火墙:NetDisabler多策略断网方法详解
前端